Android UncaughtExceptionHandler 全局异常监控
2019獨角獸企業重金招聘Python工程師標準>>>
一、全局捕獲異常
為了解決這樣的問題,我們需要能夠及時的捕獲異常,但要捕獲的地方是在太多,因此,我們需要進行全局性的異常捕獲,那么如何捕獲全局異常呢?
答案是UncaughtExceptionHandler+Thread.setDefaultUncaughtExceptionHandler
?
1.UncaughtExceptionHandler是未捕獲異常的處理接口,該類率先捕獲異常
?UncaughtExceptionHandler:?線程未捕獲異常控制器是用來處理未捕獲異常的。??如果程序出現了未捕獲異常默認情況下則會出現強行關閉對話框?實現該接口并注冊為程序中的默認未捕獲異常處理??這樣當未捕獲異常發生時,就可以做些異常處理操作?例如:收集異常信息,發送錯誤報告?等。二、代碼實戰
對于這個接口,我們需要進行實現
public?class?AppCrashHandler?implements?UncaughtExceptionHandler?{private?Context?mContext;private?Thread.UncaughtExceptionHandler?mDefaultHandler;/**防止多線程中的異常導致讀寫不同步問題的lock**/private?Lock?lock?=?null;/**本地保存文件日志**/private?final?String?CRASH_REPORTER_EXTENSION?=?".crash";/**日志tag**/private?final?String?STACK_TRACE?=?"logStackTrance";/**保存文件名**/private?final?String?crash_pref_path?="app_crash_pref.xml";private?static?final?String?OOM?=?"java.lang.OutOfMemoryError";private?static?final?String?HPROF_FILE_PATH?=?Environment.getExternalStorageDirectory().getPath()?+?"/data_crash.hprof"private?AppCrashHandler(){lock?=?new?ReentrantLock(true);}/***?獲得單例對象*?@param?context*?@return?AppCrashHandler*/public?static?AppCrashHandler?shareInstance(Context?context){AppCrashHandler?crashhandler?=?AppCrashHandler.InstanceHolder.crashHandler;crashhandler.initCrashHandler(context);return?crashhandler;}/***?使用初始化方法初始化,防止提前初始化或者重復初始化*?@param?cxt*/private?void?initCrashHandler(Context?cxt){if(!hasInitilized()){mDefaultHandler?=?Thread.getDefaultUncaughtExceptionHandler();Thread.setDefaultUncaughtExceptionHandler(this);mContext?=?cxt;?}}public?interface?InstanceHolder{public?static?AppCrashHandler?crashHandler?=?new?AppCrashHandler();}public?static?boolean?isOOM(Throwable?throwable){??Log.d(TAG,?"getName:"?+?throwable.getClass().getName());??if(OOM.equals(throwable.getClass().getName())){??return?true;??}else{??Throwable?cause?=?throwable.getCause();??if(cause?!=?null){??return?isOOM(cause);??}??return?false;??}??}??@Overridepublic?void?uncaughtException(Thread?thread,?Throwable?ex)?{if(isOOM(throwable)){??try?{??Debug.dumpHprofData(HPROF_FILE_PATH);}?catch?(Exception?e)?{??Log.e(TAG,?"couldn’t?dump?hprof",?e);??}??}??if?(!handleExceptionMessage(ex)?&&?mDefaultHandler?!=?null)?{??//?如果用戶沒有處理則讓系統默認的異常處理器來處理??mDefaultHandler.uncaughtException(thread,?ex);??}?else?{??try?{??Thread.sleep(3000);??}?catch?(InterruptedException?e)?{??Log.e(STACK_TRACE,?"Error?:?",?e);??}??android.os.Process.killProcess(android.os.Process.myPid());??System.exit(10);??}??}/**?*?自定義錯誤處理,收集錯誤信息?發送錯誤報告等操作均在此完成.?開發者可以根據自己的情況來自定義異常處理邏輯?*?@param?ex?*?@return?true:如果處理了該異常信息;否則返回false?*/??private?boolean?handleExceptionMessage(Throwable?ex)?{??if?(ex?==?null)?{??return?false;??}??//?使用Toast來顯示異常信息??new?Thread()?{??@Override??public?void?run()?{??//?Toast?顯示需要出現在一個線程的消息隊列中??Looper.prepare();??Toast.makeText(mContext,?"程序出錯啦,即將退出",?Toast.LENGTH_LONG).show();??Looper.loop();??}??}.start();??String?fileName?=?mContext.getPackageName()+"-"+"appCrash-Exception"+?CRASH_REPORTER_EXTENSION;??String?crashFileName?=?saveExceptionToFile(ex,fileName);?SharedPreferences.Editor?editor?=?mContext.getSharedPreferences(crash_pref_path?,?Context.MODE_PRIVATE).edit();editor.putString(STACK_TRACE,?crashFileName);editor.commit();Log.d(STACK_TRACE,?"errorLogPath="+crashFileName);return?true;??}??/***?是否已初始化*?@return*/public?boolean?hasInitilized(){return?mContext!=null;}/**?*?保存錯誤信息到文件中?*?@param?ex?*?@return?*?@throws?IOException?*/??private?String?saveExceptionToFile(Throwable?ex,String?fileName)?{??File?saveFile?=?null;PrintWriter?printWriter?=?null;??try?{lock.tryLock();if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){File?sdCardDir?=?Environment.getExternalStorageDirectory();//獲取SDCard目錄saveFile?=?new?File(sdCardDir,?fileName);}else{saveFile?=new?File(mContext.getFilesDir(),fileName);}if(!saveFile.exists()){saveFile.createNewFile();}printWriter?=?new?PrintWriter(saveFile);String?result?=?formatException(ex);printWriter.write(result);printWriter.flush();Log.e("CrashException",?result);}catch(Exception?e){e.printStackTrace();}?finally{if(printWriter!=null){printWriter.close();}lock.unlock();}return?saveFile!=null?saveFile.getAbsolutePath():null;??}??/***?格式化異常信息*?@param?e*?@return*/@SuppressLint("SimpleDateFormat")private??String??formatException(Throwable?e){StringBuilder?sb?=?new?StringBuilder();StackTraceElement[]?stackTrace?=?e.getStackTrace();SimpleDateFormat?sdf?=?new?SimpleDateFormat("yyyy-MM-dd?HH:mm:ss");if?(stackTrace!=null){String??timeStramp?=??sdf.format(new?Date(System.currentTimeMillis()));String?format?=?String.format("DateTime:%s\nExceptionName:%s\n\n",timeStramp,e.getLocalizedMessage());sb.append(format);for?(int?i?=?0;?i?<?stackTrace.length;?i++)?{StackTraceElement?traceElement?=?stackTrace[i];String? fileName? =?traceElement.getFileName();int? lineNumber? =?traceElement.getLineNumber();String? methodName? =?traceElement.getMethodName();String? className?=?traceElement.getClassName();sb.append(String.format("%s\t%s[%d].%s?\n",className,fileName,lineNumber,methodName));}sb.append(String.format("\n%s",e.getMessage()));Writer?stringWriter?=?new?StringWriter();PrintWriter?pw?=?new?PrintWriter(stringWriter);e.printStackTrace(pw);pw.flush();pw.close();sb.append("\n~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n");sb.append(stringWriter.toString());}return?sb.toString();} }這里只保存了文件,一般來說,當app第二次啟動時我們需要將該文件上傳到網絡,時間不是很充裕,這里上傳暫時不貼代碼了,時間充裕的話會及時補充,請保持關注吧
?
2.初始化,監聽全局異常信息,這里需要繼承Application,并替換系統默認的Application
public?class?BaseApplication?extends?Application? {private??static?BaseApplication?instance?=?null;private?AppCrashHandler?appCrashHandler?=?null;@Overridepublic?void?onCreate()?{synchronized?(this){if(instance==null){instance?=?this;}appCrashHandler?=??AppCrashHandler.shareInstance(instance);}super.onCreate();}@Overridepublic?void?onConfigurationChanged(Configuration?newConfig)?{super.onConfigurationChanged(newConfig);}}修改清單文件
<applicationandroid:name="com.hali.luya.unitest.BaseApplication?"android:hardwareAccelerated="true"android:icon="@drawable/app_logo"android:logo="@drawable/app_logo"android:label="@string/app_name"android:configChanges="locale|keyboard|screenSize"android:theme="@style/Theme.AppBaseTheme"?><!---?..這里省略一大堆代碼..?----></Application>?
三、騰訊Bugly
騰訊有一個bugly產品可以實現crash收集和處理,當然也可以同時使用UncaughtExceptionHandler,因為騰訊bugly雖然也實現了UncaughtExceptionHandler該回調,但騰訊bugly在捕獲異常的同時也會調用你自己的UncaughtExceptionHandler。
?
目前騰訊的bugly不支持回調,但我申請到了騰訊的內測版支持回調。
public?class?BaseApplication?extends?Application? {private??static?Application?instance?=?null;private?AppCrashHandler?appCrashHandler?=?null;private?final?String?APP_CONTEXT_TAG?=?"appContext";@Overridepublic?void?onCreate()?{synchronized?(this){if(instance==null){instance?=?this;}appCrashHandler?=??AppCrashHandler.shareInstance(instance);UserStrategy?strategy?=?new?UserStrategy(instance);?//App的策略Beanstrategy.setAppChannel(getPackageName());?????//設置渠道strategy.setAppVersion(getVersion());??????//App的版本strategy.setAppReportDelay(1000);??//設置SDK處理延時,毫秒strategy.setDeviceID(GlobalUtil.getInstance().getDeviceID(instance));strategy.setCrashHandleCallback(new?AppCrashHandleCallback());CrashReport.initCrashReport(instance,?"900001335",?true,?strategy);?//自定義策略生效,必須在初始化SDK前調用CrashReport.?setUserId("BBDTEK");}//shutDownLog();super.onCreate();}@Overridepublic?void?onConfigurationChanged(Configuration?newConfig)?{super.onConfigurationChanged(newConfig);}/***?獲取版本號*?@return?當前應用的版本號*/public?String?getVersion()?{try?{PackageManager?manager?=?this.getPackageManager();PackageInfo?info?=?manager.getPackageInfo(this.getPackageName(),?0);String?version?=?info.versionName;return?this.getString(R.string.app_version)?+?version;}?catch?(Exception?e)?{e.printStackTrace();return?this.getString(R.string.app_version);}}private?class?AppCrashHandleCallback?extends?CrashHandleCallback?//bugly回調{@Overridepublic?synchronized?Map<String,?String>?onCrashHandleStart(int?crashType,?String?errorType,?String?errorMessage,?String?errorStack){String?crashTypeName?=?null;switch?(crashType){case?CrashHandleCallback.CRASHTYPE_JAVA_CATCH:crashTypeName?=?"JAVA_CATCH";break;case?CrashHandleCallback.CRASHTYPE_JAVA_CRASH:crashTypeName?=?"JAVA_CRASH";break;case?CrashHandleCallback.CRASHTYPE_NATIVE:crashTypeName?=?"JAVA_NATIVE";break;case?CrashHandleCallback.CRASHTYPE_U3D:crashTypeName?=?"JAVA_U3D";break;default:{crashTypeName?=?"unknown";}}Log.e(APP_CONTEXT_TAG,?"Crash?Happen?Type:"?+?crashType?+?"?TypeName:"?+?crashTypeName);Log.e(APP_CONTEXT_TAG,?"errorType:"?+?errorType);Log.e(APP_CONTEXT_TAG,?"errorMessage:"?+?errorMessage);Log.e(APP_CONTEXT_TAG,?"errorStack:"?+?errorStack);Map<String,?String>?userDatas?=?super.onCrashHandleStart(crashType,?errorType,?errorMessage,?errorStack);if?(userDatas?==?null){userDatas?=?new?HashMap<String,?String>();}userDatas.put("DEBUG",?"TRUE");return?userDatas;}}/***?關閉重要信息的日志*/private?void?shutDownLog(){LogUtils.allowE?=?false;LogUtils.allowI?=?false;LogUtils.allowV?=?false;LogUtils.allowW?=?false;LogUtils.allowWtf?=?false;LogUtils.allowD?=?false;} }?
?
?
try ?doing it
轉載于:https://my.oschina.net/ososchina/blog/351613
總結
以上是生活随笔為你收集整理的Android UncaughtExceptionHandler 全局异常监控的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 记录使用IDEA部署Tomcat时提示错
- 下一篇: hadoop--MapReduce_Wo