Activity的生命周期

從活動一跳轉(zhuǎn)到活動二:

image.png

在跳轉(zhuǎn)中執(zhí)行完onPause之后馬上就執(zhí)行活動二的onCreate方法撞秋,等活動二的onResume執(zhí)行完后才執(zhí)行活動一的onStop方法,所以不能再onPause方法里執(zhí)行耗時操作


image.png

當(dāng)Activity異常終止的時候onSaveInstanceState和onRestoreInstanceState就會被調(diào)用嚣鄙,用來儲存和恢復(fù)數(shù)據(jù)吻贿,其他情況下不會觸發(fā)這個過程

  • 在活動異常終止不可見時馬上調(diào)用儲存數(shù)據(jù)的方法,當(dāng)活動要運(yùn)行到前臺可見之前調(diào)用恢復(fù)數(shù)據(jù)的方法
  • 每個View都有這兩個方法哑子,了解他們的具體實(shí)現(xiàn)后就知道系統(tǒng)能夠自動為每個view恢復(fù)哪些數(shù)據(jù)
  • 注:onSaveInstanceState一定是在onStop方法前調(diào)用舅列,但和onPause方法沒有既定的時序關(guān)系,有可能在這個方法之前調(diào)用卧蜓,也有可能在這個方法之后調(diào)用
  • 什么情況為異常終止:
  1. 資源相關(guān)的系統(tǒng)配置發(fā)生改變導(dǎo)致Activity被殺死并重新創(chuàng)建
    例如從橫屏狀態(tài)突然帶豎屏狀態(tài)
  2. 資源內(nèi)存不足導(dǎo)致優(yōu)先級低的Activity被殺死

測試數(shù)據(jù)儲存和恢復(fù)
測試代碼如下:

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //onCreate如果是正常啟動參數(shù)為null
        if (savedInstanceState!=null){
            String test=savedInstanceState.getString("extra_text");
            Log.d(TAG, "onCreate恢復(fù)數(shù)據(jù):"+test);
        }
        Button button=findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent=new Intent(MainActivity.this,Main2Activity.class);
                startActivity(intent);
            }
        });
        Log.d(TAG, "onCreate: ");
    }

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

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

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

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

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

    @Override
    protected void onSaveInstanceState(@NonNull Bundle outState) {
        super.onSaveInstanceState(outState);
        Log.d(TAG, "onSaveInstanceState: ");
        outState.putString("extra_text","text");
    }
    //onRestoreInstanceState一旦被調(diào)用其參數(shù)一定是有值的
    @Override
    protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
        super.onRestoreInstanceState(savedInstanceState);
        String test=savedInstanceState.getString("extra_text");
        Log.d(TAG, "onRestoreInstanceState恢復(fù)數(shù)據(jù):"+test);
    }
}

結(jié)果:

image.png

可以用Bundle傳輸數(shù)據(jù)

public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button button=findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                //用Bundle傳輸數(shù)據(jù)
                Intent intent=new Intent(MainActivity.this,Main2Activity.class);
                Bundle bundle=new Bundle();
                bundle.putString("name","Tom");
                bundle.putInt("age",20);
                intent.putExtras(bundle);
                //用intent傳輸數(shù)據(jù)
                intent.putExtra("nickname","lkl");
                startActivity(intent);
            }
        });

public class Main2Activity extends AppCompatActivity {
    private static final String TAG = "Main2Activity";

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main2);
        //獲取數(shù)據(jù)
        Intent intent1=getIntent();
        //接收intent的傳輸
        String nickName=intent1.getStringExtra("nickname");
        Log.d(TAG, "onCreate別名[intent]):"+nickName);
        //接收Bundle的傳輸
        Bundle bundle=intent1.getExtras();
        if (bundle!=null){
            String name=bundle.getString("name");
            int age=bundle.getInt("age");
            Log.d(TAG, "onCreate名字[bundle]: "+name);
            Log.d(TAG, "onCreate年齡[bundle]:"+age);
        }
    }
}

image.png

控制屏幕翻轉(zhuǎn)后活動不重新創(chuàng)建
1.在AndroidManifest.xml文件里配置

android:configChanges="orientation|screenSize"

如下:

<activity android:name=".MainActivity"
            android:configChanges="orientation|screenSize">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

2.在活動里從寫onConfigurationChanged方法

 @Override
    public void onConfigurationChanged(@NonNull Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        Log.d(TAG, "onConfigurationChanged: 最新的位置"+newConfig.orientation);
    }

這樣就不會再調(diào)用onSaveInstanceState和onRestoreInstanceState方法了

image.png

從圖中可以看出這個方法被調(diào)用了兩次帐要!Why?
1.當(dāng)推出鍵盤的時候,會觸發(fā)硬件的改變,使手機(jī)豎屏變成了橫屏
2.當(dāng)推進(jìn)鍵盤的時候,合上手機(jī)的一刻,觸發(fā)的是同一樣的固件,由于,固件是無法辨別那個是推進(jìn),和推出,發(fā)出的是一樣的信號,然后系統(tǒng)就會認(rèn)為這是一個橫屏改變,等到合上手機(jī)的時候系統(tǒng)再接受到一個信號,然后切換成豎屏.這樣系統(tǒng),認(rèn)為橫屏切換豎屏改變了兩次,這樣就導(dǎo)致調(diào)用了兩次onConfigurationChanged();


再加一個Button控件測試不同的啟動模式
在對應(yīng)的活動中添加:

android:launchMode="standard"

在活動中的代碼:

Button button2=findViewById(R.id.button2);
        button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent2=new Intent(MainActivity.this,MainActivity.class);
                startActivity(intent2);
            }
        });
  • 每點(diǎn)擊一次按鈕就會重新創(chuàng)建一個相同的活動


    image.png

修改

android:launchMode="singleTop"

點(diǎn)擊了兩次,back了一次的執(zhí)行情況

image.png
補(bǔ)充說明任務(wù)棧

重要參數(shù):TaskAffinity(任務(wù)相關(guān)性)

  • 每個 Activity 運(yùn)行時都有一個其歸屬的 task棧弥奸,我們可以用 activity.getTaskId() 的方法得到當(dāng)前 activity 的taskId榨惠。如果兩個 activity 的 taskId 不同,則他們肯定不會屬于同一個 task盛霎。
  • Android 手機(jī)的任務(wù)列表就是根據(jù)不同 task 彈出的赠橙,我們可以根據(jù)任務(wù)管理器有幾個 item 圖標(biāo),來知道我們開啟了幾個 task愤炸。
  • 指定活動2的棧測試使用方法:
<activity android:name=".Main2Activity"
            android:taskAffinity="com.example.task1">
        </activity>
  • 如上圖所示简烤,taskaffinity 可以單獨(dú)對一個 activity 使用,代表該 activity 所想歸屬的 task摇幻;
    也能對application 使用横侦,代表該 application 內(nèi)聲明的所有 activity 都?xì)w屬于這個task。這個屬性值必須和包名不同绰姻,否則就相當(dāng)于沒有指定
  • 如果 activity 組件沒有聲明 taskAffinity 的話枉侧,該 activity 的 taskAffinity 屬性也是有默認(rèn)值的。如果 application 指定了 taskAffinity 值狂芋,默認(rèn)值就是 application 指定的 taskAffinity 值榨馁;如果 application 未指定的話,默認(rèn)值就是 manifest 中聲明的包名(package 對應(yīng)的字符串)
  • 是不是我指定了一個 Activity 的 taskAffinity 值(跟包名不同)帜矾,運(yùn)行該 Activity 時翼虫,是否就會新開這個 task棧呢屑柔?答案是否定的,一個 Activity 運(yùn)行時所歸屬的task珍剑,默認(rèn)是啟動它 的那個Activity 所在的 task

在活動1和2分別打印棧的id

 Log.d(TAG, "活動1的棧: "+this.getTaskId());
 Log.d(TAG, "活動2的棧: "+this.getTaskId());

結(jié)果:

image.png

結(jié)論:

  • taskAffinity 單獨(dú)使用并不會生效掸宛。
    要想其生效,需要配合其他屬性使用招拙,或者配合 Intent.FLAG_ACTIVITY_NEW_TASK唧瘾,或者配合allowTaskReparenting 。使用時用其中的一個就行
    測試:
    在跳轉(zhuǎn)的intent中加入:
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

總體主要代碼:

Intent intent=new Intent(MainActivity.this,Main2Activity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intent);

結(jié)果:


image.png

完整測試及l(fā)og日志:
我們假定都是 Activity A 跳轉(zhuǎn)到 Activity B 中别凤,A沒有指定 taskAffinity 屬性,B 的launchMode 為standard饰序。

case1: A、B 屬同一App, intent 未指定 FLAG_ACTIVITY_NEW_TASK规哪,B 未指定 taskAffinity 屬性
D/MyApplication: onActivityResumed+MainActivity####taskid = 61
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 61

  • 可以驗(yàn)證:一個Activity 歸屬的task 是由 啟動它的 Activity 所決定的求豫。

case2: A、B 屬同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK诉稍,B 指定 taskAffinity 屬性蝠嘉,但與包名相同
D/MyApplication: onActivityResumed+MainActivity####taskid = 62
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 62

  • 可以驗(yàn)證,一個 Activity 的默認(rèn) task 值就是 manifest 定義的包名均唉。

case3: A、B 屬同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK肚菠,B 指定 taskAffinity 屬性舔箭,但與包名不同
D/MyApplication: onActivityResumed+MainActivity####taskid = 63
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 63

  • 可以驗(yàn)證:不指定 FLAG_ACTIVITY_NEW_TASK的話, 即使 taskAffinity 不同蚊逢,一個Activity 歸屬的task 仍然是由 啟動它的 Activity 所決定的层扶。

case4: A、B 屬同一App,intent 指定 FLAG_ACTIVITY_NEW_TASK烙荷,B 未指定 taskAffinity 屬性
D/MyApplication: onActivityResumed+MainActivity####taskid = 64
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 64

  • 可以驗(yàn)證:即使 使用了 FLAG_ACTIVITY_NEW_TASK镜会,但由于兩者的 taskAffinity 相同,所以仍然不會開啟一個新的task终抽。

case5: A戳表、B 屬同一App,intent 指定 FLAG_ACTIVITY_NEW_TASK,B 指定 taskAffinity 屬性昼伴,且和包名不同
D/MyApplication: onActivityResumed+MainActivity####taskid = 65
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 66

  • 可以驗(yàn)證:開啟一個新task 的條件是 FLAG_ACTIVITY_NEW_TASK 和 taskAffinity 不同 缺一不可匾旭。

case6: A、B 屬同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK圃郊,B 未指定 taskAffinity 屬性价涝,B啟動模式為 singletask or singletop
D/MyApplication: onActivityResumed+MainActivity####taskid = 67
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 67

  • 可以得出:未指定 FLAG_ACTIVITY_NEW_TASK 和 新的 taskAffinity 時,這兩種啟動模式對task 沒有影響

case7: A持舆、B 屬同一App,intent 未指定 FLAG_ACTIVITY_NEW_TASK色瘩,B 未指定 taskAffinity 屬性伪窖,B啟動模式為 singleinstance
D/MyApplication: onActivityResumed+MainActivity####taskid = 70
D/MyApplication: onActivityResumed+KeyboardActivity####taskid = 71

  • 可以得出:
    singleinstance 啟動模式本身就是會開啟一個新的task 裝載 這個Activity,且task 中只有這一個 Activity居兆。
  • 經(jīng)判斷得知覆山,AMS 是先對 launchMode 做判斷 再處理 FLAG_ACTIVITY_NEW_TASK 的,如果是 singleinstance 史辙,則會直接開啟一個task汹买。
onNewIntent方法與啟動模式

前提:ActivityA已經(jīng)啟動過,處于當(dāng)前應(yīng)用的Activity任務(wù)棧中;

當(dāng)ActivityA的LaunchMode為Standard時:

由于每次啟動ActivityA都是啟動新的實(shí)例,和原來啟動的沒關(guān)系聊倔,所以不會調(diào)用原來ActivityA的onNewIntent方法

當(dāng)ActivityA的LaunchMode為SingleTop時:

如果ActivityA在棧頂,且現(xiàn)在要再啟動ActivityA晦毙,這時會調(diào)用onNewIntent()方法 ,生命周期順序?yàn)椋?/p>

onCreate--->onStart--->onResume---onPause>onNewIntent--->onResume

當(dāng)ActivityA的LaunchMode為SingleInstance,SingleTask:

如果ActivityA已經(jīng)在堆棧中耙蔑,那么此時會調(diào)用onNewIntent()方法见妒,生命周期調(diào)用順序?yàn)椋?/p>

onCreate--->onStart--->onResume---按下Home鍵>onPause--->onstop--->onNewIntent--->onRestart--->onstart--->onResume

測試代碼:

   button2.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Intent intent2=new Intent(MainActivity.this,MainActivity.class);
                intent2.putExtra("music","hey kong");
                startActivity(intent2);
            }
        });
  @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
        String musicIs=getIntent().getStringExtra("music");
        Log.d(TAG, "onNewIntent:1 "+musicIs);
    }

注意:
有時候,我們在多次啟動同一個棧唯一模式下的activity時甸陌,在onNewIntent()里面的getIntent()得到的intent感覺都是第一次的那個數(shù)據(jù)须揣。對,這里就是這個陷阱钱豁。因?yàn)樗褪菚祷氐谝粋€intent的數(shù)據(jù)耻卡。就是這么坑。
原因就是我們沒有在onNewIntent()里面設(shè)置setIntent()牲尺,將最新的intent設(shè)置給這個activity實(shí)例卵酪。
加了setIntent():


image.png

沒加:
修改:

 intent2.putExtra("music","hey kong2");

結(jié)果:

image.png

還是上一個intent結(jié)果,沒有改變
加了setIntent():

image.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谤碳,一起剝皮案震驚了整個濱河市溃卡,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌蜒简,老刑警劉巖瘸羡,帶你破解...
    沈念sama閱讀 211,817評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異搓茬,居然都是意外死亡犹赖,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,329評論 3 385
  • 文/潘曉璐 我一進(jìn)店門卷仑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來冷尉,“玉大人,你說我怎么就攤上這事系枪∪干冢” “怎么了?”我有些...
    開封第一講書人閱讀 157,354評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長雾棺。 經(jīng)常有香客問我膊夹,道長,這世上最難降的妖魔是什么捌浩? 我笑而不...
    開封第一講書人閱讀 56,498評論 1 284
  • 正文 為了忘掉前任放刨,我火速辦了婚禮,結(jié)果婚禮上尸饺,老公的妹妹穿的比我還像新娘进统。我一直安慰自己,他們只是感情好浪听,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,600評論 6 386
  • 文/花漫 我一把揭開白布螟碎。 她就那樣靜靜地躺著,像睡著了一般迹栓。 火紅的嫁衣襯著肌膚如雪掉分。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,829評論 1 290
  • 那天克伊,我揣著相機(jī)與錄音酥郭,去河邊找鬼。 笑死愿吹,一個胖子當(dāng)著我的面吹牛不从,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播犁跪,決...
    沈念sama閱讀 38,979評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼椿息,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了耘拇?” 一聲冷哼從身側(cè)響起撵颊,我...
    開封第一講書人閱讀 37,722評論 0 266
  • 序言:老撾萬榮一對情侶失蹤宇攻,失蹤者是張志新(化名)和其女友劉穎惫叛,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體逞刷,經(jīng)...
    沈念sama閱讀 44,189評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,519評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了笼恰。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辽话。...
    茶點(diǎn)故事閱讀 38,654評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖帆喇,靈堂內(nèi)的尸體忽然破棺而出警医,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布预皇,位于F島的核電站侈玄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏吟温。R本人自食惡果不足惜序仙,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,940評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望鲁豪。 院中可真熱鬧潘悼,春花似錦、人聲如沸爬橡。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,762評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽堤尾。三九已至肝劲,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間郭宝,已是汗流浹背辞槐。 一陣腳步聲響...
    開封第一講書人閱讀 31,993評論 1 266
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留粘室,地道東北人榄檬。 一個月前我還...
    沈念sama閱讀 46,382評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像衔统,于是被迫代替她去往敵國和親鹿榜。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,543評論 2 349

推薦閱讀更多精彩內(nèi)容