日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android4.4.2fragment不显示,Android应用中使用Fragment组件的一些问题及解决方案总结...

發布時間:2025/3/21 Android 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android4.4.2fragment不显示,Android应用中使用Fragment组件的一些问题及解决方案总结... 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Fragment的主要意義就是提供與Activity綁定的生命周期回調。

Fragment不一定要向Activity的視圖層級中添加View. 當某個模塊需要獲得Activity的生命周期回調的時候,就可以考慮通過Fragment來實現.

例如: DialogFragment, 調用show方法來顯示一個Dialog(這個一個子Window,并不在Activity的視圖層級中),當旋屏時,DialogFragment利用onDestroyView回調來dismiss Dialog,然后Activity重建之后,DialogFragment利用onStart回調再顯示Dialog。

當然,我們也可以創建一個完全沒有UI的Fragment,比如BackgroundWorkerFragment,在onResume的時候執行一個Task,在onPause的時候暫停一個Task。

Fragment 生命周期先來回顧一下基礎知識,Fragment的生命周期圖如下:

說明:總的來說,Fragment和Activity的生命周期類似。需要注意的是,它相比于Activity,多了onAttach(), onDetch(), onCreateView()和onDestroyView()這幾個回調函數;但是,卻少了onRestart()。

Fragment的生命周期非常復雜,分為以下幾種情況:

如果是通過XML中的標簽實例化的,那么第一個收到的回調將是onInflate

如果setRetainInstance(true),那么當Activity重建時,Fragment的onDestroy以及Activity重建后Fragment的onCreate回調不會被調用.(無論是否將其添加到了返回棧)

如果當前顯示的是Fragment A,然后執行FragmentTransaction.replace(),那么Fragment A會執行onPause()->onStop()->onDestroyView()->onDestroy()->onDetach(),如果執行FragmentTransaction.replace().addToBackStack(),那么Fragment A會執行onPause()->onStop()->onDestroyView()

FragmentTransaction.hide(),將不會導致onPause(),而是會觸發onHiddenChanged()

FragmentTransaction.detach(),會導致onPause()->onStop()->onDestroyView(),注意:onDestroy()和onDetach()不會調用

FragmentTransaction

對于Fragment的操作都是通過FragmentTransaction來進行的,一個FragmentTransaction可以包含一個或者多個操作,通過commit或者commitAllowingStateLoss來提交.如果該FragmentTransaction被加入返回棧,那么出棧的時候,該Transaction中的所有操作都會被撤銷

commit方法是異步的(handler post相應的message到MainLooper關聯的Message queue),如果需要立刻執行Transaction的操作,可以調用executePendingTransactions()

FragmentTransaction的commit方法以及FragmentManager的popBackStack方法都是異步的,給調用者帶來了很多不便,雖然可以通過調用executePendingTransactions()方法來立即執行,但是為什么默認是異步的呢??(我覺得是因為:提交一個Transaction,會導致Fragment的生命周期方法的執行,甚至是多個回調的執行,如果Fragment在這些回調中又提交新的Transaction,那么可能會破壞當前Transaction的狀態,比方說這是一個pop操作)

Can not perform this action after onSaveInstanceState

在使用Fragment的過程中,常常會遇到在Activity的onSaveInstanceState方法調用之后,操作commit或者popBackStack而導致的crash.

因為在onSaveInstanceState方法之后的操作狀態可能會丟失,因此Android framework默認會拋出一個異常.

對于commit方法來說,單純避免這個異常很簡單,使用commitAllowingStateLoss方法即可.但是popBackStack以及popBackStackImmediate也都會檢查state(checkStateLoss),特別需要注意的是Activity的onBackPressed方法

public void onBackPressed() {

if (!mFragments.popBackStackImmediate()) {//注意

supportFinishAfterTransition();

}

}

如果onBackPressed在onSavedInstanceState之后調用,那么就會crash.

onBackPressed的調用時機:

* targetSdkVersion <= 5,在onKeyDown中調用

* targetSdkVersion > 5,在onKeyUp中調用

onSavedInstanceState的調用時機(如果調用的話):

* 一定在onStop之前

* 可能在onPause之前,也可能在onPause與onStop之間

需要注意的是:onSavedInstanceState方法不一定會調用,只有在Activity因為某些原因而被Framework銷毀,并且之后還需要重新創建的情況,才需要調用(例如:旋屏,或者內存不足而回收返回棧中的某些Activity)

舉例:

* Activity A在前臺時,屏幕逐漸變暗直至鎖屏,那么A的onSavedInstanceState會被調用

* Activity A start Activity B,Activity A的onSavedInstanceState會被調用

* Activity A因為返回鍵或者finish調用而返回到上一個界面,那么A的onSavedInstanceState不會被調用

因此,當onBackPressed在onSavedInstanceState方法之后調用,就一定會crash.解決方法主要有兩種:

重寫Activity的onSavedInstanceState()方法,并且注釋掉super調用.

這種方法能避免crash,但是它會導致整個Activity的狀態丟失.以DialogFragment為例,正常情況下,顯示的DialogFragment在旋屏Activity重新創建之后,不需要我們處理,Dialog會自動顯示出來(參見DialogFragment.onStart()),但是注釋掉Activity的onSavedInstanceState()方法之后,Fragment狀態丟失,Activity重新創建之后,Dialog也就不會再顯示出來了.

更好且通用的做法:在調用commit,popBackStack以及onBackPressed方法之前,判斷onSavedInstanceState()方法是否已經執行,并且onResume方法還沒有執行,如果不是,那么直接操作,否則加入到pending隊列,等待onResumeFragments或者onPostResume之后再執行.

注意:不要在onResume中操作,因為這時候FragmentManager中的mStateSaved依然可能是true.(如果執行順序是onSavedInstanceState()->onPause()->onResume() 或者 onPause()->onSavedInstanceState()->onResume())

例如:

public void onDataReceived() {

if(isStateSaved()) {//isStateSaved()由BaseActivity提供

addPendingFragmentOperation(new Runnable() {

@Override

public void run() {

getSupportFragmentManager().popBackStackImmediate();

}

});

} else {

getSupportFragmentManager().popBackStackImmediate();

}

}

@Override

protected void onPostResume() {

super.onPostResume();

if(pendingFragmentOperation != null && !pendingFragmentOperation.isEmpty()) {

for(Runnable operation : pendingFragmentOperation) {

operation.run();

}

pendingFragmentOperation.clear();

}

}

startActivityForResult

requestCode的可用區間:

1.Activity: [Integer.MIN_VALUE, Integer.MAX_VALUE]

(1)當requestCode取值在[Integer.MIN_VALUE, -1]區間中,效果和startActivity()一樣,不會收到onActivityResult()回調

(2)內置的Fragment可用requestCode的區間和Activity相同

2.support庫: Fragment,以及FragmentActivity:[-1, 65535]

(1)requestCode == -1,效果和startActivity()一樣,不會收到onActivityResult()回調

(2)requestCode 在 [Integer.MIN_VALUE, -2]或者[65536, Integer.MAX_VALUE]之間,會拋出異常(requestCode只能使用低16比特)

建議: requestCode的取值統一限制在[-1, 65535]之間

嵌套Fragment

首先要說的是盡量不要使用嵌套Fragment.

當在嵌套Fragment中使用startActivityForResult()時,會遇到的問題:

所有的Fragment都收不到onActivityResult()

某個level 1 的Fragment收到了onActivityResult()

總之那個發起startActivityForResult()的嵌套Fragment是一定不會收到onActivityResult()回調的.

原因如下:(可參考上面說的requestCode)

FragmentActivity.startActivityFromFragment()會改動requestCode,用高16比特存儲Fragment在FragmentManager中的index,而低16比特作為Fragment可用的requestCode.在FragmentActivity.onActivityResult()中,根據高16比特,從FragmentManager中找到對應的Fragment,然后將低16比特的值作為requestCode,調用Fragment.onActivityResult().

那么requestCode中只能存儲一個index,即root FragmentManager中的Fragment index.因此就會出現上面所列出的情形:

當嵌套Fragment在childFragmentManager中的index,大于rootFragmentManager中的所有index時, rootFragmentManager將找不到與此index對應的Fragment,所以沒有Fragment能收到onActivityResult()

當嵌套Fragment在childFragmentManager中的index,小于等于rootFragmentManager中的所有index時,那么隸屬于rootFragmentManager的一個Fragment將會收到onActivityResult()

總之即使能有Fragment能收到onActivityResult(),那也是頂層的某個Fragment,而不是發起請求的嵌套Fragment

解決方案:

不使用嵌套Fragment :)

依然利用requestCode,將其低16位拆分,其中的高8位用來存儲childFragmentManager中的index,低8位留給ChildFragment使用.(如果嵌套層級不深,那么此方案還是不錯的,如果層級較深,那么留給Fragment的requestCode的可用值區間將非常局限)

Android 4.2(Api 17)以后,可以使用內置的Fragment,以及ChildFragmentManager,內置Fragment不再需要借助requestCode的高16比特來記錄它的index.而是由Framework收到Fragment.startActivityForResult()時,記錄該Fragment的標識(android:fragment:${parentIndex}:${myIndex}),派發result時,就根據這個標識找到那個Fragment.因此就不會出現ChildFragment收不到onActivityResult()回調的問題了.可以參考Activity.dispatchActivityResult()

Tips

開發的時候,可以打開Fragment相關的調試信息

FragmentManager.enableDebugLogging(BuildConfig.DEBUG);

Activity的onResume被調用時,Fragment的onResume還未被調用.

protected void onPostResume() {

super.onPostResume();

mHandler.removeMessages(MSG_RESUME_PENDING);

onResumeFragments();

mFragments.execPendingActions();

}

protected void onResumeFragments() {

mFragments.dispatchResume();

}

如果需要在Fragment的onResume都執行完后再執行某個操作,可以重寫onPostResume()方法,一定要調用 super.onPostResume()

1.IllegalStateException(Fragment not attached to Activity)的問題

這個異常通常的發生情況是:在Fragment中啟動一個異步任務,然后在回調中執行和resource相關的操作(getString(...)),或者startActivity(...)之類的操作.但是這個時候Fragment可能已經被detach了,所以它的mHost==null,因此在執行這些操作之前,需要先判斷一下isAdded().

注意: 這里不要使用isDetached()來判斷,因為Fragment被detach之后,它的isDetached()方法依然可能返回false

2.如果Fragment A是因為被replace而detach的,那么它的isDetached()將返回false

3.如果Fragment A對應的FragmentTransaction被加入到返回棧中,因為出棧而detach,那么它的isDetached()將返回true

final public Resources getResources() {

if (mHost == null) {

throw new IllegalStateException("Fragment " + this + " not attached to Activity");

}

return mHost.getContext().getResources();

}

public void startActivity(Intent intent, @Nullable Bundle options) {

if (mHost == null) {

throw new IllegalStateException("Fragment " + this + " not attached to Activity");

}

mHost.onStartActivityFromFragment(this /*fragment*/, intent, -1, options);

}

《新程序員》:云原生和全面數字化實踐50位技術專家共同創作,文字、視頻、音頻交互閱讀

總結

以上是生活随笔為你收集整理的android4.4.2fragment不显示,Android应用中使用Fragment组件的一些问题及解决方案总结...的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。