androidクラッシュフラッシュバック処理の根絶
オンライン上で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が表示されます.に注意
すべての異常をキャプチャできますが、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);
}
}