Use iQIYI xCrash to capture java crash, native crash and ANR
Save crash log to Android\data\com.calcitem.sanmill\files\xcrash File type is *.XCRASH We can use: ndk-stack -sym "src\ui\flutter\build\app\intermediates\cmake\debug\obj\arm64-v8a" -dump *.xcrash To backtrace. See: https://my.oschina.net/u/4484233/blog/4364482
This commit is contained in:
parent
8118e09e19
commit
ed30ba8b80
|
@ -60,3 +60,7 @@ android {
|
|||
flutter {
|
||||
source '../..'
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0'
|
||||
}
|
||||
|
|
|
@ -27,12 +27,25 @@ import io.flutter.embedding.engine.FlutterEngine;
|
|||
import io.flutter.plugin.common.MethodChannel;
|
||||
import io.flutter.plugins.GeneratedPluginRegistrant;
|
||||
|
||||
import org.json.JSONObject;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import java.io.File;
|
||||
import java.io.FileWriter;
|
||||
import xcrash.TombstoneManager;
|
||||
import xcrash.TombstoneParser;
|
||||
import xcrash.XCrash;
|
||||
import xcrash.ICrashCallback;
|
||||
|
||||
public class MainActivity extends FlutterActivity {
|
||||
|
||||
private static final String ENGINE_CHANNEL = "com.calcitem.sanmill/engine";
|
||||
|
||||
private MillEngine engine;
|
||||
|
||||
private final String TAG_XCRASH = "xCrash";
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
|
@ -87,4 +100,108 @@ public class MainActivity extends FlutterActivity {
|
|||
|
||||
GeneratedPluginRegistrant.registerWith(flutterEngine);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void attachBaseContext(Context base) {
|
||||
super.attachBaseContext(base);
|
||||
|
||||
// callback for java crash, native crash and ANR
|
||||
ICrashCallback callback = new ICrashCallback() {
|
||||
@Override
|
||||
public void onCrash(String logPath, String emergency) {
|
||||
Log.d(TAG_XCRASH, "xCrash log path: " + (logPath != null ? logPath : "(null)") + ", emergency: " + (emergency != null ? emergency : "(null)"));
|
||||
|
||||
if (emergency != null) {
|
||||
debug(logPath, emergency);
|
||||
|
||||
// Disk is exhausted, send crash report immediately.
|
||||
sendThenDeleteCrashLog(logPath, emergency);
|
||||
} else {
|
||||
// Add some expanded sections. Send crash report at the next time APP startup.
|
||||
|
||||
// OK
|
||||
TombstoneManager.appendSection(logPath, "expanded_key_1", "expanded_content");
|
||||
TombstoneManager.appendSection(logPath, "expanded_key_2", "expanded_content_row_1\nexpanded_content_row_2");
|
||||
|
||||
// Invalid. (Do NOT include multiple consecutive newline characters ("\n\n") in the content string.)
|
||||
// TombstoneManager.appendSection(logPath, "expanded_key_3", "expanded_content_row_1\n\nexpanded_content_row_2");
|
||||
|
||||
debug(logPath, null);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Log.d(TAG_XCRASH, "xCrash SDK init: start");
|
||||
|
||||
// Initialize xCrash.
|
||||
XCrash.init(this, new XCrash.InitParameters()
|
||||
.setAppVersion("0.0.0") // TODO
|
||||
.setJavaRethrow(true)
|
||||
.setJavaLogCountMax(10)
|
||||
.setJavaDumpAllThreadsWhiteList(new String[]{"^main$", "^Binder:.*", ".*Finalizer.*"})
|
||||
.setJavaDumpAllThreadsCountMax(10)
|
||||
.setJavaCallback(callback)
|
||||
.setNativeRethrow(true)
|
||||
.setNativeLogCountMax(10)
|
||||
.setNativeDumpAllThreadsWhiteList(new String[]{"^xcrash\\.sample$", "^Signal Catcher$", "^Jit thread pool$", ".*mill.*", ".*engine.*"}) // TODO
|
||||
.setNativeDumpAllThreadsCountMax(10)
|
||||
.setNativeCallback(callback)
|
||||
.setAnrRethrow(true)
|
||||
.setAnrLogCountMax(10)
|
||||
.setAnrCallback(callback)
|
||||
.setPlaceholderCountMax(3)
|
||||
.setPlaceholderSizeKb(512)
|
||||
.setLogDir(getExternalFilesDir("xcrash").toString())
|
||||
.setLogFileMaintainDelayMs(1000));
|
||||
|
||||
Log.d(TAG_XCRASH, "xCrash SDK init: end");
|
||||
|
||||
// Send all pending crash log files.
|
||||
new Thread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for(File file : TombstoneManager.getAllTombstones()) {
|
||||
sendThenDeleteCrashLog(file.getAbsolutePath(), null);
|
||||
}
|
||||
}
|
||||
}).start();
|
||||
}
|
||||
|
||||
private void sendThenDeleteCrashLog(String logPath, String emergency) {
|
||||
Log.d(TAG_XCRASH, "Skip sendThenDeleteCrashLog");
|
||||
// Parse
|
||||
//Map<String, String> map = TombstoneParser.parse(logPath, emergency);
|
||||
//String crashReport = new JSONObject(map).toString();
|
||||
|
||||
// Send the crash report to server-side.
|
||||
// ......
|
||||
|
||||
// If the server-side receives successfully, delete the log file.
|
||||
//
|
||||
// Note: When you use the placeholder file feature,
|
||||
// please always use this method to delete tombstone files.
|
||||
//
|
||||
//TombstoneManager.deleteTombstone(logPath);
|
||||
}
|
||||
|
||||
private void debug(String logPath, String emergency) {
|
||||
// Parse and save the crash info to a JSON file for debugging.
|
||||
FileWriter writer = null;
|
||||
try {
|
||||
File debug = new File(XCrash.getLogDir() + "/debug.json");
|
||||
debug.createNewFile();
|
||||
writer = new FileWriter(debug, false);
|
||||
writer.write(new JSONObject(TombstoneParser.parse(logPath, emergency)).toString());
|
||||
} catch (Exception e) {
|
||||
Log.d(TAG_XCRASH, "debug failed", e);
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
try {
|
||||
writer.close();
|
||||
Log.d(TAG_XCRASH, "xCrash log written");
|
||||
} catch (Exception ignored) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue