androidクラッシュフラッシュバック処理の根絶

15349 ワード

アプリのメインスレッドが異常を投げ出すとアプリcrashが発生します.viewクリック時に異常が投げ出されたのかもしれません.このような異常はクリックしても反応しなくてもcrashを使わないことを望んでいます.ユーザーはせいぜい点が反応していないと思っているか、もともとクリックできないと思っているので、Cockroachを使うことができます.また、他の副作用がなく、ユーザーは点がないのと同じで、他の論理に影響を与えません.これは毎回crashよりずっとよく、少なくとも頻繁なcrashによってユーザーがアプリをアンインストールすることはありません.もちろんこのライブラリには、Activity初期化時などに異常が投げ出されるとActivityが何も表示されないという不確定な要因もありますが、これはANRではなく、Activityライフサイクルが完全に実行されていないため、issuesではANRだと考えている人が多く、さらにマイクロブログではこのライブラリが異常をキャプチャするとANRになると言われていますが、実はこのときメインスレッドはブロックされておらず、ANRも存在しません.もちろんこのライブラリはnative異常やANRにはどうしようもなく、java異常がcrashを起こさないことを保証するしかありません.
オンライン上でActivityに入ると大量のcrashがあることを発見した場合、Cockroachをマウントしてからアプリの実行に影響を与えず、ユーザーの健康診断に影響を与えない場合は、バックエンド制御で自動的にCockroachを開くことができ、このActivityを終了した後、自動的にCockroachをアンインストールします.
以下にも明確に説明する
必要に応じて任意の場所にマウントし、任意の場所でアンインストールできます.すべての異常をキャプチャできますが、viewの初期化時に異常が発生したり、異常後のコードが実行されなかったり、app crashには至らないかもしれませんが、view内部に問題が発生し、実行時に奇抜な現象が発生します.たとえばactivity宣言サイクルメソッドで異常が投げ出されると、ライフサイクルが不完全になり、さまざまな奇抜な現象を引き起こします.
このライブラリをどのように正しく利用するかが重要です
Cockroach
死なない強さ、永遠にcrashのAndroid.
Androidの開発で最も恐れられているのはcrashです.良いアプリのテストでは問題ありません.発表すると各種のcrashがあり、hotfixを緊急に発表することで解決するしかありませんが、hotfixを準備する時間が長い可能性があります.この時間はユーザー体験が非常に悪いので、androidではThreadを設定することができます.setDefaultUncaughtExceptionHandlerは、すべてのスレッドの例外をキャプチャしますが、プライマリ・スレッドが例外を投げ出すとactivityがフラッシュバックし、appプロセスが再起動します.Cockroachを使用すると、どんなに異常を投げてもactivityがフラッシュバックせず、appプロセスが再起動しないことを保証できます.D e f a u l t U n c aughtExceptionHandlerの使い方についてこのD e f a u l t U n c aughtExceptionHandlerを参照
使用方法
カスタムアプリケーションはandroidからのアプリケーションを継承し、アプリケーションにロードします.初期化が早ければ早いほど、アプリケーションのonCreateで初期化できます.もちろん、必要に応じて任意の場所(メインスレッドではなく)でロードし、任意の場所でアンインストールすることもできます.複数回のマウントとアンインストールが可能です.
例:
import android.app.Application;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;

/**
 * Created by wanjian on 2017/2/14.
 */

public class App extends Application {

    @Override
    public void onCreate() {
        super.onCreate();

        Cockroach.install(new Cockroach.ExceptionHandler() {

           // handlerException      try{            }catch(Throwable e){ } ,  handlerException        ,      handlerException

            @Override
            public void handlerException(final Thread thread, final Throwable throwable) {
            //     Cockroach       bug,         handlerException  Toast     ,
            //  handlerException      ui   ,Toast       ,  new   new Handler(Looper.getMainLooper()),
            //          run         ,  run      ui   。
            //new Handler(Looper.getMainLooper())        toast,      
                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        try {
                        //                ,      Error      log
                            Log.e("AndroidRuntime","--->CockroachException:"+thread+"

Cockroachのアンインストール
 Cockroach.uninstall();

テスト
Cockroachをマウントしてviewをクリックして異常とnew Handlerで異常を投げ出す
        final TextView textView = (TextView) findViewById(R.id.text);
        findViewById(R.id.install).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                textView.setText("    Cockroach");
                install();
            }
        });

        findViewById(R.id.uninstall).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                textView.setText("    Cockroach");
                Cockroach.uninstall();
            }
        });

        findViewById(R.id.but1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                throw new RuntimeException("click exception...");
            }
        });

        findViewById(R.id.but2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Handler().post(new Runnable() {
                    @Override
                    public void run() {
                        throw new RuntimeException("handler exception...");
                    }
                });
            }
        });

        findViewById(R.id.but3).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                new Thread() {
                    @Override
                    public void run() {
                        super.run();
                        throw new RuntimeException("new thread exception...");
                    }
                }.start();
            }
        });

        findViewById(R.id.but4).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                startActivity(new Intent(getApplicationContext(), SecActivity.class));
            }
        });

    }

    private void install() {
        Cockroach.install(new Cockroach.ExceptionHandler() {
            @Override
            public void handlerException(final Thread thread, final Throwable throwable) {

                Log.d("Cockroach", "MainThread: " + Looper.getMainLooper().getThread() + "  curThread: " + Thread.currentThread());

                new Handler(Looper.getMainLooper()).post(new Runnable() {
                    @Override
                    public void run() {
                        try {

                            Log.e("AndroidRuntime","--->CockroachException:"+thread+"

キャプチャされたスタックは、at com.wanjian.cockroach.Cockroach$1.run(Cockroach.java:47)によってブロックされていることがわかります.APPには何の影響もありません.フラッシュバックもありません.プロセスを再起動していません.
02-16 09:58:00.660 21199-21199/wj.com.fuck E/AndroidRuntime: --->CockroachException:Thread[main,5,main]CockroachException:Thread[main,5,main]CockroachException:Thread[Thread-26326,5,main]
at com.wanjian.cockroach.Cockroach$1.run(Cockroach.java:47)がブロックされておらず、APP crashが表示されます.
に注意
  • メインスレッドまたはサブスレッドが異常を放出するとexceptionHandlerが呼び出される.handlerException(Thread thread, Throwable throwable)
  • exceptionHandler.handlerExceptionは、UI以外のスレッドで実行される場合があります.
  • handlerException内部で手動try{あなたの異常処理ロジック}catch(Throwable){}を推奨し、handlerException内部で再び異常が放出されないようにし、handlerException
  • をループ呼び出します.
  • Threadが設定されている場合.setDefaultUncaughtExceptionHandlerでは、サブスレッドの例外を取得できない場合があります.

  • すべての異常をキャプチャできますが、viewの初期化時に異常が発生したり、異常後のコードが実行されなかったり、app crashには至らないかもしれませんが、view内部に問題が発生し、実行時に奇抜な現象が発生します.たとえばactivity宣言サイクルメソッドで異常が投げ出されると、ライフサイクルが不完全になり、さまざまな奇抜な現象を引き起こします.
    さまざまな奇抜な問題が発生する可能性がありますが、アプリの正常な動作を最大限に保証することができます.多くの場合、メインスレッドが異常を投げてもappの正常な使用に影響を与えないことを望んでいます.例えば、あるviewに背景色を設定すると、viewがnullなのでapp crashになります.このような問題では、viewが色を設定できなくてもcrashを使用しないことを望んでいます.この場合、Cockroachはあなたのニーズを満たすことができます.
    handlerException(final Thread thread,final Throwable throwable)の内部では、自分のサーバにこの異常をどのように処理するか、アプリや他の操作を直接無視するかを決定するように要求することを提案しています.
    Cockroachはandroid標準APIを採用し、依存性がなく、十分に軽量で、100行未満のコードまで軽量で、一般的に互換性の問題もなく、性能の問題もなく、すべてのandroidバージョンを互換性がある.
    jcenterにアップロードしました.wanjian:cockroach:0.0.5'
    原理分析:
    Androidで最も重要なのはHandlerメカニズムであり、簡単に言えばHandlerメカニズムはデッドサイクル内部でブロックキューヘッダのMessageを絶えず取り出すことであり、このブロックキューはメインスレッドの中で唯一であり、Messageがない場合、ループはブロックされ、Messageがあるとすぐにメインスレッドに取られ、Messageが実行される.
    Androidソースコードを確認すると、Activity Threadでmainメソッド(mainメソッド署名public static void main(String[] args){}、このmainメソッドは静的で、公有的で、アプリケーションのエントリと理解できる)が最後にLooper.loop();を実行し、このメソッド内部はデッドサイクル(for(;)ループ)のため、通常、例外が放出されない限り、プライマリ・スレッドは終了しません.queue.next();はブロックキューからヘッダを取り出すMessageであり,Messageがないとメインスレッドがここにブロックされ,Messageがあると下に進み続ける.Androidのview描画、イベント配布、activity起動、activityのライフサイクルコールバックなどはそれぞれMessageであり、androidはこれらのMessageをプライマリスレッド内の唯一のqueueに挿入し、すべてのメッセージがキューに並んでプライマリスレッドの実行を待つ.
    Activity Threadのmainメソッドは次のとおりです.
     public static void main(String[] args) {
             
    		 ...
            Looper.prepareMainLooper();//            queue
            ...
            ActivityThread thread = new ActivityThread();
            thread.attach(false);//     , queue   Message 
            ...
            Looper.loop();//     ,    Message
    
            throw new RuntimeException("Main thread loop unexpectedly exited");
        }


    Looper.loop()キーコードは のとおりです.

       for (;;) {
                Message msg = queue.next(); // might block
                ...
                msg.target.dispatchMessage(msg);//  Message
                ...
       }

    Androidメッセージメカニズムの コードは の りです.
    public class ActivityThread {
    
    	public static void main(String[]args){
    		
    		Queue queue=new Queue();//           ,       ArrayList
    		
    		queue.add(new Message(){
    			void run(){
    				...
    				print("android    ,     queue      Activity Message ");
    				Message msg=getMessage4LaunchMainActivity();
    				queue.add(msg);
    			}
    		
    		});
    		
    		for(;;){//     ,for             
    			Message  msg=queue.next();
    			
    			msg.run();
    		}
    	}
    }

    の を てandroidのメッセージメカニズムがよく かったと じています.Handlerメカニズムの については、このjavaエンジニアリング Handlerメカニズムコードを してください.
    Cockroachのコアコードを てみましょう
     new Handler(Looper.getMainLooper()).post(new Runnable() {
                @Override
                public void run() {
                   //       
                    while (true) {
                        try {
                            Looper.loop();//            
                        } catch (Throwable e) {
                                                    
                        }
                    }
                }
            });
           
            sUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
             //        ,            catch  ,                   
            Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
                @Override
                public void uncaughtException(Thread t, Throwable e) {
                    
                }
    });

    は で、Handlerを じてメインスレッドのqueueにRunnableを し、メインスレッドがこのRunnableに されると、 たちのwhileデッドサイクルに り、while が であればコードカードがここにあり、 にANRを くが、whileデッドサイクルでLooper.loop()を び し、メインスレッドがqueueのMessageを み り け、 するようになる.これにより、 でメインスレッドのすべての が たちが で び したLooper.loop()から げ されることを することができ、 げ すとtry{}catchにキャプチャされ、メインスレッドはcrashされません.このwhileがなければ、メインスレッドが に を げ すとキャプチャできません.これにより、アプリはcrashになります.そのため、whileを じてcrashが するたびに びメッセージサイクルに ります.whileの は、プライマリ・スレッドが を するたびに、プライマリ・スレッドをメッセージ・ループに アクセスさせることに られる. の コードで すことができます.
    public class ActivityThread {
    
    	public static void main(String[]args){
    		
    		Queue queue=new Queue();//           ,       ArrayList
    		
    		...
    		
    		for(;;){//     ,for             
    			Message  msg=queue.next();
    			
    			//  msg    post Runnable        
    				//  post Runnable    
    				  while (true) {
                        try {
                           	 for(;;){//           msg.run()   ,       try{}catch          ,            ,    queue      
                           	 	Message  msg=queue.next();
                           	 	msg.run();
                           	 }
                           
                        } catch (Throwable e) {
                        
                        }
    			//        	
    		}
    	}

    どうしてnew Handlerを るの?post は、メインスレッド の の で while (true) { try { Looper.loop(); } catch (Throwable e) {} }を するのではなく、
    これは、アクティブなonCreateなどのプライマリスレッドで するとwhileの のコードが されず、activityのライフサイクルも に することができず、Handlerを するデッドサイクルであるためである.post は、このメッセージの の に を えないことを することができる.
    「 なない さ、 にcrashしないAndroid」から
    では,Activityのライフサイクルメソッドで が した ,crashがActivityを できず になった だけが している.
    にcodeを ります.
    Cockroach:
    package com.support.framework.crash;
    
    import android.os.Binder;
    import android.os.Handler;
    import android.os.Looper;
    
    import com.support.BaseApp;
    
    /**
     * Created by wanjian on 2017/2/14.
     */
    
    public final class Cockroach {
    
        public interface ExceptionHandler {
    
            void handlerException(Thread thread, Throwable throwable);
        }
    
        private Cockroach() {
        }
    
        private static ExceptionHandler sExceptionHandler;
        private static Thread.UncaughtExceptionHandler sUncaughtExceptionHandler;
        private static boolean sInstalled = false;//   ,        
    
        /**
         *                 exceptionHandler.handlerException(Thread thread, Throwable throwable)
         * 

    * exceptionHandler.handlerException UI 。 *

    * Thread.setDefaultUncaughtExceptionHandler 。 * * @param exceptionHandler */ public static synchronized void install(ExceptionHandler exceptionHandler) { if (sInstalled) { return; } sInstalled = true; sExceptionHandler = exceptionHandler; new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { while (true) { try { Looper.loop(); } catch (Throwable e) { // Binder.clearCallingIdentity(); if (e instanceof QuitCockroachException) { return; } if (sExceptionHandler != null) { //Unable to start activity sExceptionHandler.handlerException(Looper.getMainLooper().getThread(), e); // sUncaughtExceptionHandler.uncaughtException(Looper.getMainLooper().getThread(), e); } } } } }); sUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler(); Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() { @Override public void uncaughtException(Thread t, Throwable e) { if (sExceptionHandler != null) { sExceptionHandler.handlerException(t, e); } } }); } public static synchronized void uninstall() { if (!sInstalled) { return; } sInstalled = false; sExceptionHandler = null; // , ANR, Thread.setDefaultUncaughtExceptionHandler(sUncaughtExceptionHandler); new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { throw new QuitCockroachException("Quit Cockroach.....");// , while (true) {} } }); } }

    QuitCockroachException:
    package com.support.framework.crash;
    
    /**
     * Created by wanjian on 2017/2/15.
     */
    
    final class QuitCockroachException extends RuntimeException {
        public QuitCockroachException(String message) {
            super(message);
        }
    }