Android應(yīng)用開(kāi)發(fā)中Fragment存儲(chǔ)功能的基本用法
一、引言
在移動(dòng)應(yīng)用程序的架構(gòu)設(shè)計(jì)中,界面與數(shù)據(jù)即不可分割又不可混淆。在絕大部分的開(kāi)發(fā)經(jīng)歷中,我們都是使用Fragment來(lái)進(jìn)行界面編程,即使保存數(shù)據(jù)基本上也只是界面相關(guān)控件的數(shù)據(jù),很少做其他的數(shù)據(jù)保存,畢竟這樣與開(kāi)發(fā)原則相背,而今天這一篇博客就要來(lái)介紹一下Fragment的另類(lèi)用法,只是用來(lái)保存數(shù)據(jù)而沒(méi)有任何界面元素。
二、實(shí)現(xiàn)背景
對(duì)于Fragment的數(shù)據(jù)保存方法,不難想到還是與setRetainInstance有關(guān)系的。這樣一來(lái)所處的背景也是在屏幕旋轉(zhuǎn)或其他配置改變時(shí)需要用到。無(wú)論在開(kāi)發(fā)中我們的界面是用Activity還是Fragment生成的,在屏幕發(fā)生旋轉(zhuǎn)時(shí),都會(huì)在生命周期onSaveInstanceState中做控件狀態(tài)和必要數(shù)據(jù)的緩存工作。通常情況下,會(huì)用到Bundle來(lái)存儲(chǔ)數(shù)據(jù)。如Bundle的官方介紹所說(shuō),Bundle是一個(gè)用來(lái)存儲(chǔ)String及其他序列化數(shù)據(jù)類(lèi)型的map。同樣Android中也存在著這樣的一個(gè)異常:http://developer.android.com/intl/zh-cn/reference/android/os/TransactionTooLargeException.html
這個(gè)異常從字面上看不難理解,是傳輸數(shù)據(jù)過(guò)大異常。在描述中可知,現(xiàn)行Android系統(tǒng)中對(duì)于應(yīng)用程序的傳輸數(shù)據(jù)大小限制在1Mb以內(nèi)。所以如果在屏幕旋轉(zhuǎn)過(guò)程中使用Bundle緩存大數(shù)據(jù)并不是十分安全的。這樣的大數(shù)據(jù)在Android中很經(jīng)典的代表之一就是Bitmap,即使Bitmap已經(jīng)是序列化數(shù)據(jù),能夠方便的使用Bundle作為緩存媒介,但是筆者還是強(qiáng)烈不建議這樣做。下邊,就提供一個(gè)簡(jiǎn)單的解決途徑。
三、實(shí)現(xiàn)過(guò)程
首先,創(chuàng)建一個(gè)用來(lái)保存數(shù)據(jù)的Fragment:
public class BitmapDataFragment extends Fragment {
public static final String TAG = "bitmapsaver";
private Bitmap bitmap;
private BitmapDataFragment(Bitmap bitmap) {
this.bitmap = bitmap;
}
public static BitmapDataFragment newInstance(Bitmap bitmap) {
return new BitmapDataFragment(bitmap);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
}
public Bitmap getData() {
return bitmap;
}
}
這個(gè)Fragment沒(méi)有任何界面,在onCreate生命周期中使用setRetainInstance(true)確保不會(huì)隨載體銷(xiāo)毀,從而確保數(shù)據(jù)的安全性。
創(chuàng)建完成后,實(shí)踐一下使用過(guò)程,假設(shè)其使用者是Activity:
@Override
protected void onSaveInstanceState(Bundle outState) {
if (mBitmap != null) {
getSupportFragmentManager().beginTransaction()
.add(BitmapDataFragment.newInstance(mBitmap), BitmapDataFragment.TAG)
.commit();
outState.putBoolean(SENSE_IMAGE_KEY, true);
} else {
outState.putBoolean(SENSE_IMAGE_KEY, false);
}
super.onSaveInstanceState(outState);
}
在設(shè)備發(fā)生旋轉(zhuǎn)時(shí),檢測(cè)當(dāng)前界面中顯示的某個(gè)Bitmap,如果確實(shí)有數(shù)據(jù),則new出一個(gè)我們剛剛創(chuàng)建的Fragment,將Bitmap數(shù)據(jù)放置進(jìn)去,然后將這個(gè)Fragment添加到FragmentManager中并指定tag,這樣我們?cè)诨謴?fù)狀態(tài)后就可以方便的找到它。
在恢復(fù)時(shí)候,Activity的生命周期走到了onCreate()中,在這里我們可以通過(guò)檢測(cè)Bundle參數(shù)來(lái)確定是否有Bitmap數(shù)據(jù)待?。?/p>
if (savedInstanceState.getBoolean(SENSE_IMAGE_KEY)) {
BitmapDataFragment fragment = (BitmapDataFragment) getSupportFragmentManager()
.findFragmentByTag(BitmapDataFragment.TAG);
bitmap = fragment.getData();
getSupportFragmentManager().beginTransaction().remove(fragment).commit();
}
PS:在取出我們所需的Bitmap數(shù)據(jù)后不要忘記把作為數(shù)據(jù)容器的這個(gè)Fragment從FragmentManager中移除掉,釋放其占用的系統(tǒng)內(nèi)存。
四、Fragment的非中斷保存
1.setRetaineInstance
首先,要明確什么叫“非中斷保存”。熟悉Fragment的開(kāi)發(fā)人員都知道,F(xiàn)ragment是依附于Activity的。當(dāng)Activity銷(xiāo)毀時(shí),F(xiàn)ragment會(huì)隨之銷(xiāo)毀。而當(dāng)Activity配置發(fā)生改變(如屏幕旋轉(zhuǎn))時(shí)候,舊的Activity會(huì)被銷(xiāo)毀,然后重新生成一個(gè)新屏幕旋轉(zhuǎn)狀態(tài)下的Activity,自然而然的Fragment也會(huì)隨之銷(xiāo)毀后重新生成,而新生成的Fragment中的各個(gè)對(duì)象也與之前的那個(gè)Fragment不一樣,伴隨著他們的動(dòng)作、事件也都不一樣。所以,這時(shí)候如果想保持原來(lái)的Fragment中的一些對(duì)象,或者想保持他們的動(dòng)作不被中斷的話,就迫切的需要將原來(lái)的Fragment進(jìn)行非中斷式的保存。
2.生命周期
Activity的生命周期在配置發(fā)生改變時(shí):
onPuase->onStop->onDestroy->onStart->onResume
比如在Activity中發(fā)生屏幕旋轉(zhuǎn),其生命周期就是如此。而在onDestroy中,Activity會(huì)將其FragmentManager所包含的Fragment都銷(xiāo)毀掉(默認(rèn)狀態(tài)),即Fragment的生命周期為:
onDestroyView->onDestroy->onDetach
通過(guò)查看FragmentManager.java的代碼,可以發(fā)現(xiàn)在Fragment生命周期執(zhí)行到onDestroyView時(shí)候,狀態(tài)會(huì)由正常的ACTIVITY_CREATED變?yōu)镃REATED。而到了onDestroy生命周期時(shí)候,執(zhí)行的代碼出現(xiàn)了有意思的事情:
if (!f.mRetaining) {
f.performDestroy();
}
f.mCalled = false;
f.onDetach();
if (!f.mCalled) {
throw new SuperNotCalledException("Fragment " + f
+ " did not call through to super.onDetach()");
}
if (!keepActive) {
if (!f.mRetaining) {
makeInactive(f);
} else {
f.mActivity = null;
f.mParentFragment = null;
f.mFragmentManager = null;
}
}
當(dāng)Fragment的mRetaining被置true的時(shí)候,Destroy生命周期并不會(huì)執(zhí)行,而Fragment的mRetaining狀態(tài)是通過(guò)其retainNonConfig()來(lái)配置的,配置條件是Fragment不為空且Framgnet的mRetainInstance為true。到這里就能看到,如果想要自己的Fragment不被銷(xiāo)毀掉,就要讓這個(gè)mRetainInstance為true。
通過(guò)查閱Fragment.java源碼發(fā)現(xiàn),通過(guò)API setRetainInstance和getRetainInstance可以對(duì)其進(jìn)行操作。同樣,Android文檔中對(duì)這兩個(gè)接口也有了一定的描述。
這里結(jié)合Fragment.java中setRetainInstance的注釋進(jìn)行一下Fragment非中斷保存的總結(jié)。原注釋如下:
/**
* Control whether a fragment instance is retained across Activity
* re-creation (such as from a configuration change). This can only
* be used with fragments not in the back stack. If set, the fragment
* lifecycle will be slightly different when an activity is recreated:
* <ul>
* <li> {@link #onDestroy()} will not be called (but {@link #onDetach()} still
* will be, because the fragment is being detached from its current activity).
* <li> {@link #onCreate(Bundle)} will not be called since the fragment
* is not being re-created.
* <li> {@link #onAttach(Activity)} and {@link #onActivityCreated(Bundle)} <b>will</b>
* still be called.
* </ul>
*/
public void setRetainInstance(boolean retain) {
if (retain && mParentFragment != null) {
throw new IllegalStateException(
"Can't retain fragements that are nested in other fragments");
}
mRetainInstance = retain;
}
如果想叫自己的Fragment即使在其Activity重做時(shí)也不進(jìn)行銷(xiāo)毀那么就要設(shè)置setRetainInstance(true)。進(jìn)行了這樣的操作后,一旦發(fā)生Activity重組現(xiàn)象,F(xiàn)ragment會(huì)跳過(guò)onDestroy直接進(jìn)行onDetach(界面消失、對(duì)象還在),而Framgnet重組時(shí)候也會(huì)跳過(guò)onCreate,而onAttach和onActivityCreated還是會(huì)被調(diào)用。需要注意的是,要使用這種操作的Fragment不能加入backstack后退棧中。并且,被保存的Fragment實(shí)例不會(huì)保持太久,若長(zhǎng)時(shí)間沒(méi)有容器承載它,也會(huì)被系統(tǒng)回收掉的。
五、總結(jié)
很簡(jiǎn)單的Fragment非主流用法,相比直接使用Bundle保存數(shù)據(jù)確實(shí)是復(fù)雜了些,但是能夠更安全的進(jìn)行數(shù)據(jù)轉(zhuǎn)移對(duì)應(yīng)用來(lái)說(shuō)還是很好的一件事。推薦指數(shù)五顆星★★★★★!
- Android應(yīng)用UI開(kāi)發(fā)中Fragment的常見(jiàn)用法小結(jié)
- Android Fragment概述及用法
- Android基礎(chǔ)之使用Fragment控制切換多個(gè)頁(yè)面
- Android基礎(chǔ)之Fragment與Activity交互詳解
- Android中fragment嵌套fragment問(wèn)題解決方法
- Android Fragment 基本了解(圖文介紹)
- android開(kāi)發(fā)教程之實(shí)現(xiàn)滑動(dòng)關(guān)閉fragment示例
- Android使用Fragment打造萬(wàn)能頁(yè)面切換框架
- Android應(yīng)用開(kāi)發(fā)中Fragment的靜態(tài)加載與動(dòng)態(tài)加載實(shí)例
- Android中Fragment的基本用法示例總結(jié)
相關(guān)文章
OKhttp攔截器實(shí)現(xiàn)實(shí)踐環(huán)節(jié)源碼解析
這篇文章主要為大家介紹了OKhttp攔截器實(shí)現(xiàn)實(shí)踐環(huán)節(jié)源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-01-01
Android實(shí)現(xiàn)顏色選取圓盤(pán)
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)顏色選取圓盤(pán),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-06-06
Kotlin開(kāi)發(fā)實(shí)戰(zhàn)之hello world
這篇文章主要為大家詳細(xì)介紹了Kotlin開(kāi)發(fā)實(shí)戰(zhàn)之hello world的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-05-05
Android組件化工具ARouter使用方法詳細(xì)分析
這篇文章主要介紹了Android組件化工具ARouter使用方法,組件化項(xiàng)目存在各個(gè)模塊之間耦合,通信麻煩的問(wèn)題,為了解決這個(gè)問(wèn)題,阿里巴巴的開(kāi)發(fā)者就搞出了Arouter這個(gè)框架,以解決上述問(wèn)題2022-10-10
Android ButtonOnClick事件的寫(xiě)法總結(jié)
這篇文章主要介紹了Android ButtonOnClick事件的寫(xiě)法總結(jié)的相關(guān)資料,這里把Android ButtonOnClick的寫(xiě)法做個(gè)總結(jié),希望能幫助到大家,需要的朋友可以參考下2017-07-07
Android編程實(shí)現(xiàn)動(dòng)態(tài)更新ListView的方法
這篇文章主要介紹了Android編程實(shí)現(xiàn)動(dòng)態(tài)更新ListView的方法,結(jié)合實(shí)例形式詳細(xì)分析了ListView的布局及動(dòng)態(tài)更新實(shí)現(xiàn)方法,需要的朋友可以參考下2016-02-02
Android Studio多工程引用同一個(gè)library項(xiàng)目配置的解決方法
大家在使用android studio的時(shí)候,會(huì)遇到多個(gè)項(xiàng)目引用相同的library這篇文章主要介紹了Android Studio多工程引用同一個(gè)library項(xiàng)目配置方法,需要的朋友可以參考下2018-03-03
Android6.0編程實(shí)現(xiàn)雙向通話自動(dòng)錄音功能的方法詳解
這篇文章主要介紹了Android6.0編程實(shí)現(xiàn)雙向通話自動(dòng)錄音功能的方法,結(jié)合實(shí)例形式分析了Android錄音功能的原理、實(shí)現(xiàn)技巧與相關(guān)注意事項(xiàng),需要的朋友可以參考下2017-07-07

