Android應用開發中Fragment儲存功能的基本用法_Android

來源:互聯網
上載者:User

一、引言

在行動裝置 App程式的架構設計中,介面與資料即不可分割又不可混淆。在絕大部分的開發經曆中,我們都是使用Fragment來進行介面編程,即使儲存資料基本上也只是介面相關控制項的資料,很少做其他的資料儲存,畢竟這樣與開發原則相背,而今天這一篇部落格就要來介紹一下Fragment的另類用法,只是用來儲存資料而沒有任何介面元素。

二、實現背景

對於Fragment的資料儲存方法,不難想到還是與setRetainInstance有關係的。這樣一來所處的背景也是在旋轉螢幕或其他配置改變時需要用到。無論在開發中我們的介面是用Activity還是Fragment產生的,在螢幕發生旋轉時,都會在生命週期onSaveInstanceState中做控制項狀態和必要資料的緩衝工作。通常情況下,會用到Bundle來儲存資料。如Bundle的官方介紹所說,Bundle是一個用來儲存String及其他序列化資料類型的map。同樣Android中也存在著這樣的一個異常:http://developer.android.com/intl/zh-cn/reference/android/os/TransactionTooLargeException.html

這個異常從字面上看不難理解,是傳輸資料過大異常。在描述中可知,現行Android系統中對於應用程式的傳輸資料大小限制在1Mb以內。所以如果在旋轉螢幕過程中使用Bundle緩衝大資料並不是十分安全的。這樣的大資料在Android中很經典的代表之一就是Bitmap,即使Bitmap已經是序列化資料,能夠方便的使用Bundle作為緩衝媒介,但是筆者還是強烈不建議這樣做。下邊,就提供一個簡單的解決途徑。

三、實現過程

首先,建立一個用來儲存資料的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;  } } 


這個Fragment沒有任何介面,在onCreate生命週期中使用setRetainInstance(true)確保不會隨載體銷毀,從而確保資料的安全性。

建立完成後,實踐一下使用過程,假設其使用者是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);  } 

在裝置發生旋轉時,檢測當前介面中顯示的某個Bitmap,如果確實有資料,則new出一個我們剛剛建立的Fragment,將Bitmap資料放置進去,然後將這個Fragment添加到FragmentManager中並指定tag,這樣我們在恢複狀態後就可以方便的找到它。

在恢複時候,Activity的生命週期走到了onCreate()中,在這裡我們可以通過檢測Bundle參數來確定是否有Bitmap資料待取:

if (savedInstanceState.getBoolean(SENSE_IMAGE_KEY)) {   BitmapDataFragment fragment = (BitmapDataFragment) getSupportFragmentManager()    .findFragmentByTag(BitmapDataFragment.TAG);   bitmap = fragment.getData();   getSupportFragmentManager().beginTransaction().remove(fragment).commit(); } 

PS:在取出我們所需的Bitmap資料後不要忘記把作為資料容器的這個Fragment從FragmentManager中移除掉,釋放其佔用的系統記憶體。


四、Fragment的非中斷儲存

1.setRetaineInstance

首先,要明確什麼叫“非中斷儲存”。熟悉Fragment的開發人員都知道,Fragment是依附於Activity的。當Activity銷毀時,Fragment會隨之銷毀。而當Activity配置發生改變(如旋轉螢幕)時候,舊的Activity會被銷毀,然後重建一個新旋轉螢幕狀態下的Activity,自然而然的Fragment也會隨之銷毀後重建,而新產生的Fragment中的各個對象也與之前的那個Fragment不一樣,伴隨著他們的動作、事件也都不一樣。所以,這時候如果想保持原來的Fragment中的一些對象,或者想保持他們的動作不被中斷的話,就迫切的需要將原來的Fragment進行非中斷式的儲存。

2.生命週期
Activity的生命週期在配置發生改變時:

onPuase->onStop->onDestroy->onStart->onResume
比如在Activity中發生旋轉螢幕,其生命週期就是如此。而在onDestroy中,Activity會將其FragmentManager所包含的Fragment都銷毀掉(預設狀態),即Fragment的生命週期為:

onDestroyView->onDestroy->onDetach
通過查看FragmentManager.java的代碼,可以發現在Fragment生命週期執行到onDestroyView時候,狀態會由正常的ACTIVITY_CREATED變為CREATED。而到了onDestroy生命週期時候,執行的代碼出現了有意思的事情:

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; }}

當Fragment的mRetaining被置true的時候,Destroy生命週期並不會執行,而Fragment的mRetaining狀態是通過其retainNonConfig()來配置的,配置條件是Fragment不為空白且Framgnet的mRetainInstance為true。到這裡就能看到,如果想要自己的Fragment不被銷毀掉,就要讓這個mRetainInstance為true。

通過查閱Fragment.java源碼發現,通過API setRetainInstance和getRetainInstance可以對其進行操作。同樣,Android文檔中對這兩個介面也有了一定的描述。

這裡結合Fragment.java中setRetainInstance的注釋進行一下Fragment非中斷儲存的總結。原注釋如下:

/**  * 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重做時也不進行銷毀那麼就要設定setRetainInstance(true)。進行了這樣的操作後,一旦發生Activity重組現象,Fragment會跳過onDestroy直接進行onDetach(介面消失、對象還在),而Framgnet重組時候也會跳過onCreate,而onAttach和onActivityCreated還是會被調用。需要注意的是,要使用這種操作的Fragment不能加入backstack後退棧中。並且,被儲存的Fragment執行個體不會保持太久,若長時間沒有容器承載它,也會被系統回收掉的。

五、總結

很簡單的Fragment非主流用法,相比直接使用Bundle儲存資料確實是複雜了些,但是能夠更安全的進行資料轉移對應用來說還是很好的一件事。推薦指數五顆星★★★★★!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.