一次艱難的 NullPointerException 排查

異常信息

Fatal Exception: java.lang.NullPointerException: Attempt to invoke interface method 'void get(Context context) on a null object reference
       at com.snaptube.mixed_list.view.card.MultiSelectCardViewHolder.(Unknown Source)
       at com.snaptube.mixed_list.fragment.MultiSelectFragmentDelegate.onCreateViewHolder(Unknown Source:429)
       at com.snaptube.search.view.YouTubeMultiSelectFragment.onCreateViewHolder(Unknown Source:220)
       at com.snaptube.mixed_list.view.list.MixedAdapter.onCreateViewHolder(Unknown Source:61)
       at android.support.v7.widget.RecyclerView$Adapter.createViewHolder(Unknown Source:6321)
       at android.support.v7.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(Unknown Source:5509)
       at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(Unknown Source:5394)
       at android.support.v7.widget.RecyclerView$Recycler.getViewForPosition(Unknown Source:5390)
       at android.support.v7.widget.LinearLayoutManager$LayoutState.next(Unknown Source:2149)
       at android.support.v7.widget.LinearLayoutManager.layoutChunk(Unknown Source:1533)
       at android.support.v7.widget.LinearLayoutManager.fill(Unknown Source:1496)
       at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(Unknown Source:593)
       at android.support.v7.widget.RecyclerView.dispatchLayoutStep2(Unknown Source:3537)
       at android.support.v7.widget.RecyclerView.dispatchLayout(Unknown Source:3266)
       at android.support.v7.widget.RecyclerView.consumePendingUpdateOperations(Unknown Source:1603)
       at android.support.v7.widget.RecyclerView$ViewFlinger.run(Unknown Source:4656)
       at android.os.Handler.handleCallback(Handler.java:739)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:145)
       at android.app.ActivityThread.main(ActivityThread.java:6934)
       at java.lang.reflect.Method.invoke(Method.java)
       at java.lang.reflect.Method.invoke(Method.java:372)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1404)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1199)

具體而言,就是說在調(diào)用如下方法時,返回 null

public static <T> T get(Context context) {
    return (T) context.getSystemService(SERVICE_NAME);
  }

這個方法定義在 DaggerService 中:

public class DaggerService {
  public static final String SERVICE_NAME = DaggerService.class.getName();

  public DaggerService() {
  }

  public static <T> T get(Context context) {
    return (T) context.getSystemService(SERVICE_NAME);
  }
}

既然問題已經(jīng)定位,就需要排查原因了,可能原因如下:

  1. context 類型不符,即 context 不為 Activity/Application 類型,這時會導(dǎo)致 context.getSystemService() 方法中沒有我們自定義的 SERVICE_NAME 方法,從而導(dǎo)致 context.getSystemService() ;
  2. context 為 Activity/Application 類型,這時就是因為由于我們自己的 getSystemService() 內(nèi)部邏輯輸出 null;

根據(jù)分析,要定位具體原因的話,需要按如下步驟進(jìn)行處理:

  1. 判斷 context 類型,若 context 不為 Activity/Application 類型,那么說明問題產(chǎn)生的原因為“傳入了一個錯誤的 context 類型”;
  2. 若 context 為 Activity 類型,則查看在哪些情況下,BaseActivity 的 getSystemService 返回 null;
  3. 若 context 為 Application 類型,則查看在哪些情況下,BaseApplication 的 getSystemService 返回 null。

打日志顯示獲得的 Context 為 Activity 類型,所以現(xiàn)在只需要查看在什么情況下 BaseActivity 的 getSystemService 會返回 null 即可。查閱源碼,我們發(fā)現(xiàn) BaseActivity 的 getSystemService 代碼如下:

public Object getSystemService(@NonNull String name) {
    if (DaggerService.SERVICE_NAME.equals(name)) {
      return activityComponent;
    }
    return null;
  }

同時在 BaseActivity 的 onDestory 方法中會將 activityComponent 置為 null,所以當(dāng) Activity 的 destory 方法調(diào)用后,activityComponent 就會返回 null,而我們的應(yīng)用場景為,在 onLoadMore 方法中加載數(shù)據(jù),然后數(shù)據(jù)加載完成后獲取該 activityComponent,并更新主界面,因為數(shù)據(jù)加載為耗時操作,所以很有可能數(shù)據(jù)還未加載完成,所在的 Activity 已經(jīng)由于某種原因被銷毀了,此時就會出現(xiàn) NPE,解決辦法為在 RxJava 的事件流中添加 Activity 生命周期綁定,確保在 onDestory 方法被調(diào)用后不再進(jìn)行后續(xù)操作。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 173,296評論 25 708
  • ¥開啟¥ 【iAPP實現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開一個線程,因...
    小菜c閱讀 6,523評論 0 17
  • Android Studio JNI流程首先在java代碼聲明本地方法 用到native關(guān)鍵字 本地方法不用去實現(xiàn)...
    MigrationUK閱讀 11,952評論 7 123
  • 2014第二學(xué)期悅知讀者協(xié)會活動策劃方案 本協(xié)會秉持共同學(xué)習(xí)的宗旨,現(xiàn)對于2014第二學(xué)期相關(guān)活動作如下計劃: 一...
    爾酥閱讀 235評論 0 1
  • 生詞表 使用場景 在實際開發(fā)中,需要開啟N個異步線程,但是后續(xù)操作,需要依賴N個線程返回的數(shù)據(jù),需要接收所有線程任...
    c5f8c453b41e閱讀 2,868評論 0 3