Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Activity的聲明周期和啟動模式

Activity的聲明周期和啟動模式

編輯:關於Android編程

正常情況下Activity的聲明周期

先來看看官方給出的聲明周期圖:
這裡寫圖片描述
這裡需要說明如下幾點:

針對特定Activity,第一次啟動,回調如下:onCreate->onStart->onResume 當用戶打開新的activity或者切換到桌面的時候,會回調onPause->onStop. 如果新的activity采用了透明的主題,那麼當前activity不會回調onStop 當用戶再次回到原activity時候,會回調如下:onRestart->onStart->onResume 當用戶按下back鍵回退的時候,回調如下:onPause->onStop->onDestroy 當兩個activity之間切換的時候,會先調用舊activity的onPause,然後新activity才啟動。所以我們不能再onPause中做重量級操作,因為舊的activity的onPause先執行完成,新activity才能resume

異常情況下activity的生命周期

很多時候我們的activity可能會由於系統資源的緊缺被系統殺死,或者系統配置發生變化等

系統配置發生改變導致activity被殺死並重新創建

如果當前activity從橫屏直接旋轉到豎屏,此時由於系統配置發生了變化,在默認情況下,activity就會被銷毀並重新創建。

當系統配置發生變化之後,activity會被銷毀,其onPause,onStop,onDestroy均會被回調,同時由於activity是在異常情況下終止的,系統會回調onSaveInstanceState來保存當前activity的狀態。onSaveInstanceState是在onStop之前調用的,onRestoreInstanceState是在onStart之後調用的

我們來看下面的栗子:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // 這裡當activity正常啟動的時候,savedInstanceState是null,所以需要做非空判斷
        if (null != savedInstanceState) {
            String str = savedInstanceState.getString("killedStr");
            Log.d(TAG,"onCreate runs...str is :"+str);
        }
    }

    /**
     * 當activity被銷毀,並重新創建時候,回調該方法獲取之前保存的數據
     */
    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        Log.d(TAG,"onRestoreInstanceState runs...");
        String str = savedInstanceState.getString("killedStr");
        savedInstanceState.putString("killedStr","testString");
        Log.d(TAG,"onRestoreInstanceState runs...str is :"+str);
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d(TAG,"onSaveInstanceState runs...");

        outState.putString("killedStr","testString");
    }

}

可以看到,我們可以同時在onRestoreInstanceState和onCreate中獲取之前保存的數據,區別在於在onRestoreInstanceState方法中,其參數一定不是null,而在onCreate中,如果當前activity正常啟動的時候,其參數為null

onSaveInstanceState方法,系統只會在Activity即將被銷毀,並且有機會重新顯示的情況下才會去調用它

資源內存不足導致activity被殺死

通常情況下我們的activity可以分為下面三種優先級:

前台activity,表示正在和用戶進行交互的activity 可見但非前台activity。 後台activity
到現在為止,我們知道了,當系統配置發生更改以後,activity會重新創建,我們可以給activity指定configChanges屬性,來防止activity重新創建。常用的有locale,orientation,keyboardHidden.

需要注意到的是,在sdkversion > 13時候,為了防止旋轉屏幕activity重啟,除了orientation還需要添加screenSize

activity的啟動模式

activity的LaunchMode

activity有四種啟動模式:

standard
標准模式,也是系統默認模式,每次啟動一個activity都會重新創建一個新的實例,不管該實例是否已經存在,在該模式下,誰啟動了和這個activity,那麼這個activity就運行在啟動它的那個activity所在的任務棧中。比如我們在service中啟動標准模式的activity,就會遇到下面這種錯誤。 singleTop
在這種模式下,如果新activity已經位於任務棧的棧頂,那麼此activity不會被重新創建,同時它的onNewIntent會被回調,該activity的onCreate,onStart,不會被系統調用。如果新的activity實例已經存在但不是位於棧頂,那麼新的activity仍然會重新創建。
比如下面的activity啟動過程:
這裡寫圖片描述vcfrx/PG9Lavo6zG5Mv50OjSqrXEyM7O8dW7zqpTMqOs08nT2lMyus1EtcTKtcD9vvmyu7Tm1NqjrMv50tTPtc2zu+HPyLS0vajIzs7x1btTMqOsyLu687S0vahEtcTKtcD9sqK9q8bkt8XI61My1tChozxiciAvPg0KPGltZyBhbHQ9"這裡寫圖片描述" src="/uploadfile/Collfiles/20161006/20161006103127505.png" title="\" /> 假設D所需要的任務棧是S1,其他情況和上面相同,由於S1已經存在,所以系統會直接創建D,並將其放入到棧S1 如果D所需要的任務棧為S1,並且當前任務棧S1的情況為ADBC根據棧內服用原則,此時D不會重新創建,系統會把D切換到棧頂並調用其onNewIntent方法,同時由於singleTask默認具有clearTop效果,會導致棧內所有D上面的activity全部出出棧,最小S1中的情況為AD.
- singleInstance
單實例模式,該模式除了具有singleTask模式的所有特性之外,另外需要注意的是,具有該種模式的activity只能夠單獨位於一個任務棧中。

TaskAffinity

默認情況下,所有的activity所需要的任務棧的名字為當前的包名,我們可以單獨為每個activity指定TaskAffinity屬性,TaskAffinity屬性主要和singleTask啟動模式或者allowTaskReparenting配對使用。

當TaskAffinity和singleTask啟動模式配對使用的時候,此時待啟動的singleTask模式的activity所在的任務棧,和TaskAffinity是相同的。 當TaskAffinity和allowTaskReparenting結合使用的時候,當一個應用A啟動了應用B的某一個activity,如果B的allowTaskReparenting屬性為true,那麼當應用B被啟動以後,此activity會直接從應用A的任務棧轉移到應用B的任務棧中。

為activity指定啟動模式

我們可以分別在代碼中或者AndroidManifest.xml裡為activity指定啟動模式。

// 在AndroidManifest.xml中指定
android:launchMode="singleTask"

// 在代碼中指定,優先級較高
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

singleTask實栗

為MainActivity添加singleTask
// 為MainActivity指定啟動模式為singleTask
 


@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.id_text).setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View arg0) {
                Intent intent = new Intent(MainActivity.this,MainActivity.class);
                MainActivity.this.startActivity(intent);
            }
        });
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        Log.d(TAG,"onNewIntent runs....");
    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG,"onPause runs....");
    }

    @Override
    protected void onResume() {
        super.onResume();
        Log.d(TAG,"onResume runs....");
    }

此時我們連續點擊三次,啟動當前MainActivity。
這裡寫圖片描述
此時由於我們指定當前MainActivity的啟動模式為singleTask,所以在默認的棧中,只有一個MainActivity實例,所以此時我們雖然啟動了三次,但是按下back鍵盤的時候,會直接返回到桌面。
通過adb shell dumpsys activity 來查看當前的activity和其任務棧
這裡寫圖片描述

為MainActivity去掉singleTask

現在我們為MainActivity去掉singleTask,做相同的操作。
這裡寫圖片描述

Activity的Flags

FLAG_ACTIVITY_NEW_TASK
這個標記位的作用是為Activity指定”singleTask”啟動模式。 FLAG_ACTIVITY_SINGLE_TOP
這個標記位的作用是為Activity指定”singleTop”啟動模式。 FLAG_ACTIVITY_CLEAR_TOP
當啟動具有該標記為的activity,在同一個任務棧中,所有位於它上面的Activity都要出棧,該模式一般需要和 FLAG_ACTIVITY_NEW_TASK配合使用,在這種情況被啟動activity如果已經存在,那麼系統就會調用它的onNewIntent,如果被啟動的activity采用standard模式啟動,那麼連同他之上的activity都要出棧,系統會創建activity實例,並放入到棧頂。

IntentFilter的匹配規則

我們都知道,隱式啟動一個activity時候,就需要匹配當前activity的IntentFilter設置,如果匹配不成功,將無法啟動目的activity,IntentFilter中設置的過濾信息有action,category,data。
為了匹配過濾列表,需要同時匹配action,category,data信息,否則會匹配失敗,只有一個intent同時匹配action類別,category類別,data類別才算完全匹配,另外一個activity可能有多個intent-filter,一個intent只要能夠匹配任何一組intent-filter即可以成功啟動對應的activity

action的匹配規則

一個過濾規則中可以有多個action,只要intent中的action只要能夠和其中的一個action相同即可匹配成功。action的匹配而規則要求intent中的action存在且必須和過濾規則中的一個action相同。

category的匹配規則

我們的代碼中可以沒有category,但是XML中要加上”android.intent.category.DEFAULT”這句。

如果你在代碼中定義了一個或者多個category,那麼你必須跟XML文件中定義的一樣。比如你定義了一個category,那麼要在XML文件中匹配到一個,,如果你定義了多個category,那麼要在XML文件中全部匹配

data的匹配規則

data的匹配規則和action類似,它也要intent中必須含有data數據,並且data數據能夠完全匹配過濾規則中的某一個data,這裡的完全匹配是指過濾規則中出現的data部分也出現在了Intent的data中。

如果要為intent指定完整的data,必須要調用setDataAndType方法,因為setData和setType方法中,會彼此清除掉對方設置的值

對intent的匹配結果做判斷

如果我們使用intent匹配了一個activity,並且會啟動當前的activity,那麼如果當前的activity不存在即匹配失敗的話,就會出現ActivityNotFoundException這樣的錯誤。

我們可以采用PackageManager的resolveActivity或者intent的resolveActivity方法,如果找不到匹配的activity,就會返回null,我們通過返回值可以規避上面的錯誤。

另外PackageManager還提供了queryIntentActivities方法,這個方法和resolveActivity不同之處在於:它返回的不是最佳匹配的activity,而是所有成功匹配的activity。

public abstract List queryIntentActivities(Intent intent,int flags);

public abstract ResolveInfo resolveActivity(Intent intent,int flags);

上面兩個方法第二個參數,使用時候需要注意,我們需要使用MATCH_DEFAULT_ONLY這個標記為,該標記為的含義是僅僅匹配在intent-filter中聲明了

這樣的activity。這樣做的好處在於只要上面兩個方法返回值不是null,則startActivity一定可以成功。

ok,今天就到這裡了,本篇博客來自android開發藝術探索的學習筆記。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved