Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發藝術探索學習筆記(一)—Activity的生命周期和啟動模式

Android開發藝術探索學習筆記(一)—Activity的生命周期和啟動模式

日期:2017/2/22 17:39:10      編輯:關於Android編程

學習章節:
第一章 Activity的生命周期和啟動模式

學習內容:
1正常情況下Activity的生命周期分析

先上一張經典圖片鎮樓:

這裡寫圖片描述

測試正常情況Activity生命周期的代碼:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.e("tag", "生命周期:onCreate");
    }


    @Override
    protected void onStart() {
        super.onStart();
        Log.e("tag", "生命周期:onStart");
    }


    @Override
    protected void onResume() {
        super.onResume();
        Log.e("tag", "生命周期:onResume");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.e("tag", "生命周期:onRestart");
    }


    @Override
    protected void onPause() {
        super.onPause();
        Log.e("tag", "生命周期:onPause");
    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.e("tag", "生命周期:onStop");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("tag", "生命周期:onDestroy");
    }
}

測試結果:

第一次啟動Activity,生命周期回調如下:
這裡寫圖片描述

這時候將應用切換到後台或打開一個新的Activity,生命周期回調如下:
這裡寫圖片描述

然後再次打開這個應用,生命周期回調如下:
這裡寫圖片描述

最後按返回鍵退出這個應用,生命周期回調如下:
這裡寫圖片描述

假如應用已經被切換到後台,這時直接結束所有進程,生命周期回調如下:
這裡寫圖片描述

從整個生命周期來說,onCreate和onDestroy是配對的,分別標識著Activity的創建和銷毀,並且只可能有一次被調用。從Activity是否可見來說,onStart和onStop是配對的,隨著用戶的操作或者設備屏幕的點亮和熄滅,這兩個方法可能被調用多次;從Activity是否在前台來說,onResume和onPause是配對的,隨著用戶的操作或者設備屏幕的點亮和熄滅,這兩個方法可能被調用多次。

假設當前Activity為A,如果這時用戶打開一個新ActivityB,那麼B的onResume和A的onPause哪個先執行???

測試代碼:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> private void initView() { btn = (Button) findViewById(R.id.btn); btn.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { startActivity(new Intent(MainActivity.this, SecondActivity.class)); } }); }

測試結果:
這裡寫圖片描述

可以看到,新啟動一個Activity的時候,舊Activity的onPause會先執行,然後才會啟動新的Activity。通過分析這個問題,我們知道不能在onPause中做重量級的操作,因為必須onPause執行完成以後新Activity才能Resume。

2異常情況下Activity的生命周期分析

2.1資源相關的系統配置發生改變導致Activity的生命周期的改變

最常見的一種情況就是屏幕旋轉導致的Activity的生命周期的改變,網上關於Android橫豎屏切換生命周期的資料一大把,照著這些資料高高興興的敲著代碼,尼瑪卻發現坑爹呀!!!

代碼與結果才是最有說服力,開始舉栗子:

Activity的配置文件不設置任何參數,從豎屏切換到橫屏:
這裡寫圖片描述

調用了一次完整的生命周期

Activity的配置文件不設置任何參數,從橫屏切換到豎屏:
這裡寫圖片描述

調用了一次完整的生命周期,並沒有調用兩次!!!

Activity的配置文件設置:android:configChanges=”orientation”,從豎屏切換到橫屏或者從橫屏切換到豎屏:
這裡寫圖片描述

調用了一次完整的生命周期,也就是說,設置這個參數沒有任何作用!!!

Activity的配置文件:android:configChanges=”orientation"keyboardHidden,從豎屏切換到橫屏或者從橫屏切換到豎屏:
這裡寫圖片描述

調用了一次完整的生命周期,也就是說,額外設置這個參數沒有任何作用,這不坑爹嘛!說好的不調用生命周期呢???

後來才知道,當android:targetSdkVersion<=12,不會調用完整生命周期;當android:targetSdkVersion>12,會調用完整生命周期。我的 targetSdkVersion是23,嗦嘎寺內。

那麼我該怎樣設置才能在targetSdkVersion大於12的時候,橫豎屏切換不調用生命周期呢:

android:configChanges="orientation|keyboardHidden|screenSize"

這樣就夠了,看看打印結果,沒有打印與生命周期相關的log,只是調用了系統的onConfigurationChanged方法,這時候我們可以自己做一些處理,完美。
這裡寫圖片描述

當然,你要是這樣設置

android:screenOrientation="portrait"

這就另當別論,只允許豎屏,管你怎麼切換都沒用,就不談生命周期的調用了。

OK,我們已經知道這種系統配置發生變化的情況會調用Activity的完整生命周期。這種Activity被異常中止的情況下,系統會調用onSaveInstanceState來保存當前Activity的狀態,正常情況下系統不會回調這個方法。 繼續舉例子:

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Log.e("tag", "A生命周期:onCreate");
        initView();
        if (savedInstanceState != null) {
            Log.e("tag", "savedInstanceState保存的值為:" + savedInstanceState.get("data"));
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.e("tag", "onSaveInstanceState");
        outState.putString("data", "我是需要保存的值");
    }

    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        Log.e("tag", "savedInstanceState保存的值為:" + savedInstanceState.get("data"));
    }

還是這段代碼,當我第一次正常啟動時,沒有什麼不同:
這裡寫圖片描述

當我切換到橫屏時,高潮來了:
這裡寫圖片描述

果然和預期的一樣,這種異常情況調用了onSaveInstanceState方法,比如我們需要在這種異常情況下保存一些必要的參數,像播放視頻時橫豎屏切換,我一定得保存當前的播放進度等信息。那麼我們可以把這些參數以鍵值對的形式保存在onSaveInstanceState方法中。怎樣恢復這些數據呢,恢復數據的位置有兩種:onRestoreInstanceState和onCreate,區別如下:

onRestoreInstanceState一旦被調用,其參數Bundle savedInstanceState一定是有值的,我們不用額外判斷是否為空
onCreate如果正常啟動的話,其參數Bundle savedInstanceState為空,我們需要額外判斷是否為空

2.2資源內存不足導致優先級的Activity被殺死

Activity按照優先級從高到低,可以分為以下三種:
(1)前台Activity:正在和用戶交互的Activity,優先級最高;
(2)可見但非前台Activity:比如Activity中彈出了一個對話框,導致Activity可見但是位於後台無法和用戶直接交互;
(3)後台Activity:已經被暫停的Activity,比如執行了onStop,優先級最低

系統會按照上述優先級去殺死目標Activity所在的進程,並通過onSaveInstanceState與onRestoreInstanceState方法來存儲和恢復數據。一些後台工作不適合脫離四大組件而獨自運行在後台中,這樣很容易被殺死。比較好的做法是將後台工作放入Service中從而保證進程有一定的優先級,這樣不會輕易地被系統殺死。

3Fragment的生命周期分析:

先上經典圖片:

這裡寫圖片描述

測試情況,初始化第一個Fragment:
這裡寫圖片描述

然後將當前Fragment進行remove操作,並加入回退棧:
這裡寫圖片描述

返回到第一個Fragment:
這裡寫圖片描述

退出當前Fragment:
這裡寫圖片描述

測試結果一目了然。更好的理解與掌握Activity與Fragment的生命周期對以後開發有很大作用!!!

4 Activity的啟動模式
任務棧:任務棧Task:一種以棧的形式來存放Activity實例的容器,原則是“後進先出”,主要有兩個操作:壓棧和出棧。其中存放的Activity是不支持重新排序的,只能根據壓棧和出棧操作更改Activity的順序。是為實現一個功能而負責管理所有用到的Activity實例的棧。默認情況下,所有Activity所需的任務棧名字為應用的包名。

啟動一個Application的時候,系統會為它默認創建一個對應的Task,用來放置根Activity。默認情況下,啟動Activity會放在同一個Task中,新啟動的Activity會被壓入啟動它的那個Activity的棧中,並且顯示新啟動的Activity。當用戶按下回退鍵時,這個Activity就會被彈出棧;當按下Home鍵回到桌面,再啟動另一個應用,這時候之前那個Task就被移到後台,成為後台任務棧,而剛啟動的那個Task就被調到前台,成為前台任務棧,Android系統顯示的就是前台任務棧中的Top實例Activity。

啟動模式: 通過設置啟動模式來修改系統的默認行為

standard:標准模式,可以不用寫配置,這也是系統的默認模式。每次啟動一個Activity都會創建一個新的實例,不管這個實例是否已經存在。
應用場景:絕大多數Activity。

singleTop:棧頂復用模式,在這中模式下,如果新Activity已經位於任務棧的棧頂,那麼此Activity不會被重新創建,同時它的onNextIntent方法會被回調,通過此方法的參數我們可以取出當前請求的信息。需要注意的是,這個Activity的onCreate和onStart不會被系統調用,因為它並沒有發生改變。
應用場景:假設目前棧內的情況為ABC,其中ABC為三個Activity,A位於棧底,C位於 棧頂。這個時候再次啟動C,如果C的啟動模式為singleTop,那麼棧內的情況依然為ABC;如果C的啟動模式為standard,那麼由於C被創建,導致棧內的情況就變為ABCC。

singleTask:棧內復用模式, activity只會在任務棧裡面存在一個實例。如果要激活的activity,在任務棧裡面已經存在,就不會創建新的activity,而是復用這個已經存在的activity,調用 onNewIntent() 方法,並且清空這個activity任務棧上面所有的activity。
應用場景:大多數App的主頁。對於大部分應用,當我們在主界面點擊回退按鈕的時候都是退出應用,那麼當我們第一次進入主界面之後,主界面位於棧底,以後不管我們打開了多少個Activity,只要我們再次回到主界面,都應該使用將主界面Activity上所有的Activity移除的方式來讓主界面Activity處於棧頂,而不是往棧頂新加一個主界面Activity的實例,通過這種方式能夠保證退出應用時所有的Activity都能報銷毀。

測試例子:首先從主界面A跳轉到第二個界面B,再從第二個界面B跳轉到第三個界面C,最後從第三個界面C跳轉到主界面A。

  main.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        second.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(getApplicationContext(), ThirdActivity.class));
            }
        });
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        third.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(getApplicationContext(), MainActivity.class));
            }
        });

設置主界面A的啟動模式為,其他兩個是默認啟動模式

android:launchMode="singleTask"

我們監聽一下最後從第三個界面C跳轉到主界面A發生了什麼:
這裡寫圖片描述

第二個界面B首先執行了onDestroy,然後第三個界面C也執行了onDestroy,也就是說兩個Activity的實例都被銷毀了,是不是這樣呢?我們這時候在主界面按返回鍵,看看發生了什麼:
這裡寫圖片描述

果然,主界面A實例銷毀,直接退出了應用,與預期效果一致。

singleInstance:單一實例模式,整個手機操作系統裡面只有一個實例存在。不同的應用去打開這個activity 共享公用的同一個activity。他會運行在自己單獨,獨立的任務棧裡面,並且任務棧裡面只有他一個實例存在。
應用場景:呼叫來電界面。這種模式的使用情況比較罕見,在Launcher中可能使用。或者你確定你需要使Activity只有一個實例。建議謹慎使用。

1.3 Activity的Flags

系統提供了兩種方式來設置一個Activity的啟動模式,除了在AndroidManifest文件中設置以外,還可以通過Intent的Flag來設置一個Activity的啟動模式,下面我們在簡單介紹下一些Flag。

FLAG_ACTIVITY_NEW_TASK

使用一個新的Task來啟動一個Activity,但啟動的每個Activity都講在一個新的Task中。該Flag通常使用在從Service中啟動Activity的場景,由於Service中並不存在Activity棧,所以使用該Flag來創建一個新的Activity棧,並創建新的Activity實例。

FLAG_ACTIVITY_SINGLE_TOP

使用singletop模式啟動一個Activity,與指定android:launchMode=“singleTop”效果相同。

FLAG_ACTIVITY_CLEAR_TOP

使用SingleTask模式來啟動一個Activity,與指定android:launchMode=“singleTask”效果相同。

FLAG_ACTIVITY_NO_HISTORY

Activity使用這種模式啟動Activity,當該Activity啟動其他Activity後,該Activity就消失了,不會保留在Activity棧中。

測試例子:

還是上面一樣的場景,這次我們不在配置文件設置,將從第三個界面C跳轉到主界面A的代碼設置如下:

third = (Button) findViewById(R.id.third_btn);
        third.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent = new Intent();
                intent.setClass(ThirdActivity.this, MainActivity.class);
                intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
                startActivity(intent);
            }
        });

看看發生了什麼:
這裡寫圖片描述

然後按下返回鍵:
這裡寫圖片描述

與預期效果一致,完美。

5 IntentFilter的匹配原則

只有一個Intent同時匹配action類別,category類別,date類別才算完全匹配,只有完全匹配才能成功啟動目標Activity。一個Activity中可以有多個intent-filter,一個Intent只要能匹配任何一組intent-filter即可成功啟動對應的Activity。

action的匹配規則:
action的匹配要求Intent中的action存在且必須和過濾規則中的其中一個action相同,action區分大小寫,大小寫不同字符串相同的action會匹配失敗。

category的匹配規則:
Intent如果含有category,那麼所有的category都必須和過濾規則中的其中一個category相同,如果沒有category依舊可以匹配成功。

data的匹配規則:
與action類似,如果過濾規則中定義了data,那麼Intent中必須也要定義可匹配的data。

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