Activity結合Fragment生命周期的全面測試;Fragment not attached to Activity問題解析
最近的一個項目中出現以下問題,用instant run快速重新開機應用時總是報Fragment not attached to Activity異常,由于目趕工沒時間做其他測試,自己感覺也問題不大就暫時沒去管他。這幾天有時間解決一下。
其實一出現這個問題心裡大概有個數是什麼原因導緻的苦于不知道具體原理和症結,對于程式從啟動到結束的具體流程不熟悉。
該異常,是因為Fragment的還沒有Attach到Activity時,調用了如getResource()、getString()等,需要上下文Content的函數。原因就是這麼個原因解決辦法也是找到一些其中就有加isAdded()判斷,如果你知道具體那一步會出現這個問題的話确實可以解決,但是不能因為問題解決了而不去先其中的深層次問題。比如為什麼fragment會出現未和acticity關聯的問題?fragment在activity,運作時他們的各自生命周期是怎麼走的,相信搞明白這些,這個問題也就不是問題可以從多方面去解決。從以下幾個方面一起學習學習:
Activity生命周期
參考文檔
生命周期圖就不上了完善一大堆。一個一個來解析:
- onCreate:建立activity的時候調用此方法,我們一般情況會在此方法中,做一些資料初始化的工作,例如:加載布局,建立線程等等。
- onStart:在activity從不可見到可見的時候回調此方法。
- onRestart():表示activity正在重新啟動。出現的時機是activity的onPause和onStop執行了,接着使用者又回到這個activity時。activity重新變為可見狀态的時候,即重新切到前台回調此方法。
- onResume:activity擷取焦點的時候回調此方法,在此方法中可進行一些與界面相關的操作。
- onPause:界面失去焦點的時候回調此方法對使用者來說,界面變為不可觸,我們在這個方法中做一些資料儲存的工作表示,activity正在停止工作,緊接着會執行onStop。這裡不可以執行耗時操作。因為目前activity的onPause執行完,下一個activity的onResume才會執行。
- onStop:表示activity的停止工作。activity變為不可見的時候回調此方法,可以執行一些耗時操作。
- onDestory:activity從記憶體中回收時調用此方法,我們一般在這個方法中做一些釋放記憶體的工作,将全局變量指向null表示activity的銷毀。
Fargment生命周期
參考文檔
- onAttach():回調獲得activity的傳遞值,這個時候fragment和activity建立了關聯,一般可以通過getActivity()獲得依附的activity。他隻調用一次。為什麼是一般呢,因為有的時候activity會意外銷毀,這個時候如果程式中有耗時操作如網絡請求,線程還在執行但是此時獲得不到上下文會報空指針。我的問題就是因為這個空指針導緻:(網絡請求後獲得資料渲染資料用到了上下文,如果這個時候突然重新開機activity就導緻了fragment調用了兩次生命周期,第一次調用後緊接着第二次,兩次都會開啟線程網絡請求第一次是擷取不到資料的會傳回失敗,而我在傳回失敗是用了土司,這時候fragment還沒atach到activity是以報not attached to Activity異常)
- onCreate():系統建立fragment的時候回調他,在他裡面執行個體化一些變量這些個變量主要是:當你停止的時候你想保持的資料。他也隻調用一次。
- onCreateView():第一次使用的時候fragment會在這上面畫一個Layout出來,要傳回一個布局的View,也可以傳回null,當系統用到fragment的時候fragment就要傳回他的view,越快越好,是以盡量在這裡不要做耗時操作,當然線程還是可以的。說白了就是加載fragment的布局的。一般會先判斷是否為null。
-
onActivityCreated()
當Activity中的onCreate方法執行完後調用。
是以不要再onCreateView()中進行 與activity有互動的UI操作,UI互動操作可以在onActivityCreated()裡面進行。是以呢:這個方法主要是初始化那些你需要你的父Activity或者Fragment的UI已經被完整初始化才能初始化的元素。如果在onCreateView裡面初始化空間 會慢很多,比listview等。
- onStart():同activity。Fragement 啟動時回調, 此時Fragement可見。
- onResume():同activity。在activity中運作是可見的激活Fragement 進入前台, 可擷取焦點時激活。
-
onPause():同activity。其他的activity獲得焦點,這個仍然可見:
指的是使用者離開這個fragment(并不是被銷毀)。
- onStop():同activity。fragment不可見的,可能情況:activity被stopped了或fragment被移除但被加入到回退棧中一個stopped的fragment仍然是活着的如果長時間不用也會被移除。
-
onDestroyView():Fragment中的布局被移除時調用,表示fragemnt銷毀相關聯的UI布局清除所有跟視圖相關的資源。
用處:ViewPager+Fragment,由于ViewPager的緩存機制,每次都會加載3頁。例如:有四個fragment 滑動到第四頁的時候 第一頁執行onDestroyView(),但沒有執行onDestroy。他依然和activity關聯。當在滑動到第一頁的時候又執行了onCreateView(),會出現重複加載view的局面,解決方法自行百度。。
- onDestroy():同activity。銷毀fragment對象。
- onDetach():Fragment和Activity解除關聯的時候調用脫離activity。
測試代碼
Activity:
@Override
protected void onCreate(Bundle savedInstanceState) {
Log.d("fty", "onCreate: " +"start");
super.onCreate(savedInstanceState);
Log.d("fty", "onCreate: " +"end");
setContentView(R.layout.activity_home);
FrameLayout container = (FrameLayout) findViewById(R.id.container);
TextView textView = (TextView) findViewById(R.id.tv);
textView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
recreate();
}
});
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.add(R.id.container,PlusOneFragment.newInstance()).commit();
}
@Override
protected void onStart() {
Log.d("fty", "onStart: " + "start");
super.onStart();
Log.d("fty", "onStart: " + "end");
}
@Override
protected void onResume() {
Log.d("fty", "onResume: " + "start");
super.onResume();
Log.d("fty", "onResume: " + "end");
}
@Override
protected void onPause() {
Log.d("fty", "onPause: " + "start");
super.onPause();
Log.d("fty", "onPause: " + "end");
}
@Override
protected void onStop() {
Log.d("fty", "onStop: " + "start");
super.onStop();
Log.d("fty", "onStop: " + "end");
}
@Override
protected void onDestroy() {
Log.d("fty", "onDestroy: " + "start");
super.onDestroy();
Log.d("fty", "onDestroy: " + "end");
}
Fragment:
@Override
public void onAttach(Context context) {
Log.e("fty", "onAttach: " + "start");
super.onAttach(context);
Log.e("fty", "onAttach: " + "end");
}
@Override
public void onCreate(Bundle savedInstanceState) {
Log.e("fty", "onCreate: " + "start");
super.onCreate(savedInstanceState);
Log.e("fty", "onCreate: " + "end");
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.e("fty", "onCreateView: " + "start");
View view = inflater.inflate(R.layout.fragment_plus_one, container, false);
TextView textView = (TextView) view.findViewById(R.id.tv);
Log.e("fty", "onCreateView: " + "end");
return view;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
Log.e("fty", "onActivityCreated: " + "start");
super.onActivityCreated(savedInstanceState);
Log.e("fty", "onActivityCreated: " + "end");
}
@Override
public void onStart() {
Log.e("fty", "onStart: " + "start");
super.onStart();
Log.e("fty", "onStart: " + "end");
}
@Override
public void onResume() {
Log.e("fty", "onResume: " + "start");
super.onResume();
Log.e("fty", "onResume: " + "end");
}
@Override
public void onPause() {
Log.e("fty", "onPause: " + "start");
super.onPause();
Log.e("fty", "onPause: " + "end");
}
@Override
public void onStop() {
Log.e("fty", "onStop: " + "start");
super.onStop();
Log.e("fty", "onStop: " + "end");
}
@Override
public void onDestroyView() {
Log.e("fty", "onDestroyView: " + "start");
super.onDestroyView();
Log.e("fty", "onDestroyView: " + "end");
}
@Override
public void onDestroy() {
Log.e("fty", "onDestroy: " + "start");
super.onDestroy();
Log.e("fty", "onDestroy: " + "end");
}
@Override
public void onDetach() {
Log.e("fty", "onDetach: " + "start");
super.onDetach();
Log.e("fty", "onDetach: " + "end");
}
測試結果
啟動時候的生命周期:
重新開機activity時的生命周期:
總結
由重新開機activity的生命周期中可以看出activity的onCreate()開始執行還沒結束的時候Fragment的onAtach()和onCreate()已經開始執行并執行完畢,之後Fragment執行第一次onCreateView()此時代碼中是擷取不到上下文的,但第二次執行onCreateView()時才能擷取到上下文。是以如果在onCreateView()中有線程進行耗時操作并且有用到上下文的的話,當重新開機activity的時候第一次就擷取不到上下文報空指針。