天天看點

Pro Android學習筆記(五六):配置變化

橫屏豎屏的切換就是最為常見的配置變化,我們在Pro Android學習筆記(四一):Fragment(6):資料保留中讨論過。 配置變化還有裝置連接配接了dock,改變語言等,在資源res/中,不同的配置有不同的字尾來進行最佳比對,具體見Pro Android學習筆記(四):了解Android資源(下)的資源和配置的變更 ,我們從中可以看到Android檢測哪些配置變化。

當配置變化時,目前的activity被destroy,并新re-created一個activity對象,我們要確定使用者體驗的無縫切換。我們應該盡量避免re-create的時間,減少activity處理的非UI部分。在配置變化是activity被destroy,但是application仍然存在,背景線程,資料庫、content provider也存在,是以盡可能将資料和業務邏輯放在activity之外。

Activity的destory/create過程

配置變化過程中,Activity經曆destroy,重新create的過程,有三個回調函數需要關注。

onSaveInstanceState(Bundle outState):當檢測到配置變化時會觸發此回調函數,在activity的onStop()之前被調用,在此通過Bundle儲存資料,并傳遞到新建立activity的onCreate(Bundle savedInstanceState)和onRestoreInstanceState(Bundle savedInstanceState)的參數中。對于帶有android:id的view,系統在onSaveInstanceState()中或自動存儲使用者輸入的内容,并在重建時恢複。是以,如果我們需要重寫,要記住調用super.onSaveInstanceState(); 通過bundle儲存資料,例如整數使用putInt()方法,對于組合對象,可以采用putParcelable()

onCreate()和onRestoreInstanceState()都可以通過bundle擷取資訊。一般會在onCreate()中處理,因為那裡進行UI初始化的處理。但是有時我們會extends activity,這是在onRestoreInstanceState()中處理有時會更為簡單。相關的執行順序為onCreate() –>onStart() –>onRestoreInstanceState() –> onResume()。

如果我們通過bundle進行資料保留,要注意,這種情況下,舊的activity是不能進行垃圾回收的,存在記憶體洩漏的風險,是以我們應當避免将activity上下文相關的Drawables、Views、Adapters等對象放入bundle中。如果實作需要,我們可以将之放在activity之外,或者使用某些id索引(int這類不是保持對象,而是指派)。

Fragment的destory/create過程

onSaveInstanceState():

Fragment和activity一樣,可以通過onSaveInstanceState()進行資料儲存,可以通過onInflate()、onCreate()、onCreateView()和onActivityCreate()進行擷取。Android隻保證在onDestroy()之前調用onSaveInstanceState(),不保證具體的執行順序,是以有可能在調用onSaveInstanceState()時,相關的view容器已經無效,也就是,不應該在onSaveInstanceState()中處理view的資料。有例如,如果fragment在back stack,由于不可視,沒有view(即為null),這并非異常,在代碼中需要注意。同樣,不應将fragment重建立後已不存在的對象進行保留,bundle攜帶的資料應盡可能地少,減少記憶體洩漏的風險。

saveFragmentInstanceState():

如果确實需要進行view的處理,可以通過fragment管理器主導要求觸發onSaveInstanceState(),如下:

getFragmentManager().saveFragmentInstanceState(this);

saveFragmentInstanceState()将傳回Fragment.SavedState對象,這state對象中包含fragment的狀态(含有在onSaveInstanceState中儲存的bundle),通過setInitialSavedState()可以在新的fragment恢複。我們利用Dialog中的提示框的例子,對例子進行少許修改,實作在下次彈出提示框時,能保留上次的輸入值。

public class PromptDialogFragment extends DialogFragment implements OnClickListener{ 

    … …

    private static Fragment.SavedState fgState = null;  //可以儲存在Application的某個對象,為了示範友善,本例子采用靜态函數

    public static PromptDialogFragment newInstance(String prompt){

        PromptDialogFragment pdf = new PromptDialogFragment();

        pdf.setInitialSavedState(fgState);  //擷取儲存的狀态 

        ... ... 

        return pdf;

    } 

    @Override //如果fgState非null,那麼就可以從得到Bundle資料。在原例子中,新彈框是建立的dialog fragment,和原對話框無關,也不屬于配置改變,是以bundle為null。在新例子中,由于我們儲存了上一次對話框的state,并在執行個體建立時恢複,是以可以擷取原對話框的state,包括當中的bundle

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 

        ... ...

        if(savedInstanceState != null){

            CharSequence text = savedInstanceState.getCharSequence("input");

            et.setText(text == null ? "" : text);

        }

        //将原來dismiss的button改為觸發資訊儲存

        Button dismissBtn = (Button)v.findViewById(R.id.button_dismiss);

        dismissBtn.setOnClickListener(this);

        dismissBtn.setText("儲存資訊");  

        ......

    } 

    @Override 

    public void onSaveInstanceState(Bundle outState) {

        showInfo("onSaveInstanceState() is called");

        outState.putCharSequence("input", et.getText());

        super.onSaveInstanceState(outState);

    }

    @Override

    public void onClick(View v) { 

        //saveFragmentInstanceState()将觸發onSaveInstanceState()回調函數,進行資訊儲存,并将儲存内容存放在fgState對象中

        switch(v.getId()){

        case R.id.button_dismiss:  //新例子為“資訊儲存”觸發按鈕

            fgState = getFragmentManager().saveFragmentInstanceState(this);

            break; 

        ... ...

        }

    } 

}

onRetainInstance():

通過onRetainInstatnce(true),當activity被destroy和re-created時,fragment的onDestroy()并不會被調用,說明fragment對象仍然存在于app中,在新activity建立時,fragment對象将attach到新的activity中,這個過程無需調用onCreate(),因為并重新創新fragment。我們在原對話框的例子進行測試,在onCreate()中加入onRetainInstatnce(true),進行橫豎屏切換,狀态跟蹤如下。

Pro Android學習筆記(五六):配置變化

相關連結: 我的Android開發相關文章