Codenameone Codename One-获取Android和iOS本机日志的本机代码

Codenameone Codename One-获取Android和iOS本机日志的本机代码,codenameone,Codenameone,读者更新:这里讨论的代码现在可以在这个代码名一库中找到(它也在CN1扩展管理器中…): 简短问题: 我编写了一个正常工作的本机Android代码,以便在执行我的Codename One应用程序期间获取设备的本机日志。我用了这个。我需要帮助来为iOS实现这个接口 长问题… 首先,当我的应用程序中出现异常行为时,我尝试获取本机日志(如Android Studio和XCode提供的日志)以寻求帮助。。。因为标准的Codename One日志在某些情况下是不够的(例如,我遇到了使用GoogleMaps

读者更新:这里讨论的代码现在可以在这个代码名一库中找到(它也在CN1扩展管理器中…):


简短问题:

我编写了一个正常工作的本机Android代码,以便在执行我的Codename One应用程序期间获取设备的本机日志。我用了这个。我需要帮助来为iOS实现这个接口

长问题…

首先,当我的应用程序中出现异常行为时,我尝试获取本机日志(如Android Studio和XCode提供的日志)以寻求帮助。。。因为标准的Codename One日志在某些情况下是不够的(例如,我遇到了使用GoogleMaps CN1Lib等本机组件的问题)

我是Android Studio的新手,我很难获得我的Codename One应用程序的日志,我也很难使用构建服务器提供的本地源代码。而且,我没有Mac电脑,所以我不能使用XCode

我的语言技能仅限于代号为One Java 8,我不会“说”Android原生Java,我觉得iOS原生Objective-C不可读

这就是为什么,当我需要在Codename One的Github存储库中提交一些问题时,为了帮助我自己提供准确的日志,我尝试编写本机代码以获取字符串形式的本机日志(我可以用几种简单的方法来管理,例如我可以将其显示在表单中并通过电子邮件发送给自己)

所以。。。我能够实现Android的代码:它工作得非常完美。我测试了几个安卓版本

CallNativeCode.java

package ...;

import com.codename1.system.NativeInterface;

/**
 * Native Code interface
 */
public interface CallNativeCode extends NativeInterface {

    public void clearAndRestartLog();
    public String readLog();
    public String getFilePath();

}
package ...;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class CallNativeCodeImpl {

    public String getFilePath() {
        return null;
    }

    public void clearAndRestartLog() {
        // https://developer.android.com/studio/command-line/logcat
        try {
            Runtime.getRuntime().exec("logcat -b all -c");
            Runtime.getRuntime().exec("logcat");
        } catch (IOException ex) {
            // logcat non available?
        }
    }

    // original code: https://stackoverflow.com/q/12692103
    public String readLog() {
        try {
            Process process = Runtime.getRuntime().exec("logcat -d");
            BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));

            StringBuilder log = new StringBuilder();
            String line = "";
            while ((line = bufferedReader.readLine()) != null) {
                log.append(line);
                log.append("\n");
            }
            return log.toString();
        } catch (IOException e) {
            return "Log is not available.";
        }
    }

    public boolean isSupported() {
        return true;
    }

}
CallNativeCodeImpl.java

package ...;

import com.codename1.system.NativeInterface;

/**
 * Native Code interface
 */
public interface CallNativeCode extends NativeInterface {

    public void clearAndRestartLog();
    public String readLog();
    public String getFilePath();

}
package ...;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;

public class CallNativeCodeImpl {

    public String getFilePath() {
        return null;
    }

    public void clearAndRestartLog() {
        // https://developer.android.com/studio/command-line/logcat
        try {
            Runtime.getRuntime().exec("logcat -b all -c");
            Runtime.getRuntime().exec("logcat");
        } catch (IOException ex) {
            // logcat non available?
        }
    }

    // original code: https://stackoverflow.com/q/12692103
    public String readLog() {
        try {
            Process process = Runtime.getRuntime().exec("logcat -d");
            BufferedReader bufferedReader = new BufferedReader(
                    new InputStreamReader(process.getInputStream()));

            StringBuilder log = new StringBuilder();
            String line = "";
            while ((line = bufferedReader.readLine()) != null) {
                log.append(line);
                log.append("\n");
            }
            return log.toString();
        } catch (IOException e) {
            return "Log is not available.";
        }
    }

    public boolean isSupported() {
        return true;
    }

}
它是这样工作的:在代码名的主类的init()中,我添加了一个应用程序:

// Test of Native code
        CallNativeCode callNativeCode = NativeLookup.create(CallNativeCode.class);
        if (callNativeCode != null && callNativeCode.isSupported()) {
            Log.p("Native code can be executed");
            callNativeCode.clearAndRestartLog();
            Log.p("Native LOG cleared and restarted");
        } else {
            Log.p("Native code cannot be executed");
        }
然后,当我想要获取应用程序的本机日志时,我可以执行:

String nativeLog = callNativeCode.readLog();
通过这种方式,我获得了相同的Android Studio Logcat输出,而不需要使用Android Studio,也不需要连接到计算机的设备

我尝试将此功能复制到iOS。。。但是我遇到了麻烦,因为我不知道Objective-C。我尝试将本机输出日志重定向到一个文件,然后读取该文件(修改我找到的一些代码,并尝试猜测它是如何工作的)。。。但我不知道该怎么做,我的代码也没有在iOS构建服务器上编译

下面的代码是我试图做的。怎么能修好呢?谢谢

myPackageName\u CallNativeCodeImpl.h

#import <Foundation/Foundation.h>

@interface cool_teammate_registration_CallNativeCodeImpl : NSObject {
}

-(NSString*)readLog;
-(NSString*)getFilePath;
-(void)clearAndRestartLog;
-(BOOL)isSupported;
@end

我的iOS代码中有一个打字错误(
NSString
而不是
NSStrings*

我在这篇文章中找到了有用的信息:。我使用这些信息对代码进行了一个小优化

最后,这是我的工作代码(在iOS 8、iOS 9、iOS 10、iOS 11上测试成功)。这样,我就可以获得控制台日志,而无需将设备连接到安装了XCode的Mac:)

myPackageName\u CallNativeCodeImpl.m

#import "myPackageName_CallNativeCodeImpl.h"

@implementation myPackageName_CallNativeCodeImpl

-(NSString*)getFilePath{
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
    NSString* documentsDirectory = [paths objectAtIndex:0];
    NSString* fileName =[NSString stringWithFormat:@"%@.log",[NSDate date]];
    NSString* logFilePath = [documentsDirectory stringByAppendingPathComponent:fileName];
    return logFilePath;
}

-(NSString)readLog{
    NSString* logFilePath = [self getFilePath];
    NSString* content = [NSString stringWithContentsOfFile:logFilePath encoding:NSUTF8StringEncoding error:nil];
    return content;
}

-(void)clearAndRestartLog{
    // https://stackoverflow.com/questions/5179108/iphone-how-to-read-application-logs-from-device
    NSString* logFilePath = [self getFilePath];
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding],"a+",stderr);
}

-(BOOL)isSupported{
    return YES;
}

@end
#import "myPackageName_CallNativeCodeImpl.h"

@implementation myPackageName_CallNativeCodeImpl

// Useful information:
// Log iOS Errors to File
// https://www.progressconcepts.com/blog/log-ios-errors-file/

-(NSString*)getFilePath{
    NSArray* paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,NSUserDomainMask, YES);
    NSString* documentsDirectory = [paths objectAtIndex:0];
    NSString* fileName = @"device.log";
    NSString* logFilePath = [documentsDirectory stringByAppendingPathComponent:fileName];
    return logFilePath;
}

-(NSString*)readLog{
    NSString* logFilePath = [self getFilePath];
    NSString* content = [NSString stringWithContentsOfFile:logFilePath encoding:NSUTF8StringEncoding error:nil];
    return content;
}

-(void)clearAndRestartLog{
    NSError *error = nil;
    NSString* logFilePath = [self getFilePath];
    [[NSFileManager defaultManager] removeItemAtPath:logFilePath error:&error];

    // wrap the code to check if the debugger was attached, and only write to the file when it wasn’t
    if (!isatty(STDERR_FILENO)) { 
        freopen([logFilePath cStringUsingEncoding:NSUTF8StringEncoding],"a+",stdout);
        freopen([logFilePath cStringUsingEncoding:NSUTF8StringEncoding],"a+",stderr);
    }
}

-(BOOL)isSupported{
    return YES;
}

@end

如果您有不理解的编译错误,请单击网站上的聊天按钮并询问。请在那里提供日志链接,以便我们的工程师可以帮助解决编译错误。关于这个前提,这是一个非常有趣的想法,但我不确定这是正确的目标C代码。注意FPEN代码确实是标准的UNIX系统代码谢谢您Shai的反馈,我很高兴听到您认为这个想法非常有趣。我刚刚用iOS代码的一个有效实现为这个问题添加了一个答案。通过这种方式,结合问题和答案,读者可以完全实现iOS和Android的本机界面,以获取本机日志。我不确定它是否可以在CN1Lib中转换…我看不出任何对CN1Lib来说应该是问题的东西。只需创建一个新的cn1lib并将本机目录和相关的src文件复制到正确的位置。如果不想公开完整的本机接口,那么添加一些抽象。将其提交到github,并向提交更改以将其添加到扩展管理器。如果途中有任何困难,我们可以陪你度过!读者更新:这里讨论的代码现在在这个代码名一库中可用:它也在扩展管理器中。。。