Android Activity登堂入室

2023-03-31 13:45 更新

1.Activity,Window與View的關(guān)系

好吧,本來就想了解下他們幾個(gè)的關(guān)系,然后手多多,然后就開始看起他們的調(diào)用過程來了...結(jié)果扣了兩個(gè)小時(shí),只理解了很小很小的一部分,果然,到底層擼源碼的都是大神,比如老羅,還沒到那個(gè)等級(jí),下面是自己查閱資料,看了下一點(diǎn)源碼的歸納所得,如果哪寫錯(cuò)了歡迎指出!下面貼下小結(jié)圖:

流程解析: Activity調(diào)用startActivity后最后會(huì)調(diào)用attach方法,然后在PolicyManager實(shí)現(xiàn)一個(gè)Ipolicy接口,接著實(shí)現(xiàn)一個(gè)Policy對(duì)象,接著調(diào)用makenewwindow(Context)方法,該方法會(huì)返回一個(gè)PhoneWindow對(duì)象,而PhoneWindow 是Window的子類,在這個(gè)PhoneWindow中有一個(gè)DecorView的內(nèi)部類,是所有應(yīng)用窗口的根View,即View的老大, 直接控制Activity是否顯示(引用老司機(jī)原話..),好吧,接著里面有一個(gè)LinearLayout,里面又有兩個(gè)FrameLayout他們分別拿來裝ActionBar和CustomView,而我們setContentView()加載的布局就放到這個(gè)CustomView中!

總結(jié)下這三者的關(guān)系: 打個(gè)牽強(qiáng)的比喻: 我們可以把這三個(gè)類分別堪稱:畫家,畫布,畫筆畫出的東西; 畫家通過畫筆( LayoutInflater.infalte)畫出圖案,再繪制在畫布(addView)上! 最后顯示出來(setContentView)


2.Activity,Task和Back Stack的一些概念

接著我們來了解Android中Activity的管理機(jī)制,這就涉及到了兩個(gè)名詞:Task和Back Stack了!

概念解析:

我們的APP一般都是由多個(gè)Activity構(gòu)成的,而在Android中給我們提供了一個(gè)Task(任務(wù))的概念, 就是將多個(gè)相關(guān)的Activity收集起來,然后進(jìn)行Activity的跳轉(zhuǎn)與返回!當(dāng)然,這個(gè)Task只是一個(gè) frameworker層的概念,而在Android中實(shí)現(xiàn)了Task的數(shù)據(jù)結(jié)構(gòu)就是Back Stack(回退堆棧)! 相信大家對(duì)于棧這種數(shù)據(jù)結(jié)構(gòu)并不陌生,Java中也有個(gè)Stack的集合類!棧具有如下特點(diǎn):

后進(jìn)先出(LIFO),常用操作入棧(push),出棧(pop),處于最頂部的叫棧頂,最底部叫棧底

而Android中的Stack Stack也具有上述特點(diǎn),他是這樣來管理Activity的:

當(dāng)切換到新的Activity,那么該Activity會(huì)被壓入棧中,成為棧頂! 而當(dāng)用戶點(diǎn)擊Back鍵,棧頂?shù)腁ctivity出棧,緊隨其后的Activity來到棧頂!

我們來看下官方文檔給出的一個(gè)流程圖:

流程解析:

應(yīng)用程序中存在A1,A2,A3三個(gè)activity,當(dāng)用戶在Launcher或Home Screen點(diǎn)擊應(yīng)用程序圖標(biāo)時(shí), 啟動(dòng)主A1,接著A1開啟A2,A2開啟A3,這時(shí)棧中有三個(gè)Activity,并且這三個(gè)Activity默認(rèn)在 同一個(gè)任務(wù)(Task)中,當(dāng)用戶按返回時(shí),彈出A3,棧中只剩A1和A2,再按返回鍵, 彈出A2,棧中只剩A1,再繼續(xù)按返回鍵,彈出A1,任務(wù)被移除,即程序退出!

接著在官方文檔中又看到了另外兩個(gè)圖,處于好奇,我又看了下解釋,然后跟群里的人討論了下:

然后還有這段解釋:

然后總結(jié)下了結(jié)論:

Task是Activity的集合,是一個(gè)概念,實(shí)際使用的Back Stack來存儲(chǔ)Activity,可以有多個(gè)Task,但是 同一時(shí)刻只有一個(gè)棧在最前面,其他的都在后臺(tái)!那棧是如何產(chǎn)生的呢?

答:當(dāng)我們通過主屏幕,點(diǎn)擊圖標(biāo)打開一個(gè)新的App,此時(shí)會(huì)創(chuàng)建一個(gè)新的Task!舉個(gè)例子:
我們通過點(diǎn)擊通信錄APP的圖標(biāo)打開APP,這個(gè)時(shí)候會(huì)新建一個(gè)棧1,然后開始把新產(chǎn)生的Activity添加進(jìn)來,可能我們?cè)谕ㄓ嶄浀腁PP中打開了短信APP的頁面,但是此時(shí)不會(huì)新建一個(gè)棧,而是繼續(xù)添加到棧1中,這是 Android推崇一種用戶體驗(yàn)方式,即不同應(yīng)用程序之間的切換能使用戶感覺就像是同一個(gè)應(yīng)用程序, 很連貫的用戶體驗(yàn),官方稱其為seamless (無縫銜接)! ——————這個(gè)時(shí)候假如我們點(diǎn)擊Home鍵,回到主屏幕,此時(shí)棧1進(jìn)入后臺(tái),我們可能有下述兩種操作:
1)點(diǎn)擊菜單鍵(正方形那個(gè)按鈕),點(diǎn)擊打開剛剛的程序,然后棧1又回到前臺(tái)了! 又或者我們點(diǎn)擊主屏幕上通信錄的圖標(biāo),打開APP,此時(shí)也不會(huì)創(chuàng)建新的棧,棧1回到前臺(tái)!
2)如果此時(shí)我們點(diǎn)擊另一個(gè)圖標(biāo)打開一個(gè)新的APP,那么此時(shí)則會(huì)創(chuàng)建一個(gè)新的棧2,棧2就會(huì)到前臺(tái), 而棧1繼續(xù)呆在后臺(tái);
3) 后面也是這樣...以此類推!


3.Task的管理

1)文檔翻譯:

好的,繼續(xù)走文檔,從文檔中的ManagingTasks開始,大概的翻譯如下:


1)文檔翻譯

繼續(xù)走文檔,從文檔中的ManagingTasks開始,翻譯如下:

如上面所述,Android會(huì)將新成功啟動(dòng)的Activity添加到同一個(gè)Task中并且按照以"先進(jìn)先出"方式管理多個(gè)Task 和Back Stack,用戶就無需去擔(dān)心Activites如何與Task任務(wù)進(jìn)行交互又或者它們是如何存在于Back Stack中! 或許,你想改變這種正常的管理方式。比如,你希望你的某個(gè)Activity能夠在一個(gè)新的Task中進(jìn)行管理; 或者你只想對(duì)某個(gè)Activity進(jìn)行實(shí)例化,又或者你想在用戶離開任務(wù)時(shí)清理Task中除了根Activity所有Activities。你可以做這些事或者更多,只需要通過修改AndroidManifest.xml中 的相關(guān)屬性值或者在代碼中通過傳遞特殊標(biāo)識(shí)的Intent給startActivity( )就可以輕松的實(shí)現(xiàn) 對(duì)Actvitiy的管理了。

中我們可以使用的屬性如下:

  • taskAffinity
  • launchMode
  • allowTaskReparenting
  • clearTaskOnLaunch
  • alwaysRetainTaskState
  • finishOnTaskLaunch

你能用的主要的Intent標(biāo)志有:

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

好的,接下來逐個(gè)介紹這些怎么用:


2)taskAffinity和allowTaskReparenting

默認(rèn)情況下,一個(gè)應(yīng)用程序中的所有activity都有一個(gè)Affinity,這讓它們屬于同一個(gè)Task。 你可以理解為是否處于同一個(gè)Task的標(biāo)志,然而,每個(gè)Activity可以通過 中的taskAffinity屬性設(shè)置單獨(dú)的Affinity。 不同應(yīng)用程序中的Activity可以共享同一個(gè)Affinity,同一個(gè)應(yīng)用程序中的不同Activity 也可以設(shè)置成不同的Affinity。 Affinity屬性在2種情況下起作用:

1)當(dāng)啟動(dòng) activity的Intent對(duì)象包含FLAG_ACTIVITY_NEW_TASK標(biāo)記: 當(dāng)傳遞給startActivity()的Intent對(duì)象包含 FLAG_ACTIVITY_NEW_TASK標(biāo)記時(shí),系統(tǒng)會(huì)為需要啟動(dòng)的Activity尋找與當(dāng)前Activity不同Task。如果要啟動(dòng)的 Activity的Affinity屬性與當(dāng)前所有的Task的Affinity屬性都不相同,系統(tǒng)會(huì)新建一個(gè)帶那個(gè)Affinity屬性的Task,并將要啟動(dòng)的Activity壓到新建的Task棧中;否則將Activity壓入那個(gè)Affinity屬性相同的棧中。

2)allowTaskReparenting屬性設(shè)置為true 如果一個(gè)activity的allowTaskReparenting屬性為true, 那么它可以從一個(gè)Task(Task1)移到另外一個(gè)有相同Affinity的Task(Task2)中(Task2帶到前臺(tái)時(shí))。 如果一個(gè).apk文件從用戶角度來看包含了多個(gè)"應(yīng)用程序",你可能需要對(duì)那些 Activity賦不同的Affinity值。


3)launchMode:

四個(gè)可選值,啟動(dòng)模式我們研究的核心,下面再詳細(xì)講! 他們分別是:standard(默認(rèn)),singleTop,singleTasksingleInstance


4)清空棧

當(dāng)用戶長時(shí)間離開Task(當(dāng)前task被轉(zhuǎn)移到后臺(tái))時(shí),系統(tǒng)會(huì)清除task中棧底Activity外的所有Activity 。這樣,當(dāng)用戶返回到Task時(shí),只留下那個(gè)task最初始的Activity了。我們可以通過修改下面這些屬性來 改變這種行為!

alwaysRetainTaskState: 如果棧底Activity的這個(gè)屬性被設(shè)置為true,上述的情況就不會(huì)發(fā)生。 Task中的所有activity將被長時(shí)間保存。

clearTaskOnLaunch 如果棧底activity的這個(gè)屬性被設(shè)置為true,一旦用戶離開Task, 則 Task棧中的Activity將被清空到只剩下棧底activity。這種情況剛好與 alwaysRetainTaskState相反。即使用戶只是短暫地離開,task也會(huì)返回到初始狀態(tài) (只剩下棧底acitivty)。

finishOnTaskLaunch 與clearTaskOnLaunch相似,但它只對(duì)單獨(dú)的activity操 作,而不是整個(gè)Task。它可以結(jié)束任何Activity,包括棧底的Activity。 當(dāng)它設(shè)置為true時(shí),當(dāng)前的Activity只在當(dāng)前會(huì)話期間作為Task的一部分存在, 當(dāng)用戶退出Activity再返回時(shí),它將不存在。


4.Activity的四種加載模式詳解:

接下來我們來詳細(xì)地講解下四種加載模式: 他們分別是:standard(默認(rèn)),singleTopsingleTask,singleInstance 在泡在網(wǎng)上的日子看到一篇圖文并茂的講解啟動(dòng)模式的,很贊,可能更容易理解吧,這里借鑒下:

原文鏈接:Activity啟動(dòng)模式圖文詳解:standard, singleTop, singleTask 以及 singleInstance

英文原文:Understand Android Activity's launchMode: standard, singleTop, singleTask and singleInstance 另外還有一篇詳細(xì)講解加載模式的:Android中Activity四種啟動(dòng)模式和taskAffinity屬性詳解

先來看看總結(jié)圖:

模式詳解:


standard模式:

標(biāo)準(zhǔn)啟動(dòng)模式,也是activity的默認(rèn)啟動(dòng)模式。在這種模式下啟動(dòng)的activity可以被多次實(shí)例化,即在同一個(gè)任務(wù)中可以存在多個(gè)activity的實(shí)例,每個(gè)實(shí)例都會(huì)處理一個(gè)Intent對(duì)象。如果Activity A的啟動(dòng)模式為standard,并且A已經(jīng)啟動(dòng),在A中再次啟動(dòng)Activity A,即調(diào)用startActivity(new Intent(this,A.class)),會(huì)在A的上面再次啟動(dòng)一個(gè)A的實(shí)例,即當(dāng)前的桟中的狀態(tài)為A-->A。


singleTop模式:

如果一個(gè)以singleTop模式啟動(dòng)的Activity的實(shí)例已經(jīng)存在于任務(wù)棧的棧頂, 那么再啟動(dòng)這個(gè)Activity時(shí),不會(huì)創(chuàng)建新的實(shí)例,而是重用位于棧頂?shù)哪莻€(gè)實(shí)例, 并且會(huì)調(diào)用該實(shí)例的onNewIntent()方法將Intent對(duì)象傳遞到這個(gè)實(shí)例中。 舉例來說,如果A的啟動(dòng)模式為singleTop,并且A的一個(gè)實(shí)例已經(jīng)存在于棧頂中, 那么再調(diào)用startActivity(new Intent(this,A.class))啟動(dòng)A時(shí), 不會(huì)再次創(chuàng)建A的實(shí)例,而是重用原來的實(shí)例,并且調(diào)用原來實(shí)例的onNewIntent()方法。 這時(shí)任務(wù)棧中還是這有一個(gè)A的實(shí)例。如果以singleTop模式啟動(dòng)的activity的一個(gè)實(shí)例 已經(jīng)存在與任務(wù)棧中,但是不在棧頂,那么它的行為和standard模式相同,也會(huì)創(chuàng)建多個(gè)實(shí)例。


singleTask模式:

只允許在系統(tǒng)中有一個(gè)Activity實(shí)例。如果系統(tǒng)中已經(jīng)有了一個(gè)實(shí)例, 持有這個(gè)實(shí)例的任務(wù)將移動(dòng)到頂部,同時(shí)intent將被通過onNewIntent()發(fā)送。 如果沒有,則會(huì)創(chuàng)建一個(gè)新的Activity并置放在合適的任務(wù)中。

官方文檔中提到的一個(gè)問題:

系統(tǒng)會(huì)創(chuàng)建一個(gè)新的任務(wù),并將這個(gè)Activity實(shí)例化為新任務(wù)的根部(root) 這個(gè)則需要我們對(duì)taskAffinity進(jìn)行設(shè)置了,使用taskAffinity后的解雇:



singleInstance模式

保證系統(tǒng)無論從哪個(gè)Task啟動(dòng)Activity都只會(huì)創(chuàng)建一個(gè)Activity實(shí)例,并將它加入新的Task棧頂 也就是說被該實(shí)例啟動(dòng)的其他activity會(huì)自動(dòng)運(yùn)行于另一個(gè)Task中。 當(dāng)再次啟動(dòng)該activity的實(shí)例時(shí),會(huì)重用已存在的任務(wù)和實(shí)例。并且會(huì)調(diào)用這個(gè)實(shí)例 的onNewIntent()方法,將Intent實(shí)例傳遞到該實(shí)例中。和singleTask相同, 同一時(shí)刻在系統(tǒng)中只會(huì)存在一個(gè)這樣的Activity實(shí)例。


5.Activity拾遺

對(duì)于Activity可能有些東西還沒講到,這里預(yù)留一個(gè)位置,漏掉的都會(huì)在這里補(bǔ)上! 首先是群友珠海-坤的建議,把開源中國的Activity管理類也貼上,嗯,這就貼上,大家可以直接用到 項(xiàng)目中~

1)開源中國客戶端Activity管理類:

package net.oschina.app;

import java.util.Stack;

import android.app.Activity;
import android.app.ActivityManager;
import android.content.Context;

public class AppManager {

    private static Stack<Activity> activityStack;
    private static AppManager instance;

    private AppManager(){}
    /**
     * 單一實(shí)例
     */
    public static AppManager getAppManager(){
        if(instance==null){
            instance=new AppManager();
        }
        return instance;
    }
    /**
     * 添加Activity到堆棧
     */
    public void addActivity(Activity activity){
        if(activityStack==null){
            activityStack=new Stack<Activity>();
        }
        activityStack.add(activity);
    }
    /**
     * 獲取當(dāng)前Activity(堆棧中最后一個(gè)壓入的)
     */
    public Activity currentActivity(){
        Activity activity=activityStack.lastElement();
        return activity;
    }
    /**
     * 結(jié)束當(dāng)前Activity(堆棧中最后一個(gè)壓入的)
     */
    public void finishActivity(){
        Activity activity=activityStack.lastElement();
        finishActivity(activity);
    }
    /**
     * 結(jié)束指定的Activity
     */
    public void finishActivity(Activity activity){
        if(activity!=null){
            activityStack.remove(activity);
            activity.finish();
            activity=null;
        }
    }
    /**
     * 結(jié)束指定類名的Activity
     */
    public void finishActivity(Class<?> cls){
        for (Activity activity : activityStack) {
            if(activity.getClass().equals(cls) ){
                finishActivity(activity);
            }
        }
    }
    /**
     * 結(jié)束所有Activity
     */
    public void finishAllActivity(){
        for (int i = 0, size = activityStack.size(); i < size; i++){
            if (null != activityStack.get(i)){
                activityStack.get(i).finish();
            }
        }
        activityStack.clear();
    }
    /**
     * 退出應(yīng)用程序
     */
    public void AppExit(Context context) {
        try {
            finishAllActivity();
            ActivityManager activityMgr= (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
            activityMgr.restartPackage(context.getPackageName());
            System.exit(0);
        } catch (Exception e) { }
    }
}

本節(jié)小結(jié):

好的,本節(jié)就到這里,東西都比較苦澀難懂,暫時(shí)知道下即可,總結(jié)下Task進(jìn)行整體調(diào)度的 相關(guān)操作吧:

  • 按Home鍵,將之前的Task切換到后臺(tái)
  • 長按Home鍵,會(huì)顯示出最近執(zhí)行過的Task列表
  • 在Launcher或HomeScreen點(diǎn)擊app圖標(biāo),開啟一個(gè)新Task,或者是將已有的Task調(diào)度到前臺(tái)
  • 啟動(dòng)singleTask模式的Activity時(shí),會(huì)在系統(tǒng)中搜尋是否已經(jīng)存在一個(gè)合適的Task,若存在,則會(huì)將這個(gè)Task調(diào)度到前臺(tái)以重用這個(gè)Task。如果這個(gè)Task中已經(jīng)存在一個(gè)要啟動(dòng)的Activity的實(shí)例,則清除這個(gè)實(shí)例之上的所有Activity,將這個(gè)實(shí)例顯示給用戶。如果這個(gè)已存在的Task中不存在一個(gè)要啟動(dòng)的Activity的實(shí)例,則在這個(gè)Task的頂端啟動(dòng)一個(gè)實(shí)例。若這個(gè)Task不存在,則會(huì)啟動(dòng)一個(gè)新的Task,在這個(gè)新的Task中啟動(dòng)這個(gè)singleTask模式的Activity的一個(gè)實(shí)例。
  • 啟動(dòng)singleInstance的Activity時(shí),會(huì)在系統(tǒng)中搜尋是否已經(jīng)存在一個(gè)這個(gè)Activity的實(shí)例,如果存在,會(huì)將這個(gè)實(shí)例所在的Task調(diào)度到前臺(tái),重用這個(gè)Activity的實(shí)例(該Task中只有這一個(gè)Activity),如果不存在,會(huì)開啟一個(gè)新任務(wù),并在這個(gè)新Task中啟動(dòng)這個(gè)singleInstance模式的Activity的一個(gè)實(shí)例。

好的本節(jié)就到這里,關(guān)于Task與Activity加載模式的東西還是比較復(fù)雜的,下面給大家貼下編寫該文的 時(shí)候的一些參考文獻(xiàn),可以自己看看~


參考文獻(xiàn):

以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)