Browse Source

改用xcrash捕获异常

weizhengliang 3 years ago
parent
commit
f36b8c0afd

+ 11 - 4
app/build.gradle

@@ -43,7 +43,8 @@ android {
         }
         ndk {
             //选择要添加的对应cpu类型的.so库。
-            abiFilters 'armeabi', 'armeabi-v7a', 'armeabi-v8a' ,'x86', 'x86_64', 'mips', 'mips64'
+            //abiFilters 'armeabi', 'armeabi-v7a', 'armeabi-v8a' ,'x86', 'x86_64', 'mips', 'mips64'
+            abiFilters 'armeabi-v7a', 'armeabi-v8a'
         }
         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
     }
@@ -100,9 +101,15 @@ dependencies {
     /**
      * 内存泄漏监控
      */
-    debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
-    releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
-    testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
+    //debugCompile 'com.squareup.leakcanary:leakcanary-android:1.5.1'
+    //releaseCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
+    //testCompile 'com.squareup.leakcanary:leakcanary-android-no-op:1.5.1'
+
+    // debugImplementation because LeakCanary should only run in debug builds.
+    debugImplementation ('com.squareup.leakcanary:leakcanary-android:2.4') {
+        exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib'
+    }
+
     /**
      * Kotlin依赖
      */

+ 6 - 2
app/src/main/code/com/wdkl/app/ncs/application/Application.kt

@@ -10,8 +10,9 @@ import com.wdkl.core.socket.SocketManager
 import com.wdkl.core.voip.VoipEvent
 import com.wdkl.ncs.android.component.nursehome.common.Constants
 import com.wdkl.ncs.android.component.nursehome.led.LedManagerUtils
-import com.wdkl.ncs.android.component.nursehome.util.AnrFcExceptionUtil
+//import com.wdkl.ncs.android.component.nursehome.util.AnrFcExceptionUtil
 import com.wdkl.ncs.android.component.nursehome.util.NetHelper
+import com.wdkl.ncs.android.component.nursehome.util.XCrashUtils
 import com.wdkl.net.HttpRequestPresenter
 import com.wdkl.net.urlconn.UrlConnRequest
 import com.wdkl.skywebrtc.SkyEngineKit
@@ -96,7 +97,10 @@ class Application : BaseApplication() {
         LedManagerUtils.getInstance().ledInit(this)
 
         //anr catcher
-        AnrFcExceptionUtil.getInstance(this).initFCException()
+        //AnrFcExceptionUtil.getInstance(this).initFCException()
+
+        //xCrash catcher
+        XCrashUtils().init(this)
 
         Constants.supportCamera = isCameraSupport()
     }

+ 4 - 1
nursehome/build.gradle

@@ -106,7 +106,10 @@ dependencies {
      */
     compile 'com.android.support.constraint:constraint-layout:1.1.0-beta5'
 
-    compile 'com.github.anrwatchdog:anrwatchdog:1.3.0'
+    //compile 'com.github.anrwatchdog:anrwatchdog:1.3.0'
+
+    //使用xCrash捕获异常
+    implementation 'com.iqiyi.xcrash:xcrash-android-lib:3.0.0'
 
     //compile project(':starRTC')
     //compile project(':AmDemo_R')

nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/util/AnrFcExceptionUtil.java → nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/util/AnrFcExceptionUtil.java.bak


+ 2 - 1
nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/util/HttpHelper.java

@@ -20,6 +20,7 @@ import static com.wdkl.ncs.android.component.nursehome.util.AppUpdateHelper.FILE
 import static com.wdkl.ncs.android.component.nursehome.util.AppUpdateHelper.FILE_APK_PATH;
 
 public class HttpHelper {
+    private final static String TAG = "HttpHelper";
 
     /**
      * @param url   服务器地址
@@ -46,7 +47,7 @@ public class HttpHelper {
 
             @Override
             public void onResponse(Call call, Response response) throws IOException {
-                Log.d("wzlll", "voice msg response: " + response.toString());
+                Log.d(TAG, "upload file response: " + response.toString());
                if( response.code()==200 && response.body() != null) {
                    String data = response.body().string();
                    //voice msg response: upload/file/202104102037715.mp3

+ 222 - 0
nursehome/src/main/java/com/wdkl/ncs/android/component/nursehome/util/XCrashUtils.java

@@ -0,0 +1,222 @@
+package com.wdkl.ncs.android.component.nursehome.util;
+
+import android.app.AlarmManager;
+import android.app.Application;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.util.Log;
+
+import com.blankj.utilcode.util.AppUtils;
+import com.wdkl.ncs.android.component.nursehome.BuildConfig;
+import com.wdkl.ncs.android.component.nursehome.activity.RegisterActivity;
+import com.wdkl.ncs.android.component.nursehome.common.Constants;
+import com.wdkl.ncs.android.middleware.api.UrlManager;
+import com.wdkl.ncs.android.middleware.tcp.channel.DeviceChannel;
+import com.wdkl.skywebrtc.CallSession;
+import com.wdkl.skywebrtc.EnumType;
+import com.wdkl.skywebrtc.SkyEngineKit;
+
+import org.json.JSONObject;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import okhttp3.Call;
+import okhttp3.Callback;
+import okhttp3.FormBody;
+import okhttp3.OkHttpClient;
+import okhttp3.Request;
+import okhttp3.Response;
+import xcrash.ICrashCallback;
+import xcrash.TombstoneManager;
+import xcrash.TombstoneParser;
+import xcrash.XCrash;
+
+public class XCrashUtils {
+    private final static String TAG = "XCrashUtils";
+
+    private Application app;
+    private OkHttpClient okHttpClient;
+
+    // callback for java crash, native crash and ANR
+    private final ICrashCallback callback = new ICrashCallback() {
+        @Override
+        public void onCrash(String logPath, String emergency) {
+            Log.d(TAG, "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);
+            }
+
+            // Disk is exhausted, send crash report immediately.
+            //sendThenDeleteCrashLog(logPath, emergency);
+
+            //非debug版本上传crash日志
+            if (!BuildConfig.DEBUG) {
+                uploadCrashLog(logPath);
+            }
+
+            //restartApp();
+        }
+    };
+
+    public void init(Application application) {
+        Log.d(TAG, "xCrash SDK init: start");
+        app = application;
+
+        // Initialize xCrash.
+        XCrash.init(application, new XCrash.InitParameters()
+                .setAppVersion(BuildConfig.VERSION_NAME)
+                .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$", ".*(R|r)ender.*", ".*Chrome.*"})
+                .setNativeDumpAllThreadsCountMax(10)
+                .setNativeCallback(callback)
+                .setAnrRethrow(true)
+                .setAnrLogCountMax(10)
+                .setAnrCallback(callback)
+                .setPlaceholderCountMax(3)
+                .setPlaceholderSizeKb(512)
+                .setLogDir(application.getExternalFilesDir("xcrash").toString())
+                .setLogFileMaintainDelayMs(5000));
+    }
+
+    private void uploadCrashLog(String path) {
+        final File logFile = new File(path);
+        if (logFile.exists()) {
+            final UrlManager urlManager = UrlManager.Companion.build();
+            String url = urlManager.getBuyer() + "ncs/upload/file";
+            HttpHelper.upload(url, logFile, new HttpHelper.UploadCallback() {
+                @Override
+                public void onFail() {
+                    Log.e(TAG,"错误日志文件上传失败!");
+                    AppUtils.relaunchApp(true);
+                }
+
+                @Override
+                public void onSuccess(String data) {
+                    Log.e(TAG,"错误日志文件上传成功!" + data);
+                    sendThenDeleteCrashLog(urlManager.getBuyer() + data, null);
+                    //删除日志文件
+                    //logFile.delete();
+                }
+            });
+        }
+    }
+
+    private void sendThenDeleteCrashLog(final String logPath, String emergency) {
+        try {
+            // Parse
+            //Map<String, String> map = TombstoneParser.parse(logPath, emergency);
+            //String crashReport = new JSONObject(map).toString();
+
+            if(okHttpClient == null){
+                okHttpClient = new OkHttpClient();
+            }
+
+            FormBody.Builder formBody = new FormBody.Builder();
+            formBody.add("class_name", app.getPackageName());
+            formBody.add("method_name", "");
+            formBody.add("exception_name", "");
+            formBody.add("err_msg", "");
+            formBody.add("stack_trace", logPath);
+
+            UrlManager urlManager = UrlManager.Companion.build();
+            Request request  = new Request.Builder()
+                    .url(urlManager.getBuyer() + "device/error_log")
+                    .post(formBody.build())
+                    .build();
+
+            okHttpClient.newCall(request).enqueue(new Callback() {
+                @Override
+                public void onFailure(Call call, IOException e) {
+                    Log.e(TAG,"错误日志名称上传失败"+e.getMessage());
+                    AppUtils.relaunchApp(true);
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    Log.d(TAG,"错误日志名称上传成功");
+                    TombstoneManager.deleteTombstone(logPath);
+                    //String data = response.body().string();
+                    //Log.d(TAG,"错误日志数据 data "+data);
+                    AppUtils.relaunchApp(true);
+                }
+            });
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+
+        // 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, "debug failed", e);
+        } finally {
+            if (writer != null) {
+                try {
+                    writer.close();
+                } catch (Exception ignored) {
+                }
+            }
+        }
+    }
+
+    private void restartApp() {
+        Constants.Companion.setCALL_STATE(Constants.Companion.getCALL_STANDBY());
+        DeviceChannel.calling = false;
+        CallSession session= SkyEngineKit.Instance().getCurrentSession();
+        if(session!=null&&session.getState()!= EnumType.CallState.Idle){
+            SkyEngineKit.Instance().endCall();
+        }
+
+        //重新启动app
+        Intent mStartActivity = new Intent(app.getApplicationContext(), RegisterActivity.class);
+        mStartActivity.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
+        int mPendingIntentId = 123456;
+        PendingIntent mPendingIntent = PendingIntent.getActivity(app.getApplicationContext(), mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
+        AlarmManager mgr = (AlarmManager) app.getApplicationContext().getSystemService(Context.ALARM_SERVICE);
+        mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 2000, mPendingIntent);
+
+        android.os.Process.killProcess(android.os.Process.myPid());
+        System.exit(0);
+    }
+
+}