クラッシュ・ログの保存と再起動を実現するための例外プロセッサUncaughtExceptionHandler


前言
プログラムを作成するとき、異常な方法が投げ出されると、try...catchの方法を採用します.
try {
        bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            return null;
        }

しかし、もし私たちが捕まえられなかったら、プログラムはFCして、あるアプリケーションが停止したダイアログボックスをポップアップします.そしてユーザーがショックを受けた後にアプリケーションを再開するのは2つの点から言えばよくありません.これは私たちが今回実現する機能です.
  • APPがユーザーが使用している場合、私たちは得られた異常
  • を処理できません.
  • アプリケーションは、クラッシュ後に応答しない処理
  • を適用する.
    問題外の小技を話す
    ログを手動で取得する方法(ROOT権限が必要)/data/system/dropboxには、すべてのアプリケーションの例外ログファイルが保存されており、手動で抽出できる場合があります
    生成されたログファイル:
    クラッシュ後のヒント:
    再起動すると、前回の例外ログが表示されます.
    例外プロセッサの作成を開始
    使用するパラメータ
    /** * Created by OCWVAR * Package: com.ocwvar.surfacetest.ExceptionHandler * Date: 2016/5/25 9:20 * Project: SurfaceTest *          * *      Activity        Bundle *        OCExceptionHandler.handleIncomingBundle()        * *   : * IsRecover .    .                      true * hasLogs .    .             * Throwable .Serializable     .           * * *   : * SLEEPTIME_RESTART_ACTIVITY           Activity    .   . 1000ms = 1s * RESTART_ACTIVITY      Activity  * LOG_NAME_HEAD         * LOG_SAVE_FOLDER        * SAVE_LOGS        */
    public class OCExceptionHandler{
    
        private final static long SLEEPTIME_RESTART_ACTIVITY = 2000;
        private final static Class RESTART_ACTIVITY = MainActivity.class;
        private final static String LOG_NAME_HEAD = "OCLog";
        private final static String LOG_SAVE_FOLDER = "/log/";
        private final static boolean SAVE_LOGS = true;
    
        public final static String THROWABLE_OBJECT = "Throwable";
        public final static String IS_RECOVERY = "IsRecover";
        public final static String HAS_LOGS = "hasLogs";
    
        private boolean logsCreated = false;
    
        ...
    }
    

    1.Thread.UncaughtExceptionHandlerインタフェースはApplicationクラスを継承
    (Applicationの継承はs e t e D e f a u l t U n c aughtExceptionHandlerを設定し、ApplicationContextを使用するためだけであり、他の実装方法があれば継承しなくてもよい)
    
                   
    
    public class OCExceptionHandler extends Application implements Thread.UncaughtExceptionHandler {
    
    @Override
        public void onCreate() {
            super.onCreate();
            //                 
            Thread.setDefaultUncaughtExceptionHandler(this);
        }
    
    @Override
        public void uncaughtException(Thread thread, Throwable ex){
            ...
        }

    2.プログラムがクラッシュした後、対応するActivityを起動する
    /** *          * @param activityClass     Activity */
        private void restartActivity(Class activityClass , Throwable throwable){
    
            //        Intent ,       
            Intent intent = new Intent(getApplicationContext(),activityClass);
            intent.putExtra("IsRecover",true);
            intent.putExtra("hasLogs",logsCreated);
            intent.putExtra("Throwable",throwable);
    
            PendingIntent pendingIntent = PendingIntent.getActivity(
                    getApplicationContext(),
                    0,
                    intent,
                    PendingIntent.FLAG_ONE_SHOT
            );
    
            //        ,              
            AlarmManager mgr = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
            //    PendingIntent        +SLEEPTIME_RESTART_ACTIVITY     
            mgr.set(AlarmManager.RTC, System.currentTimeMillis() + SLEEPTIME_RESTART_ACTIVITY , pendingIntent);
        }

    3.プログラムがクラッシュしたときにログファイルを記録する
    /** *        * @param throwable        * @return      */
        private boolean createLogs(Throwable throwable){
            if (throwable == null || !SAVE_LOGS){
                return false;
            }
    
            if (Build.VERSION.SDK_INT >= 23 && !checkPermission()){
                Log.e("    --    ", "     , Android 6.0+   .            " );
                return false;
            }
    
            if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
                //              
    
                //      
                String savePath = Environment.getExternalStorageDirectory().getPath()+LOG_SAVE_FOLDER;
                File file = new File(savePath);
                file.mkdirs();
    
                if (file.canWrite()){
                    //        
    
                    FileWriter fileWriter;
                    PrintWriter printWriter;
    
                    //           ,     
                    String exceptionTime = DateFormat.format("yyyy-MM-dd hh:mm:ss", new Date()).toString();
    
                    //        
                    file = new File(savePath + LOG_NAME_HEAD + " " + exceptionTime + " .log");
                    try {
                        if (file.createNewFile()){
                        //         ,      
                            fileWriter = new FileWriter(file,true);
                            printWriter = new PrintWriter(fileWriter);
                            printWriter.println("Date:"+exceptionTime+"
    "
    ); printWriter.println("Exception Class Name: "); printWriter.println(throwable.getStackTrace()[0].getClassName()); printWriter.println(""); printWriter.println("Exception Class Position: "); printWriter.println("Line number: "+throwable.getStackTrace()[0].getLineNumber()); printWriter.println(""); printWriter.println("Exception Cause: "); printWriter.println(throwable.getMessage()); printWriter.println(""); printWriter.println("-----------------------------------
    Exception Message:
    "
    ); for (int i = 0; i < throwable.getStackTrace().length; i++) { printWriter.println(throwable.getStackTrace()[i]); } // printWriter.flush(); fileWriter.flush(); printWriter.close(); fileWriter.close(); Log.w(" -- ", " " ); return true; }else { Log.e(" -- ", " , " ); return false; } } catch (IOException e) { Log.e(" -- ", " , " ); return false; } }else { // , Log.e(" -- ", " , " ); file = null; return false; } }else { Log.e(" -- ", " , " ); return false; } } /** * Android 6.0+ * @return */ @TargetApi(23) private boolean checkPermission(){ return checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED; }

    4.作業を終了し、作成した方法を使用します.
    メソッドpublic void uncaughtException(Thread thread,Throwable ex)でメソッドを呼び出す
    @Override
        public void uncaughtException(Thread thread, Throwable ex) {
            //         ,    Intent         
            logsCreated = createLogs(ex);
            restartActivity(RESTART_ACTIVITY , ex);
            //      ,             .    ANR
            System.exit(2);
        }

    プロファイルでこのクラスを使用します(独自のアプリケーションクラスを使用する場合は必要ありません):
    <application
            android:icon="@mipmap/ic_launcher"
            android:label="TEST TEST TEST"
            android:theme="@style/AppTheme"
    
            //       Application 
            android:name=".ExceptionHandler.OCExceptionHandler">
    
            ...
    
            </application>

    に注意
    プログラムがクラッシュしたときにToastやダイアログボックスを表示しようとする人もいるかもしれませんが、直接実行してもHandlerを使ってもだめです.Stack OverflowのQAを見て、個人的な観点があり、私が最も認めている観点でもあります.
    アプリケーションがクラッシュすると、もう使えるアプリケーションContextがないので、使っても反応しません.
    でももしあなたがActivityを使っていたらContextは目的を達成することができるが、Activityが回収できないリスクがあるため、非常に推奨しない.なにしろ一言表示のためにメモリが漏れてしまったので、ゴマを拾ってスイカをなくした.
    欠点を補う
    私たちはUncaughtExceptionHandlerでヒントを与えるのは不便ですが、起動したActivityにIntentを伝える以上、直接それを使えばいいです.
    /** *           Activity Bundle   * @param bundle    Bundle   * @return True:      False:             */
        public static boolean handleIncomingBundle(@NonNull Bundle bundle , Context context){
    
            //    Bundle             
            if (bundle.get(IS_RECOVERY) != null){
                if (bundle.getBoolean(HAS_LOGS)){
                    Toast.makeText(context, "      ,        ", Toast.LENGTH_SHORT).show();
                }else {
                    Toast.makeText(context, "     ", Toast.LENGTH_SHORT).show();
                }
                Throwable throwable = (Throwable)bundle.getSerializable(THROWABLE_OBJECT);
                if (throwable != null){
                    Log.e("      ", "---------------------------------------------");
                    for (int i = 0; i < throwable.getStackTrace().length; i++) {
                        System.out.println(throwable.getStackTrace()[i]);
                    }
                    Log.e("      ", "---------------------------------------------");
                }else {
                    Log.e("      ", "            !");
                }
                return true;
            }else {
                return false;
            }
        }

    起動するActivityで使用します.
    if (getIntent().getExtras() != null){
                OCExceptionHandler.handleIncomingBundle(getIntent().getExtras(),getApplicationContext());
            }

    みんな見終わったら何か言いたいことがあるんだよ~~~
    頂点をクリックしてQQQ