Android Activity的一些要點

qq54728811 8年前發布 | 5K 次閱讀 Activity Android開發 移動開發

Activity是安卓四大組件之一,也是使用最頻繁的組件,這里就來記錄下在使用Activity的過程中應該注意的一些小細節

一、Activity的生命周期

  1. "onStart與onResume"、"onPause與onStop"在描述上來看都是類似的,當Activity開始創建調用了onCreate方法后,會依次調用"onStart與onResume",當Activity切換時到另一個界面時,會依次調用"onPause與onStop"
    那么,它們之間有什么不同呢?可不可以只保留其中之一呢?
    其實,onStart與onResume的差異在于要啟動的Activity是否可見,兩者都是Activity從創建到顯示在前臺這之間被調用的,不過onStart方法被調用時Activity還在后臺,onResume被調用時表示Activity已經可見并開始活動了
    onPause與onStop在用戶切換Activity或者打開一個Dialog時調用。當用戶直接切換到一個非透明Activity時,onPause與onStop會依次調用,如果下一個Activity是透明主題,或者打開的是一個Dialog(只遮擋了一部分當前Activity的界面)時,只會調用onPause,而不會調用onResume
  2. 當前臺Activity A切換到Activity B時,兩者之間的回調函數調用順序是怎樣的呢?
    首先新建一個MainActivity,重寫其各個回調函數,添加一句Log輸出語句,類似如下所示
    private final String TAG = "MainActivity";
    @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart被調用");
    }
    再在主布局文件中添加一個按鈕,用于啟動第二個Activity——Main2Activity
    也重寫Main2Activity的各大回調函數,與MainActivity類似
    private final String TAG = "第二個Activity";
     @Override
    protected void onStart() {
        super.onStart();
        Log.e(TAG, "onStart被調用");
    }
    啟動MainActivity,輸出語句如下:
    com.czy.myapplication E/MainActivity: onCreate被調用
    com.czy.myapplication E/MainActivity: onStart被調用
    com.czy.myapplication E/MainActivity: onResume被調用
    點擊按鈕切換Activity
    com.czy.myapplication E/MainActivity: onPause被調用
    com.czy.myapplication E/第二個Activity: onCreate被調用
    com.czy.myapplication E/第二個Activity: onStart被調用
    com.czy.myapplication E/第二個Activity: onResume被調用
    com.czy.myapplication E/MainActivity: onStop被調用
    點擊back鍵回退到MainActivity
    com.czy.myapplication E/第二個Activity: onPause被調用
    com.czy.myapplication E/MainActivity: onRestart被調用
    com.czy.myapplication E/MainActivity: onStart被調用
    com.czy.myapplication E/MainActivity: onResume被調用
    com.czy.myapplication E/第二個Activity: onStop被調用
    com.czy.myapplication E/第二個Activity: onDestroy被調用
    可以看出來,當啟動新的Activity或回退到前一個Activity時,執行順序是:之前的Activity的onPause方法被調用——要切換到的Activity的onResume方法被調用——之前的Activity的onStop方法被調用
    因此,原Activity的onPause不能用于執行耗時操作,否則會影響新的Activity的顯示

二、Activity異常情況下的數據保存

  1. 當Android系統資源相關的配置發生變化(如橫豎屏切換)時,默認情況下當前Activity就會被銷毀并重新創建

    此時Activity是由于異常情況而銷毀的,所以系統會調用如下方法來保存當前數據

    protected void onSaveInstanceState(Bundle outState)

    即我們可以向Bundle中存入需要恢復的數據

    當Activity被重新創建后,以下方法會被自動調用

    protected void onRestoreInstanceState(Bundle savedInstanceState)

    該Bundle就是在onSaveInstanceState中所使用的,我們可以從中取出數據,用于恢復Activity的狀態

此外,該Bundle同時也會傳遞給

protected void onCreate(Bundle savedInstanceState)

因此我們可以選擇在兩個方法其中之一來恢復數據,一般是在onRestoreInstanceState(Bundle savedInstanceState)當中執行恢復操作比較合理

且onRestoreInstanceState方法只在Bundle不為空時才會調用,而在onCreate方法中還需要再次判斷Bundle的值

例如,如果當前Activity中有一個EditText,當屏幕發生旋轉時,可以將其文本內容存入Bundle中,當重新創建Activity時再將其從Bundle取出并顯示在EditText當中

@Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        EditText editText = (EditText) findViewById(R.id.editText);
        if (!TextUtils.isEmpty(editText.getText())) {
            outState.putString("TextContent", editText.getText().toString());
        }
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        String content = savedInstanceState.getString("TextContent", "");
        EditText editText = (EditText) findViewById(R.id.editText);
        editText.setText(content);
    }

View本身也包含有該兩個方法,某些View的數據保存與恢復操作系統已經自動為我們完成了,這也是我們旋轉屏幕重新創建Activity后某些狀態依然一樣的原因

從Activity第一次創建到旋轉屏幕,各回調函數的執行順序如下所示:

com.czy.myapplication E/MainActivity: onCreate被調用
com.czy.myapplication E/MainActivity: onStart被調用
com.czy.myapplication E/MainActivity: onResume被調用

com.czy.myapplication E/MainActivity: onPause被調用
com.czy.myapplication E/MainActivity: onSaveInstanceState被調用
com.czy.myapplication E/MainActivity: onStop被調用
com.czy.myapplication E/MainActivity: onDestroy被調用

com.czy.myapplication E/MainActivity: onCreate被調用
com.czy.myapplication E/MainActivity: onStart被調用
com.czy.myapplication E/MainActivity: onRestoreInstanceState被調用
com.czy.myapplication E/MainActivity: onResume被調用

2 . 禁止因系統配置改變而重新創建Activity

如果不想在系統配置被改變后而導致Activity重新創建,可以在Activity聲明時為之指定configChanges屬性

android:configChanges="orientation|screenSize"

這樣,當屏幕旋轉時Activity就不會重新創建也不會去進行數據保存和數據恢復操作了,此時系統會改為去調用以下方法

public void onConfigurationChanged(Configuration newConfig)

重寫之

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    Log.e(TAG, "onConfigurationChanged被調用,屏幕方向:" + newConfig.orientation);
}

這樣,當多次旋轉屏幕時,onSaveInstanceState和onRestoreInstanceState均不會被調用,調用的只是onConfigurationChanged方法,且Activity并沒有銷毀重建

com.czy.myapplication E/MainActivity: onCreate被調用
com.czy.myapplication E/MainActivity: onStart被調用
com.czy.myapplication E/MainActivity: onResume被調用
com.czy.myapplication E/MainActivity: onConfigurationChanged被調用,屏幕方向:2
com.czy.myapplication E/MainActivity: onConfigurationChanged被調用,屏幕方向:1
com.czy.myapplication E/MainActivity: onConfigurationChanged被調用,屏幕方向:2
com.czy.myapplication E/MainActivity: onConfigurationChanged被調用,屏幕方向:1

三、Activity的啟動模式

Android共有四種Activity啟動模式,分別為:standard、singleTop、singleTask、singleInstance

Activity位于任務棧當中,棧是一種“后進先出”的結構,當點擊back鍵時,棧頂的Activity會一一出棧,直到棧為空后退出程序

Activity的默認棧名為包名,可以通過taskAffinity屬性來設置,該屬性必須包含有包名分隔符“.”,而不能僅僅是一個單詞

<activity
        android:name=".AnotherActivity"
        android:launchMode="singleTask"
        android:taskAffinity="com.custom.task" />

taskAffinity屬性主要和singleTask模式和allowTaskReparenting屬性配合使用,在其他情況下沒有作用

(1) standard

此為標準模式,即Activity的默認啟動模式

在該啟動模式下,每啟動一個Activity,不管該Activity是否已存在,均會創建一個新的Activity實例,并將該Activity壓入啟動它的Acivity所在的任務棧的棧頂(singleInstance模式除外),不管該棧的棧名和新啟動的Activity在AndroidManifest中指定的taskAffinity屬性是否相同

(2) singleTop

此為棧頂復用模式

如果要啟動的Activity已位于任務棧的棧頂,則此Activity不會被重新創建

不過,此時該Activity的以下方法會被調用

protected void onNewIntent(Intent intent)

可以通過該intent來傳輸數據

例如,在啟動Activity時,向intent傳入文本信息

public void startAnotherActivity(View view) {
        Intent intent = new Intent(this, AnotherActivity.class);
        Bundle bundle = new Bundle();
        bundle.putString("Content", "要傳輸的內容");
        intent.putExtras(bundle);
        startActivity(intent);
    }

然后在onNewIntent方法中再來獲取數據

@Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.e(TAG, "onNewIntent被調用");
        Bundle bundle = intent.getExtras();
        if (bundle != null) {
            String text = bundle.getString("Content", "默認文本");
            Toast.makeText(AnotherActivity.this, text, Toast.LENGTH_SHORT).show();
        }
    }

需要注意的是:如果要啟動的Activity并不位于棧頂,則會創建該Activity實例,且onNewIntent方法不會被調用

(3) singleTask

即棧內復用模式

這是一種單實例模式,在該模式下,只要Activity存在于棧中,不管是否位于棧頂,均不會重新創建Activity實例,且系統也會回調其onNewIntent方法

如果該Activity存在但不位于棧頂,則系統會彈出該Activity之上的所有Activity,將該Activity置于棧頂

(4)singleInstance

即單實例模式

該模式與singleTask模式類似,不過該模式下的Activity只能單獨地位于一個任務棧中

例如,如果Activity A是singleInstance模式,當A第一次啟動時,系統會為之創建一個任務棧,然后將A單獨置于該棧中,除非A被銷毀了,否則之后每次啟動A,都不會重新創建實例

指定啟動模式有兩種方法:

1.在AndroidManifest中直接為Activity指定啟動模式

<activity
        android:name=".AnotherActivity"
        android:launchMode="singleTask"
        android:taskAffinity="com.custom.task" />

2.在Intent中設置標志位Flags來為Activity指定啟動模式

Intent intent = new Intent(this, AnotherActivity.class);
    intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
    startActivity(intent);

在優先級上,第二種方式的優先級要高于第一種,如果兩者同時存在,則以第二種為準

兩者在限定范圍上有所差別,第一種方式無法設定“Intent.FLAG_ACTIVITY_CLEAR_TOP”模式,第二種方式無法設定“singleInstance”模式

 

來自:http://www.jianshu.com/p/872a87fc5734

 

 本文由用戶 qq54728811 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!