Android基礎入門教程——4.1.3 Activity登堂入室

來源:互聯網
上載者:User

標籤:android   activity   launchmode   singletask   singleinst   

Android基礎入門教程——4.1.3 Activity登堂入室

標籤(空格分隔): Android基礎入門教程

本節引言:

好的,在學習了兩節的Activity後相信大家已經知道如何去使用Activity了,好的,本節講深一點,我們走下
底層,但不是考究源碼,還沒到那個水平..我們先看下Activity與Window,View之間的關係,即載入Actvitiy
經曆的流程,然後我們走下文檔,來學習Activity管理機制:Task和Back Stack,接著看下Android給我們提供
的允許管理Task的相關屬性,最後重點講解下Activity的四種啟動方式模式,好的,開始本節內容!

1.Activity,Window與View的關係

好吧,本來就想瞭解下他們幾個的關係,然後手多多,然後就開始看起他們的調用過程來了…結果扣了兩個小時,只理解了很小很小的一部分,果然,到底層擼源碼的都是大神,比如老羅,還沒到那個等級,下面是自己查閱資料,看了下一點源碼的歸納所得,如果哪寫錯了歡迎指出!下面貼下小結圖:

流程解析:
Activity調用startActivity後最後會調用attach方法,然後在PolicyManager實現一個Ipolicy介面,接著實現一個Policy對象,接著調用makenewwindow(Context)方法,該方法會返回一個PhoneWindow對象,而PhoneWindow
是Window的子類,在這個PhoneWindow中有一個DecorView的內部類,是所有應用視窗的根View,即View的老大,
直接控制Activity是否顯示(引用老司機原話..),好吧,接著裡面有一個LinearLayout,裡面又有兩個FrameLayout他們分別拿來裝ActionBar和CustomView,而我們setContentView()載入的布局就放到這個CustomView中!

總結下這三者的關係:
打個牽強的比喻:
我們可以把這三個類分別堪稱:畫家,畫布,畫筆畫出的東西;
畫家通過畫筆( LayoutInflater.infalte)畫出圖案,再繪製在畫布(addView)上!
最後顯示出來(setContentView)

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

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

概念解析:

我們的APP一般都是由多個Activity構成的,而在Android中給我們提供了一個Task(任務)的概念,
就是將多個相關的Activity收集起來,然後進行Activity的跳轉與返回!當然,這個Task只是一個
frameworker層的概念,而在Android中實現了Task的資料結構就是Back Stack(回退堆棧)
相信大家對於棧這種資料結構並不陌生,Java中也有個Stack的集合類!棧具有如下特點:

先進先出(LIFO),常用操作入棧(push),出棧(pop),處於最頂部的叫棧頂,最底部叫棧底

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

當切換到新的Activity,那麼該Activity會被壓入棧中,成為棧頂!
而當使用者點擊Back鍵,棧頂的Activity出棧,緊隨其後的Activity來到棧頂!

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

流程解析:

應用程式中存在A1,A2,A3三個activity,當使用者在Launcher或Home Screen點擊應用程式圖示時,
啟動主A1,接著A1開啟A2,A2開啟A3,這時棧中有三個Activity,並且這三個Activity預設在
同一個任務(Task)中,當使用者按返回時,彈出A3,棧中只剩A1和A2,再按返回鍵,
彈出A2,棧中只剩A1,再繼續按返回鍵,彈出A1,任務被移除,即程式退出!

接著在官方文檔中又看到了另外兩個圖,處於好奇,我又看了下解釋,然後跟群裡的人討論了下:

然後還有這段解釋:

然後總結下了結論:

Task是Activity的集合,是一個概念,實際使用的Back Stack來儲存Activity,可以有多個Task,但是
同一時刻只有一個棧在最前面,其他的都在後台!那棧是如何產生的呢?

答:當我們通過主畫面,點擊表徵圖開啟一個新的App,此時會建立一個新的Task!舉個例子:
我們通過點擊通訊錄APP的表徵圖開啟APP,這個時候會建立一個棧1,然後開始把新產生的Activity添加進來,可能我們在通訊錄的APP中開啟了簡訊APP的頁面,但是此時不會建立一個棧,而是繼續添加到棧1中,這是
Android推崇一種使用者體驗方式,即不同應用程式之間的切換能使使用者感覺就像是同一個應用程式,
很連貫的使用者體驗,官方稱其為seamless (無縫銜接)!
——————這個時候假如我們點擊Home鍵,回到主畫面,此時棧1進入後台,我們可能有下述兩種操作:
1)點擊菜單鍵(正方形那個按鈕),點擊開啟剛剛的程式,然後棧1又回到前台了!
又或者我們點擊主畫面上通訊錄的表徵圖,開啟APP,此時也不會建立新的棧,棧1回到前台!
2)如果此時我們點擊另一個表徵圖開啟一個新的APP,那麼此時則會建立一個新的棧2,棧2就會到前台,
而棧1繼續呆在後台;
3) 後面也是這樣…以此類推!

3.Task的管理1)文檔翻譯:

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

1)文檔翻譯

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

如上面所述,Android會將新成功啟動的Activity添加到同一個Task中並且按照以“先進先出”方式管理多個Task
和Back Stack,使用者就無需去擔心Activites如何與Task任務進行互動又或者它們是如何存在於Back Stack中!
或許,你想改變這種正常的管理方式。比如,你希望你的某個Activity能夠在一個新的Task中進行管理;
或者你只想對某個Activity進行執行個體化,又或者你想在使用者離開任務時清理Task中除了根Activity所有Activities。你可以做這些事或者更多,只需要通過修改AndroidManifest.xml中
< activity >的相關屬性值或者在代碼中通過傳遞特殊標識的Intent給startActivity( )就可以輕鬆的實現
對Actvitiy的管理了。

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

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

你能用的主要的Intent標誌有:

  • FLAG_ACTIVITY_NEW_TASK
  • FLAG_ACTIVITY_CLEAR_TOP
  • FLAG_ACTIVITY_SINGLE_TOP

好的,接下來逐個介紹這些怎麼用:

2)taskAffinity和allowTaskReparenting

預設情況下,一個應用程式中的所有activity都有一個Affinity,這讓它們屬於同一個Task。
你可以理解為是否處於同一個Task的標誌,然而,每個Activity可以通過
< activity>中的taskAffinity屬性設定單獨的Affinity。
不同應用程式中的Activity可以共用同一個Affinity,同一個應用程式中的不同Activity
也可以設定成不同的Affinity。
Affinity屬性在2種情況下起作用:

1)當啟動 activity的Intent對象包含FLAG_ACTIVITY_NEW_TASK標記:
當傳遞給startActivity()的Intent對象包含 FLAG_ACTIVITY_NEW_TASK標記時,系統會為需要啟動的Activity尋找與當前Activity不同Task。如果要啟動的 Activity的Affinity屬性與當前所有的Task的Affinity屬性都不相同,系統會建立一個帶那個Affinity屬性的Task,並將要啟動的Activity壓到建立的Task棧中;否則將Activity壓入那個Affinity屬性相同的棧中。
2)allowTaskReparenting屬性設定為true
如果一個activity的allowTaskReparenting屬性為true, 那麼它可以從一個Task(Task1)移到另外一個有相同Affinity的Task(Task2)中(Task2帶到前台時)。
如果一個.apk檔案從使用者角度來看包含了多個“應用程式”,你可能需要對那些 Activity賦不同的Affinity值。

3)launchMode:

四個可選值,啟動模式我們研究的核心,下面再詳細講!
他們分別是:standard(預設),singleTopsingleTasksingleInstance

4)清空棧

當使用者長時間離開Task(當前task被轉移到後台)時,系統會清除task中棧底Activity外的所有Activity
。這樣,當使用者返回到Task時,只留下那個task最初始的Activity了。我們可以通過修改下面這些屬性來
改變這種行為!

alwaysRetainTaskState
如果棧底Activity的這個屬性被設定為true,上述的情況就不會發生。 Task中的所有activity將被長時間儲存。

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

finishOnTaskLaunch
與clearTaskOnLaunch相似,但它只對單獨的activity操
作,而不是整個Task。它可以結束任何Activity,包括棧底的Activity。
當它設定為true時,當前的Activity只在當前會話期間作為Task的一部分存在,
當使用者退出Activity再返回時,它將不存在。

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

接下來我們來詳細地講解下四種載入模式:
他們分別是:standard(預設),singleTopsingleTasksingleInstance
在泡在網上的日子看到一篇圖文並茂的講解啟動模式的,很贊,可能更容易理解吧,這裡借鑒下:
原文連結:Activity啟動模式圖文詳解:standard, singleTop, singleTask 以及 singleInstance
英文原文:Understand Android Activity’s launchMode: standard, singleTop, singleTask and singleInstance
另外還有一篇詳細講解載入模式的:Android中Activity四種啟動模式和taskAffinity屬性詳解

先來看看總結圖:

模式詳解:

standard模式:

標準啟動模式,也是activity的預設啟動模式。在這種模式下啟動的activity可以被多次執行個體化,即在同一個任務中可以存在多個activity的執行個體,每個執行個體都會處理一個Intent對象。如果Activity A的啟動模式為standard,並且A已經啟動,在A中再次啟動Activity A,即調用startActivity(new Intent(this,A.class)),會在A的上面再次啟動一個A的執行個體,即當前的桟中的狀態為A–>A。


singleTop模式:

如果一個以singleTop模式啟動的Activity的執行個體已經存在於任務棧的棧頂,
那麼再啟動這個Activity時,不會建立新的執行個體,而是重用位於棧頂的那個執行個體,
並且會調用該執行個體的onNewIntent()方法將Intent對象傳遞到這個執行個體中。
舉例來說,如果A的啟動模式為singleTop,並且A的一個執行個體已經存在於棧頂中,
那麼再調用startActivity(new Intent(this,A.class))啟動A時,
不會再次建立A的執行個體,而是重用原來的執行個體,並且調用原來執行個體的onNewIntent()方法。
這時任務棧中還是這有一個A的執行個體。如果以singleTop模式啟動的activity的一個執行個體
已經存在與任務棧中,但是不在棧頂,那麼它的行為和standard模式相同,也會建立多個執行個體。

singleTask模式:

只允許在系統中有一個Activity執行個體。如果系統中已經有了一個執行個體,
持有這個執行個體的任務將移動到頂部,同時intent將被通過onNewIntent()發送。
如果沒有,則會建立一個新的Activity共置放在合適的任務中。

官方文檔中提到的一個問題:

系統會建立一個新的任務,並將這個Activity執行個體化為新任務的根部(root)
這個則需要我們對taskAffinity進行設定了,使用taskAffinity後的解僱:


singleInstance模式

保證系統無論從哪個Task啟動Activity都只會建立一個Activity執行個體,並將它加入新的Task棧頂
也就是說被該執行個體啟動的其他activity會自動運行於另一個Task中。
當再次啟動該activity的執行個體時,會重用已存在的任務和執行個體。並且會調用這個執行個體
的onNewIntent()方法,將Intent執行個體傳遞到該執行個體中。和singleTask相同,
同一時刻在系統中只會存在一個這樣的Activity執行個體。

5.Activity拾遺

對於Activity可能有些東西還沒講到,這裡預留一個位置,漏掉的都會在這裡補上!
首先是群友珠海-坤的建議,把開源中國的Activity管理類也貼上,嗯,這就貼上,大家可以直接用到
項目中~

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(){}    /**     * 單一執行個體     */    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);    }    /**     * 擷取當前Activity(堆棧中最後一個壓入的)     */    public Activity currentActivity(){        Activity activity=activityStack.lastElement();        return activity;    }    /**     * 結束當前Activity(堆棧中最後一個壓入的)     */    public void finishActivity(){        Activity activity=activityStack.lastElement();        finishActivity(activity);    }    /**     * 結束指定的Activity     */    public void finishActivity(Activity activity){        if(activity!=null){            activityStack.remove(activity);            activity.finish();            activity=null;        }    }    /**     * 結束指定類名的Activity     */    public void finishActivity(Class<?> cls){        for (Activity activity : activityStack) {            if(activity.getClass().equals(cls) ){                finishActivity(activity);            }        }    }    /**     * 結束所有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();    }    /**     * 退出應用程式     */    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) { }    }}
本節小結:

好的,本節就到這裡,東西都比較苦澀難懂,暫時知道下即可,總結下Task進行整體調度的
相關操作吧:

  • 按Home鍵,將之前的Task切換到後台
  • 長按Home鍵,會顯示出最近執行過的Task列表
  • 在Launcher或HomeScreen點擊app表徵圖,開啟一個新Task,或者是將已有的Task調度到前台
  • 啟動singleTask模式的Activity時,會在系統中搜尋是否已經存在一個合適的Task,若存在,則會將這個Task調度到前台以重用這個Task。如果這個Task中已經存在一個要啟動的Activity的執行個體,則清除這個執行個體之上的所有Activity,將這個執行個體顯示給使用者。如果這個已存在的Task中不存在一個要啟動的Activity的執行個體,則在這個Task的頂端啟動一個執行個體。若這個Task不存在,則會啟動一個新的Task,在這個新的Task中啟動這個singleTask模式的Activity的一個執行個體。
  • 啟動singleInstance的Activity時,會在系統中搜尋是否已經存在一個這個Activity的執行個體,如果存在,會將這個執行個體所在的Task調度到前台,重用這個Activity的執行個體(該Task中只有這一個Activity),如果不存在,會開啟一個新任務,並在這個新Task中啟動這個singleInstance模式的Activity的一個執行個體。

好的本節就到這裡,關於Task與Activity載入模式的東西還是比較複雜的,下面給大家貼下編寫該文的
時候的一些參考文獻,可以自己看看~

參考文獻:

1.Tasks and Back Stack
2.理解android中Activity和Task的關係
3.Activity啟動模式圖文詳解:standard, singleTop, singleTask 以及 singleInstance
4.Understand Android Activity’s launchMode: standard, singleTop, singleTask and singleInstance
5.Android中Activity四種啟動模式和taskAffinity屬性詳解
6.Android的Activity和Tasks詳解
7.Activity的四種啟動模式和onNewIntent()
8.譯:Android任務和返回棧完全解析,細數那些你所不知道的細節

著作權聲明:本文為博主原創文章,未經博主允許不得轉載。

Android基礎入門教程——4.1.3 Activity登堂入室

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.