好久以前看過的一篇文章,寫的很好。轉載並譯於:https://inthecheesefactory.co...。java
幾個月之前,我發佈了一篇關於Fragment狀態保存和恢復的文章,那多是目前爲止最好的方式用於保存/恢復 Android Fragment 的狀態。我收到了不少來自世界各地的Android開發者有價值的反饋。十分感謝大家 =)android
不管如何,StatedFragment
打破了設計模式,我使用了不一樣於Android狀態保存/恢復的方式來設計它,這樣作的目的是爲了讓Android開發者可以更簡單的理解Fragment狀態的保存與恢復,就像Activity作的同樣(同時處理View狀態和實例狀態),因此我經過開發StatedFragment
作了一個實驗,而且看看它是怎麼作的,以及StatedFragment這樣的設計是否更容易被理解?是否對開發者更加友好?git
如今,兩個月的實驗過去了,我相信我已經獲得告終果。雖然StatedFragment
有點容易被理解,可是它同時帶來了很大的問題。它破壞了Android View的基本框架.因此我認爲這是很糟糕的,可能會致使長遠的影響。事實上我已經對本身的代碼感到擔心了...github
因爲這個緣由,我決定從如今開始棄用StatedFragment
。而且,爲了彌補我錯誤,我寫了這篇文章,用可見的方式來展現基於Android的設計如何保存和恢復Fragment的狀態的最佳實踐。設計模式
當Activity的onSaveInstanceState
被調用的時候,Activity將會從View 層次(View Hierachy)中的每個View中自動蒐集View的狀態。請注意,只會蒐集實現了View狀態保存/恢復的內部方法的View的數據。一旦onRestoreInstanceState被調用,Activity將會將這些蒐集到的數據一對一的返還給View 層次裏在蒐集的時候提供了一樣的android:id
屬性的View。框架
讓咱們看看視覺上的效果。ide
這就是爲何儘管Activity已經被銷燬,而咱們並無作一些特別的事情來保存狀態,可是EditText中鍵入的文本仍然可以呈現的緣由。這並非什麼魔法,這些View 的狀態已經被自動的保存和恢復回來了。this
這也是爲何View 在沒有被設置android:id
屬性的時候不能保存和恢復本身的狀態的緣由。spa
儘管這些View 的狀態被自動的保存了,可是Activity的成員變量並不會有一樣的效果。這些成員變量會被和Activity一塊兒銷燬。你能夠手動的保存和恢復它們,經過onSaveInstanceState
和onRestoreInstanceState
方法。設計
public class MainActivity extends AppCompatActivity { // These variable are destroyed along with Activity private int someVarA; private String someVarB; ... @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("someVarA", someVarA); outState.putString("someVarB", someVarB); } @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); someVarA = savedInstanceState.getInt("someVarA"); someVarB = savedInstanceState.getString("someVarB"); } }
這就是爲了恢復Activity實例的狀態和View 狀態須要作的。
若是Fragment被系統銷燬,全部事情都會發生的像Activity發生的那樣。
這意味着每個單獨的成員變量被銷燬了。你必須分別地經過onSaveInstanceState
和onActivityCreated
方法,手動的保存和恢復這些變量。
請注意在Fragment裏面沒有onRestoreInstanceState
方法存在。
public class MainFragment extends Fragment { // These variable are destroyed along with Activity private int someVarA; private String someVarB; ... @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putInt("someVarA", someVarA); outState.putString("someVarB", someVarB); } @Override public void onActivityCreated(@Nullable Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); someVarA = savedInstanceState.getInt("someVarA"); someVarB = savedInstanceState.getString("someVarB"); } }
對於Fragment來講,有一些特殊狀況不一樣於Activity,我以爲你須要知道這些狀況。一旦Fragment從後退棧中返回,它的View 會被銷燬,並從新建立
在這種狀況下,Fragment並不會被銷燬,只有Fragment中的View 會被銷燬。 結果是,並不會發生任何實例狀態的保存。可是上面展現的這些View在Fragment生命週期中被從新建立時,發生了什麼?
別驚訝,由於Android是這樣設計的。在這種狀況下,Fragment中的View 狀態的保存/恢復會被內部調用。結果就是,每個實現了內部View 狀態保存/恢復的View ,將會被自動的保存而且恢復狀態,例如帶有android:freezeText="true"
屬性的EditText
或者TextView
。就以前完美顯示的同樣。
請注意,在這種狀況下,只有View 被銷燬(並重建)了。Fragment仍然在這裏,就像它內部的成員變量同樣。因此你不須要對它們作任何事情。不須要任何額外的代碼。
public class MainFragment extends Fragment { // These variable still persist in this case private int someVarA; private String someVarB; ... }
你也許已經注意到了,若是Fragment中的每個View 都在內部實現了View 的狀態的保存和恢復.在這種狀況下,你就沒有必要作任何事情,View 狀態會被自動的恢復,而且Fragment中的成員變量也和以前的同樣。
因此Fragment狀態保存/恢復最佳實踐的第一條件就是...
Android內部經過onSaveInstanceState
和 onRestoreInstanceState
方法提供了保存和恢復View 狀態的機制。開發者的任務就是實現它。
public class CustomView extends View { ... @Override public Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); // Save current View's state here return bundle; } @Override public void onRestoreInstanceState(Parcelable state) { super.onRestoreInstanceState(state); // Restore View's state here } ... }
基本上來講,每個單獨的Android提供的的標準View 組件都已經在內部完成了這些事情,例如EditText
,TextView
,Checkbox
.
儘管也許你手動讓它生效,例如,你須要爲TextView
設置android:freezeText
爲true,來使用這個功能。
可是若是咱們討論關於互聯網上貢獻的第三方的自定義組件。我必須說,它們大多數都沒有實現這部分的代碼,在使用中會致使很大的問題。
若是你決定使用第三方的自定義組件,你不得不確保它已經在內部實現了View 的保存和恢復,不然你必須建立繼承這個三方組件的一個派生的子類,而且本身實現onSaveInstanceState
和onRestoreInstanceState
方法。
// // Assumes that SomeSmartButton is a 3rd Party view that // View State Saving/Restoring are not implemented internally // public class SomeBetterSmartButton extends SomeSmartButton { ... @Override public Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); // Save current View's state here return bundle; } @Override public void onRestoreInstanceState(Parcelable state) { super.onRestoreInstanceState(state); // Restore View's state here } ... }
而且,若是你建立你本身的自定義View或者自定義ViewGroup,也不要忘記實現這兩個方法。應用中每個類型的View實現這部分都是很重要的。
而且不要忘記爲每個你須要開啓View狀態保存和恢復的View設置android:id
屬性,否則它們的狀態不能正確的恢復。
<EditText android:id="@+id/editText1" android:layout_width="match_parent" android:layout_height="wrap_content" /> <EditText android:id="@+id/editText2" android:layout_width="match_parent" android:layout_height="wrap_content" /> <CheckBox android:id="@+id/cbAgree" android:text="I agree" android:layout_width="wrap_content" android:layout_height="wrap_content" />
這篇文章已經進行到了一半!
爲了使你的代碼變得乾淨和可擴展,你最好把Fragment狀態和View狀態分開處理。若是這裏有任何屬性是屬於View的,在View內部進行保存和恢復.若是這裏有任何屬性是屬於Fragment的,在Fragment內部進行保存和恢復。這裏有一個例子。
public class MainFragment extends Fragment { ... private String dataGotFromServer; @Override public void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("dataGotFromServer", dataGotFromServer); } @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); dataGotFromServer = savedInstanceState.getString("dataGotFromServer"); } ... }
讓我再重複一遍。不要在Fragment中的 onSaveInstanceState
中保存View狀態,反之亦然。
這就是全部的內容了。這是如何保存和恢復Activity,Fragment和View狀態的最佳實踐。但願你在這篇文章中找到一些有用的信息=)
請按照上面所描述的方法來保存恢復Activity,Fragment,View的狀態。從如今開始,讓我把StatedFragment
廢棄掉吧。
還有,StatedFragment中獲取onActivityResult的特性在NestedFragment中仍然是可使用的。爲了不將來的混淆,我決定把這個功能分離到一個NestedActivityResultFragment
這個新的類裏面,在v0.10.0以及後續的版本可用。
更多的關於它的信息參見 https://github.com/nuuneoi/St...,請隨時查看。
但願這篇文章中這種動態圖片的方式可以幫助大家清楚的理解Activity,Fragment,View狀態的恢復。對於前一篇文章的致使的混淆,我感到十分抱歉。^^"