Activity登堂入室


1.Activity,Window與View的關係

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

1.jpg

#流程解析: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來到棧頂!

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

2.png

流程解析:

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

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

3.png 

接著還有這段解釋:

4.png

然後總結下了結論:

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屬性詳解

#先來看看總結圖:

5.png

#模式詳解:


standard模式:

標準啟動模式,也是activity的預設啟動模式。在這個模式下啟動的activity可以被多次實例化,也就是在同一個任務中可以存在多個activity的實例,每個實例都會處理一個Intent物件。如果Activity A的啟動模式為standard,且A已經啟動,在A中再次啟動Activity A,即呼叫startActivity(new Intent(this,A.class)),會在A的上方再次啟動一個A的實例,即目前的桟中的狀態為A-->A。

6.jpg

7.jpg


singleTop模式:

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

8.jpg


singleTask模式:

只允許在系統中有一個Activity實例。如果系統中已經有了一個實例, 持有這個實例的任務將會移到頂部,同時intent將會被透過onNewIntent()傳送。 如果沒有,則會建立一個新的Activity並置放在適當的任務中

9.jpg

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

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

10.jpg

11.jpg



singleInstance模式

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

12.jpg


5.Activity拾遺

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

1)開源中國客戶端活動管理類別:

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 ; ActivityStack;
私人靜態 AppManager 實例;

私人 AppManager(){}
/**
 * 單一實例
 */
公用靜態 AppManager getAppManager(){
if(instance= =null){
實例=新AppManager();
}
傳回實例;
}
/**
 * 加入Activity到堆疊
 */
public void addActivity(Activity Activity){
if(activityStack==null){
ActivityStack=new Stack();
}
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) {
if(activity.getClass().equals(cls) ){
finishActivity(activity);
}
}
}
/**
 * 結束所有Activity
 */
public void finishAllActivity(){
for (int i = 0, size = activityStack.size(); 我< 尺寸; i++){
            if (null != activityStack.get(i)){
          }
    }
ActivityStack.clear();
}
/**
 * 退出應用程式
 */
public void AppExit(Context context) {
try {
finishAllActivity();
ActivityManager ActivityMgr=(ActivityManager) context.getSystemService (上下文.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屬性詳解#############C #6.###Android的Activity和Tasks詳解#########7.###Activity的四個啟動模式和onNewIntent()#########8.###譯:Android任務和返回堆疊完全解析,細數那些你所不知道的細節#####################