Activity的生命周期

在現在以及以后,我都會把知識點進行整理,為什么呢?雖然不整理也可以,用的時候去百度,百度?呵呵了吧大家,太菜了吧,對,我菜。寫文章的目的就是加深自己的記憶,到時候用的時候盡量下筆如有神,哪怕回來看看自己整理的文章,也不要去百度,使自己花個10多分鐘多余的時間去搜索答案。還有一方面就是,看著自己開發了這么長時間,居然沒有怎么整理發布過博客,感覺沒有學到什么東西,看看其他同行都在寫自己的整理,心里不是滋味啊,mmp,還是因為一個字 “懶”。好了文章開始前就說這么多,下面就來看看我們今天的基礎知識吧,Activity的生命周期和啟動模式。

Activity生命周期

系統是通過實現回調方法來管理Activity的生命周期的,在說這些回調方法什么時候會被調用前,先來了解下Activity在整個生命周期中,是以3個狀態存在的。

-活動狀態: 此Activity位于屏幕前臺并且擁有用戶焦點。
-暫停狀態:另外一個Activity位于屏幕前臺并且擁有用戶焦點,但是此Activity任然可見。意思就是說當前Activity覆蓋在此Activity上,并且當前Activity是部分透明或者沒有完全覆蓋此Activity,從而使得此Activity仍然可見。此時此Activity是暫停狀態,然后是存活的(Activity對象保留在內存中,它保留了所有成員變量和狀態,與窗口管理器連接),但是在內存不足的情況下,可能會被系統終止。
-停止狀態:另外一個Activity把當前Activity完全覆蓋,使此Activity完全不可見(該Activity完全在后臺)。此時該Activity是停止狀態,但是此時該Activity還是存活狀態(Activity對象保留在內存中,擁有所有的狀態和成員變量信息,但是未與窗口管理器連接)。不過,他對用戶是不可見的,在其他地方需要內存時,該Activity可能被終止。即銷毀。
好了了解了Activity在整個生命周期的三種狀態后,生命周期就是指上面的三種狀態在相互之間轉換時,系統會通過各種回調方法來進行通知,我們繼續往下看吧。

1.正常情況下的生命周期

所謂正常情況下的生命周期就是,我們平時正常操作手機時,Activity所經歷的生命周期。我們先來看看在整個生命周期Activity會回調的方法。
(1).onCreate()方法: 首次創建Activity時會調用,在這個方法里面可以做一些初始化的工作,比如去加載布局文件等。后接onStart()方法。
(2).onRestart()方法:在Activity已經停止(onStop()方法后)并即將再次啟動前調用。注意之前Activity已經停止狀態并沒有銷毀時。后接onStart()方法。
(3).onStart()方法:在Activity即將對用戶可見之前調用。如果Activity轉入前臺可見,則后接onResume()方法,如果Activity進入隱藏狀態,則后接onStop()方法。
(4).onResume()方法:方法,在Activity即將開始與用戶進行交互之前調用。始終后接onPause()方法。
(5).onPause()方法:,當系統即將開始繼續另一個Activity時調用,此方法通常用于確認對持久化數據的未保存更改、停止動畫以及其他可能消耗CPU的內容。他應該非常迅速的執行所需操作,因為他返回后,下一個Activity才能繼續執行。如果此Activity轉入前臺,則執行onResume()方法,要是轉為后臺,則執行onStop()方法。
(6).onStop()方法:在Activity對用戶不可見時調用。對用戶不可見發生的情況一是當前Activity被銷毀(執行了finish()方法);二是另外一個Activity完全覆蓋了當前的Activity。如果Activity恢復與用戶的交互,則后接onRestart()方法。如果Activity被銷毀則后接onDestory()方法。
(7).onDestory()方法:在Activity被銷毀時調用。這是Activity收到的最后的回調方法。當Activity結束(有人對Activity調用了finish()方法),或者系統為節省控件兒暫時銷毀該Activity時,可能會調用他。

以上就是所有的回調方法,一共7個,注意上面三個方法,onPause()、onStop()、onDestory()方法,這三個方法在執行后,都有可能終止承載Activity的進程,畫個角度就是說,承載當前Activity的進程被終止時,onPause()方法是一定會被執行的,如果在緊急情況下需要內存,onStop()和onDestory()方法不一定會執行。因此,在onPause()方法中應該像存儲設備保存至關重要的數據(面試題問過)。不過不要在onPause()方法中執行太多的操作,不然會影響下一個Activity的執行。而其他的方法都會防止承載Activity的進程被終止。

雖然上面7個方法什么時候運行和之間的切換都描述了,我們還是用官方的一張圖來展示下:


生命周期轉換圖

看了上面7個方法的描述,這張圖看起來是不是很爽,很簡單。
好了生命周期的回調方法在上面寫的很清楚,包括什么時候執行什么方法和下一步具體的操作又會執行什么方法,下面我們就來驗證下正常操作Activity的整個生命周期的回調方法。不然上面說個蛋蛋,誰知道上面說的是對是錯。結合上面描述我們得到以下的結論:

1.一個Activity從啟動到顯示,會執行onCreate()-->onStart()-->onResume()。
2.當用戶打開新的Activity覆蓋 當前Activity時,會執行onPause()-->onStop();如果新的Activity采用了透明主題或者沒有完全覆蓋之前Activity時(這里的覆蓋只是Activity,若是Dialog覆蓋了當前Activity就不會對Activity的生命周期產生影響),只會執行onPause(),不會執行onStop()。
3.當用戶把不可見的Activity再次回到完全可見(與用戶交互)時,會執行onRestart()-->onstart()-->onResume()。
4.當用戶把被透明主題的Activity覆蓋的Activity再次回到完全可見時(與用戶交互),只會執行onResume()。
5.當系統回收一個Activity后,重新打開時,回調與1是一樣的。

1.1 代碼測試
/**
 * Created by h on 2018/2/6 0006.
 */
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.bt_tosecond_activity).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //跳轉到SecondActivity
                startActivity(new Intent(MainActivity.this, SecondActivity.class));
            }
        });


        findViewById(R.id.bt_open_dialog).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //彈出透明的LucencyActivity
                startActivity(new Intent(MainActivity.this, LucencyActivity.class));
            }
        });
        Log.e("TAG", "onCreate()");
    }

    @Override
    protected void onRestart() {
        super.onRestart();
        Log.e("TAG", "onRestart()");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.e("TAG", "onStart()");

    }

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

    @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()");
    }
}


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.gh.jiejin">

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <activity android:name=".SecondActivity" />
        <activity android:name=".LucencyActivity" android:theme="@android:style/Theme.Translucent"></activity>
    </application>

</manifest>

以上代碼就是通過兩個按鈕來啟動兩個新的Actiivity,注意LucencyActivity采用了透明主題。

1.2 測試結果
  • 運行MainActivity后控制臺打印如下:


    啟動MainActivity

    因此驗證了結論1。

  • 在MainActivity執行代碼打開SecondActivity,完全覆蓋MainActivity,然后再按返回按鈕,使MainActivity重新顯示控制臺打印如下:


    image.png

    可以看出當顯示SecondActivity完全覆蓋MainActivity時,依次打印了onPause()-->onStop()。
    當按下返回按鍵,MainActivity重新顯示時,依次執行了onRestart()-->onStart()-->onResume().
    因此證明了結論2和3。

  • 當在MainActivity中打開LucencyActivity時,由于采用了透明主題,MainActivity還是可見的,因此只會打印onPause()方法。這時候,要是再按下返回按鍵,使MainActivity完全顯示時,是不是也會只打印onResume()方法呢,我們這兩個操作依次執行,看看控制臺是怎樣打印的:


    image.png

    可以看出和我們想的完全一樣,這里也就驗證了結論2和4

  • 我們再看看MainActivity跳轉到SecondActivity這個過程,這兩個的生命周期方法是怎么回調的
    如下圖:


    image.png

    可以看出MainActivity的onPause()方法先調用完成后,然后再開始新的Activity,這也驗證了我們之前的分析,不要再onPause()方法中執行太多的操作,不然會影響新的Activity的顯示速度。

2 異常情況下的生命周期

2.1說到異常情況下的生命周期,什么樣的情況才是異常情況呢,異常情況分為以下兩種:

1.系統其他地方需要內存時,從而銷毀了某項Activity。優先銷毀后臺的Activity。
2.資源相關的系統配置發生改變導致Activity被殺死并重新創建。

2.2 異常情況下的生命周期概述

就是說,在以上兩種情況下,Activity會先銷毀,然后再會重新創建,這個過程的回調方法是onPause()-->onStop()-->onDestory()-->onCreate()-->onStart()-->onResume();這個過程就是異常情況下的生命周期。對于情況1,優先銷毀的是后臺的Activity,當用戶再次切換到該Activity時,該Activity就會重新創建;對于情況2,是在改變了系統的配置信息時(例如手機屏幕方向的切換、鍵盤可用性及語言)時,當前Activity會被銷毀并且馬上被創建。

2.2研究異常情況下生命周期的目的:

因為以上兩種情況Activity的殺死,基本都是在運行的Activity中發生的,在繼續Activity時,因為Activity被銷毀了又被重新創建了,之前Activity保存的狀態就不存在了,但是用戶并不知道Activity的狀態變化了,所以我們應該在系統銷毀Activity時保存狀態(也就是進行的操作),然后再重新創建Activity的時候恢復之前的狀態(也就是進行的操作)。

2.3 保存狀態和數據的操作

對于以上兩種情況而言,保存狀態或者數據和恢復狀態和數據是完全一致的。系統若是準備銷毀Activity時,系統會回調onSaveInstanceState()方法來對Activity的狀態信息進行保存,以確保有關Activity的狀態的重要信息進行保存,系統在調用onSaveInstanceState()方法后,然后再使Activity易于銷毀,在執行onSaveInstance()方法時,系統會像該方法中傳入一個Bundle,咱們可以使用這個Bundle對象使用putString()或者putInt()等方法以鍵值對的形式來保存信息。并且當用戶返回該Activity時,該Activity會被重建,并將Bundle對象傳遞給onCreate()和onRestoreInstanceState(),從這兩個方法中的任意一個都可以來提取之前保存的狀態和恢復Activiy的狀態。如果沒有狀態要恢復(比如之前銷毀前沒有進行保存數據,或者第一次啟動Activity時),這里系統傳入的Bundle對象就是空的。對于情況2與情況1唯一不同的是,情況1重新創建Activity時,是當用戶切換到該Activity時重新創建,情況2當系統配置發生變話時,會先銷毀然后馬上新建Activity,就只有重建Activity的時機不一樣而已.信息的保存和恢復過程是一模一樣的。

注意
1.并不是在任意Activity銷毀前都會調用onSaveInstanceState()方法的,因為這個方法是在異常情況下才會調用,普通的銷毀是不會銷毀這個方法的,比如按下返回按鍵是不會調用這個方法的。并且onSaveInstanceState方法是在onStop()方法之前調用的,與onPause()方法沒有前后關系,既有可能在onPause()之前調用,也有可能在之后調用。
2.不過,即使您什么都不做,也不實現onSaveInstanceState()方法,Activity的onSaveInstanceState()默認實現也會恢復部分 Activity 狀態。具體地講,默認實現會為布局中的每個View調用onSaveInstanceState()方法,讓每個視圖都能保存自身的狀態,Android 框架中幾乎每個小部件都會根據需要實現此方法,以便在重建 Activity 時自動保存和恢復對 UI 所做的任何可見更改,例如EditText部件保存用戶輸入的任何文本,checkBox 小部件保存復選框的選中或未選中狀態。您只需為想要保存其狀態的每個小部件提供一個唯一的 ID(通過 )android:id屬性)。如果小部件沒有 ID,則系統無法保存其狀態。
3.由于 onSaveInstanceState()的默認實現有助于保存 UI 的狀態,因此如果您為了保存更多狀態信息而替換該方法,應始終先調用onSaveInstanceState()的父類實現,然后再執行任何操作。 同樣,如果您替換 onRestoreInstanceState()方法,也應調用它的父類實現,以便默認實現能夠恢復視圖狀態。

2.4 代碼驗證

下面就通過代碼來演示下異常情況下的生命周期,因為異常的兩種情況下生命周期是一樣的,并且由于系統回收Activity不好模擬,我們就模擬在系統配置改變的情況下(屏幕方向改變)來測試,測試的結果說白了就是演示下生命周期的回調方法的調用順序和觀察下保存數據和恢復數據的情況還有一些View自帶的恢復效果。代碼如下:


   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 onRestart() {
        super.onRestart();
        Log.e("TAG", "onRestart()");
    }

    @Override
    protected void onStart() {
        super.onStart();
        Log.e("TAG", "onStart()");

    }

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

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

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //可以進行保存一些數據
        outState.putString("data","我保存的data");
        Log.e("TAG","onSaveInstanceState---保存數據data");
    }


    @Override
    protected void onRestoreInstanceState(Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        //恢復數據
        if(savedInstanceState!=null){
             String data = savedInstanceState.getString("data");
            Log.e("TAG","onRestoreInstanceState---取出數據data="+data);
        }

    }

    @Override
    protected void onStop() {
        super.onStop();
        Log.e("TAG", "onStop()");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Log.e("TAG", "onDestroy()");
    }



}

豎屏截圖


豎屏.png

橫屏截圖


橫屏.png

控制臺打印結果:


控制臺打印結果.png
2.5分析結果

可以看出在手機屏幕方向為豎屏時,EditText編寫了文字,然后改變手機方向為橫屏時,發現EditText編寫的文字還在,這就是EditText默認保存了信息狀態,我們再來看看在手機屏幕由豎屏轉換為橫屏時打印的方法,明顯時Activity先銷毀再重新創建了,在銷毀之前,調用onStop()方法之前確實先調用了onSaveInstanceState()方法,我們在這個中我們保存一個字符串信息,然后再重建的Activity顯示之前調用了onRestoreInstanceState()方法,并且取出了之前在onSaveInstanceState()方法中保存的字符串。這樣就驗證了我們上面的分析。最后說一句,我們能不能在系統配置文件改變的時候不重新創建Activity呢?答案是有的,我們可以在Actiivty配置文件中添加android:configChanges=""屬性就可以,屬性值的填寫可以具體查閱參考資料(去百度吧)哈哈,在android Studio會提示出來的,具體每個屬性值的意思應該能看出來,想配置多個屬性值時,中間用“|”來分隔開即可。


configChanges.png

3總結

以上就是對Activity生命周期的理解,主要是參考官方文檔來寫的,第一次寫文章,寫的有點亂,希望不要噴我。

?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,106評論 6 542
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,441評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,211評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,736評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,475評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,834評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,829評論 3 446
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,009評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,559評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,306評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,516評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,038評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,728評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,132評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,443評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,249評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,484評論 2 379

推薦閱讀更多精彩內容