Cockroach 2.0
為什么開發這個庫
很多時候由於一些微不足道的bug導致app崩潰很可惜,android默認的異常殺進程機制簡單粗暴,但很多時候讓app崩潰其實也並不能解決問題。
有些bug可能是系統bug,對於這些難以預料的系統bug我們不好繞過,還有一些bug是我們自己編碼造成的,對於有些bug來說直接忽略掉的話可能只是導致部分不重要的功能沒法使用而已,又或者對用戶來說完全沒有影響,這種情況總比每次都崩潰要好很多。
下面介紹幾個真實案例來說明這個庫的優勢:
-
有一款特殊的手機,每次開啟某個Activity時都報錯,提示沒有在清單中聲明,但其他幾百萬機型都沒問題,這種情況很可能就是系統bug了,由於是在onclick回調里直接使用startActivity來開啟Activity,onclick里沒有其他邏輯,對於這種情況的話直接忽略掉是最好的選擇,因為onclick回調是在一個單獨的message中的,執行完了該message就接着執行下一個message,該message執行不完也不會影響下一個message的執行,調用startactivity后會同步等待ams返回的錯誤碼,結果這款特殊的機型返回了沒有聲明這個Activity,所以對於這種情況可以直接忽略掉,唯一的影響就是這個Activity不會顯示,就跟沒有調用onClick一樣
-
我們在app中集成了個三方的數據統計庫,這個庫是在Application的onCreate的最后初始化的,但上線后執行初始化時卻崩潰了,對於這種情況直接忽略掉也是最好的選擇。根據app的啟動流程來分析,Application的創建以及onCreate方法的調用都是在同一個message中執行的,該message執行的最后調用了Application的onCreate方法,又由於這個數據統計庫是在onCreate的最后才初始化的,所以直接忽略的話也沒有影響,就跟沒有初始化過一樣
-
我們做了個檢查app是否需要升級的功能,若需要升級,則使用context開啟一個dialog風格的Activity提示是否需要升級,測試階段沒有任何問題,但一上線就崩潰了,提示沒有設置FLAG_ACTIVITY_NEW_TASK,由於啟動Activity的context是Application,但在高版本android中,可以使用Application啟動Activity並且不設置這個FLAG,但在低版本中必須要設置這個FLAG,對於這種問題也可以直接忽略
API28 ContextImpl startActivity源碼
public void startActivity(Intent intent, Bundle options) { warnIfCallingFromSystemProcess(); // Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is // generally not allowed, except if the caller specifies the task id the activity should // be launched in. A bug was existed between N and O-MR1 which allowed this to work. We // maintain this for backwards compatibility. final int targetSdkVersion = getApplicationInfo().targetSdkVersion; if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 && (targetSdkVersion < Build.VERSION_CODES.N || targetSdkVersion >= Build.VERSION_CODES.P) && (options == null || ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) { throw new AndroidRuntimeException( "Calling startActivity() from outside of an Activity " + " context requires the FLAG_ACTIVITY_NEW_TASK flag." + " Is this really what you want?"); } mMainThread.getInstrumentation().execStartActivity( getOuterContext(), mMainThread.getApplicationThread(), null, (Activity) null, intent, -1, options); }
- 還有各種執行onclick時觸發的異常,這些很多時候都是可以直接忽略掉的
更新日志
- 修復Android P反射限制導致的Activity生命周期異常無法finish Activity問題
Cockroach 2.0新特性
- Cockroach 2.0減少了Cockroach 1.0版本中Activity生命周期中拋出異常黑屏的問題。
- Cockroach 1.0未雨綢繆,提前做好准備,等待異常到來。Cockroach 2.0馬后炮,只有當拋出異常時才去拯救。
- Cockroach 2.0試圖在APP即將崩潰時盡量去挽救,不至於情況更糟糕。
用一張圖片來形容就是
特別注意: 當view的measure,layout,draw,以及recyclerview的bindviewholder 方法拋出異常時會導致 viewrootimpl掛掉,此時會回調 onMayBeBlackScreen 方法,建議直接殺死app。以后的版本會只 finish掉viewrootimpl掛掉的Activity而不是直接殺死app
使用姿勢
- 必須要在Application初始化時裝載
例如:
package com.wanjian.demo; import android.app.Application; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.widget.Toast; import com.wanjian.cockroach.Cockroach; /** * Created by wanjian on 2018/5/19. */ public class App extends Application { @Override public void onCreate() { super.onCreate(); install(); } private void install() { Cockroach.install(new ExceptionHandler() { @Override protected void onUncaughtExceptionHappened(Thread thread, Throwable throwable) { Log.e("AndroidRuntime", "--->onUncaughtExceptionHappened:" + thread + "<---", throwable); new Handler(Looper.getMainLooper()).post(new Runnable() { @Override public void run() { toast.setText(R.string.safe_mode_excep_tips); toast.show(); } }); } @Override protected void onBandageExceptionHappened(Throwable throwable) { throwable.printStackTrace();//打印警告級別log,該throwable可能是最開始的bug導致的,無需關心 toast.setText("Cockroach Worked"); toast.show(); } @Override protected void onEnterSafeMode() { int tips = R.string.safe_mode_tips; Toast.makeText(App.this, getResources().getString(tips), Toast.LENGTH_LONG).show(); } @Override protected void onMayBeBlackScreen(Throwable e) { Thread thread = Looper.getMainLooper().getThread(); Log.e("AndroidRuntime", "--->onUncaughtExceptionHappened:" + thread + "<---", e); //黑屏時建議直接殺死app sysExcepHandler.uncaughtException(thread, new RuntimeException("black screen")); } }); } }
原理分析
cockroach2.0通過替換ActivityThread.mH.mCallback
,實現攔截Activity生命周期, 通過調用ActivityManager的finishActivity
結束掉生命周期拋出異常的Activity
相關視頻 https://github.com/android-notes/Cockroach/blob/master/cockroach.mp4?raw=true