優雅的處理 Android 崩潰問題(二)


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問題

cockroach1.0版在這

Cockroach 2.0新特性

  • Cockroach 2.0減少了Cockroach 1.0版本中Activity生命周期中拋出異常黑屏的問題。
  • Cockroach 1.0未雨綢繆,提前做好准備,等待異常到來。Cockroach 2.0馬后炮,只有當拋出異常時才去拯救。
  • Cockroach 2.0試圖在APP即將崩潰時盡量去挽救,不至於情況更糟糕。

用一張圖片來形容就是

img

特別注意: 當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

相關原理分析

相關連接


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM