Sfoglia il codice sorgente

使用xcrash捕获异常

weizhengliang 3 anni fa
parent
commit
5232bd94c2

+ 4 - 3
app/build.gradle

@@ -43,10 +43,11 @@ android {
         dataBinding {
             enabled = true
         }
-        //ndk {
+        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"
     }
 

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

@@ -5,8 +5,9 @@ import com.enation.javashop.android.jrouter.JRouter
 import com.wdkl.ncs.android.lib.base.BaseApplication
 import com.enation.javashop.net.engine.config.NetEngineConfig
 import com.enation.javashop.net.engine.plugin.exception.RestfulExceptionInterceptor
-import com.wdkl.app.ncs.conversion_box.helper.AnrFcExceptionUtil
+//import com.wdkl.app.ncs.conversion_box.helper.AnrFcExceptionUtil
 import com.wdkl.app.ncs.conversion_box.helper.NetHelper
+import com.wdkl.app.ncs.conversion_box.helper.XCrashUtils
 import com.wdkl.core.socket.SocketManager
 import com.wdkl.core.voip.VoipEvent
 import com.wdkl.net.HttpRequestPresenter
@@ -99,7 +100,9 @@ class Application : BaseApplication() {
         SocketManager.getInstance().init(applicationContext)
 
         //anr catcher
-        AnrFcExceptionUtil.getInstance(this).initFCException()
+        //AnrFcExceptionUtil.getInstance(this).initFCException()
 
+        //xCrash catcher
+        XCrashUtils().init(this)
     }
 }

+ 4 - 1
conversion_box/build.gradle

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

+ 3 - 2
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/activity/AppUpdateActivity.kt

@@ -34,7 +34,9 @@ class AppUpdateActivity :BaseActivity<AppUpdatePresenter, UpdateLayBinding>(), A
 
     override fun init() {
         if (!TextUtils.isEmpty(Constant.APP_PATH)) {
-            downLoadAPK(urlManager.base + "/" + Constant.APP_PATH)
+            val downloadPath = urlManager.base + Constant.APP_PATH
+            Log.e(TAG, "start download: $downloadPath")
+            downLoadAPK(downloadPath)
         } else {
             showMessage("下载路径异常")
             finish()
@@ -45,7 +47,6 @@ class AppUpdateActivity :BaseActivity<AppUpdatePresenter, UpdateLayBinding>(), A
      * 下载APK包
      */
     fun downLoadAPK(url: String) {
-        Log.d(TAG, "downLoadAPK  url==$url")
         activity_appupdate_dialog_progressview.setCurProgress(0)
         HttpHelper.download(url, object : HttpHelper.DownloadListener {
             override fun onDownloadSuccess() {

conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/AnrFcExceptionUtil.java → conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/AnrFcExceptionUtil.java.bak


+ 2 - 1
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/HttpHelper.java

@@ -20,6 +20,7 @@ import static com.wdkl.app.ncs.conversion_box.helper.AppUpdateHelper.FILE_APK_NA
 import static com.wdkl.app.ncs.conversion_box.helper.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

+ 239 - 0
conversion_box/src/main/java/com/wdkl/app/ncs/conversion_box/helper/XCrashUtils.java

@@ -0,0 +1,239 @@
+package com.wdkl.app.ncs.conversion_box.helper;
+
+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.app.ncs.conversion_box.BuildConfig;
+import com.wdkl.app.ncs.conversion_box.activity.MainActivity;
+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);
+            //uploadCrashLog(logPath, true);
+
+            //restartApp();
+            AppUpdateHelper.reboot(app);
+        }
+    };
+
+    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));
+
+        // Send all pending crash log files.
+        /*new Thread(new Runnable() {
+            @Override
+            public void run() {
+                for(File file : TombstoneManager.getAllTombstones()) {
+                    //Log.e(TAG, "crash log file: " + file.getAbsolutePath());
+                    //sendThenDeleteCrashLog(file.getAbsolutePath(), null);
+                    uploadCrashLog(file.getAbsolutePath(), false);
+                    try {
+                        Thread.sleep(2000);
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                    }
+                }
+            }
+        }).start();*/
+    }
+
+    private void uploadCrashLog(String path, boolean restart) {
+        final File logFile = new File(path);
+        Log.e(TAG,"upload file exist: " + logFile.exists());
+        if (logFile.exists()) {
+            final UrlManager urlManager = UrlManager.Companion.build();
+            String url = urlManager.getBase() + "ncs/upload/file";
+            HttpHelper.upload(url, logFile, new HttpHelper.UploadCallback() {
+                @Override
+                public void onFail() {
+                    Log.e(TAG,"错误日志文件上传失败!");
+                    if (restart) {
+                        AppUtils.relaunchApp(true);
+                    }
+                }
+
+                @Override
+                public void onSuccess(String data) {
+                    Log.e(TAG,"错误日志文件上传成功!" + data);
+                    sendThenDeleteCrashLog(urlManager.getBase() + data, null, restart);
+                    //删除日志文件
+                    logFile.delete();
+                }
+            });
+        }
+    }
+
+    private void sendThenDeleteCrashLog(String logPath, String emergency, boolean restart) {
+        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());
+                    if (restart) {
+                        AppUtils.relaunchApp(true);
+                    }
+                }
+
+                @Override
+                public void onResponse(Call call, Response response) throws IOException {
+                    Log.d(TAG,"错误日志名称上传成功");
+                    //String data = response.body().string();
+                    //Log.d(TAG,"错误日志数据 data "+data);
+                    if (restart) {
+                        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() {
+        CallSession session= SkyEngineKit.Instance().getCurrentSession();
+        if(session!=null&&session.getState()!= EnumType.CallState.Idle){
+            SkyEngineKit.Instance().endCall();
+        }
+
+        //重新启动app
+        Intent mStartActivity = new Intent(app.getApplicationContext(), MainActivity.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);
+    }
+
+}

+ 1 - 1
traditionlib/src/main/jni/Application.mk

@@ -1 +1 @@
-APP_ABI := armeabi armeabi-v7a x86
+APP_ABI := armeabi-v7a armeabi-v8a