新聞中心
Android中的Activity詳解--啟動模式與任務(wù)棧
目錄
創(chuàng)新互聯(lián)是專業(yè)的羅山網(wǎng)站建設(shè)公司,羅山接單;提供成都網(wǎng)站建設(shè)、網(wǎng)站建設(shè),網(wǎng)頁設(shè)計,網(wǎng)站設(shè)計,建網(wǎng)站,PHP網(wǎng)站建設(shè)等專業(yè)做網(wǎng)站服務(wù);采用PHP框架,可快速的進行羅山網(wǎng)站開發(fā)網(wǎng)頁制作和功能擴展;專業(yè)做搜索引擎喜愛的網(wǎng)站,專業(yè)的做網(wǎng)站團隊,希望更多企業(yè)前來合作!
activity的簡單介紹就不寫了,作為最常用的四大組件之一,肯定都很熟悉其基本用法了。
首先,是都很熟悉的一張圖,即官方介紹的Activity生命周期圖.
情景:打開某個應(yīng)用的的FirstActivity調(diào)用方法如下:
由于之前已經(jīng)很熟悉了,這里就簡單貼一些圖。
按下返回鍵:
重新打開并按下home鍵:
再重新打開:
在其中打開一個DialogActivity(SecondActivity)
按下返回:
修改SecondAcitvity為普通Activity,依舊是上述操作:
這里強調(diào)一下 onSaveInstanceState(Bundle outState) 方法的調(diào)用時機:
當(dāng)Activity有可能被系統(tǒng)殺掉時調(diào)用,注意,一定是被系統(tǒng)殺掉,自己調(diào)用finish是不行的。
測試如下:FirstActivity啟動SecondActivity:
一個App會包含很多個Activity,多個Activity之間通過intent進行跳轉(zhuǎn),那么原始的Activity就是使用棧這個數(shù)據(jù)結(jié)構(gòu)來保存的。
Task
A task is a collection of activities that users interact with when performing a certain job. The activities are arranged in a stack (the back stack ), in the order in which each activity is opened.
即若干個Activity的集合的棧表示一個Task。
當(dāng)App啟動時如果不存在當(dāng)前App的任務(wù)棧就會自動創(chuàng)建一個,默認(rèn)情況下一個App中的所有Activity都是放在一個Task中的,但是如果指定了特殊的啟動模式,那么就會出現(xiàn)同一個App的Activity出現(xiàn)在不同的任務(wù)棧中的情況,即會有任務(wù)棧中包含來自于不同App的Activity。
標(biāo)準(zhǔn)模式,在不指定啟動模式的情況下都是以此種方式啟動的。每次啟動都會創(chuàng)建一個新的Activity實例,覆蓋在原有的Activity上,原有的Activity入棧。
測試如下:在FirstActivity中啟動FirstActivity:
當(dāng)只有一個FirstActivity時堆棧情況:
此種模式下,Activity在啟動時會進行判斷,如果當(dāng)前的App的棧頂?shù)腁ctivity即正在活動的Activity就是將要啟動的Activity,那么就不會創(chuàng)建新的實例,直接使用棧頂?shù)膶嵗?/p>
測試,設(shè)置FirstActivity為此啟動模式,多次點擊FirstActivity中的啟動FirstActivity的按鈕查看堆棧情況:
(其實點擊按鈕沒有啟動新Activity的動畫就可以看出并沒有啟動新Activity)
大意就是:
對于使用singleTop啟動或Intent.FLAG_ACTIVITY_SINGLE_TOP啟動的Activity,當(dāng)該Activity被重復(fù)啟動(注意一定是re-launched,第一次啟動時不會調(diào)用)時就會調(diào)用此方法。
且調(diào)用此方法之前會先暫停Activity也就是先調(diào)用onPause方法。
而且,即使是在新的調(diào)用產(chǎn)生后此方法被調(diào)用,但是通過getIntent方法獲取到的依舊是以前的Intent,可以通過setIntent方法設(shè)置新的Intent。
方法參數(shù)就是新傳遞的Intent.
1.如果是同一個App中啟動某個設(shè)置了此模式的Activity的話,如果棧中已經(jīng)存在該Activity的實例,那么就會將該Activity上面的Activity清空,并將此實例放在棧頂。
測試:SecondActivity啟動模式設(shè)為singleTask,啟動三個Activity:
這個模式就很好記,以此模式啟動的Activity會存放在一個單獨的任務(wù)棧中,且只會有一個實例。
測試:SecondActivity啟動模式設(shè)為singleInstance
結(jié)果:
顯然,啟動了兩次ThirdActivity任務(wù)棧中就有兩個實例,而SecondActivity在另外一個任務(wù)棧中,且只有一個。
在使用Intent啟動一個Activity時可以設(shè)置啟動該Activity的啟動模式:
這個屬性有很多,大致列出幾個:
每個啟動的Activity都在一個新的任務(wù)棧中
singleTop
singleTask
用此種方式啟動的Activity,在它啟動了其他Activity后,會自動finish.
官方文檔介紹如下:
這樣看來的話,通俗易懂的講,就是給每一個任務(wù)棧起個名,給每個Activity也起個名,在Activity以singleTask模式啟動時,就檢查有沒有跟此Activity的名相同的任務(wù)棧,有的話就將其加入其中。沒有的話就按照這個Activity的名創(chuàng)建一個任務(wù)棧。
測試:在App1中設(shè)置SecondActivity的taskAffinity為“gsq.test”,App2中的ActivityX的taskAffinity也設(shè)為“gsq.test”
任務(wù)棧信息如下:
結(jié)果很顯然了。
測試:在上述基礎(chǔ)上,在ActivityX中進行跳轉(zhuǎn)到ActivityY,ActivityY不指定啟動模式和taskAffinity。結(jié)果如下:
這樣就沒問題了,ActivityY在一個新的任務(wù)棧中,名稱為包名。
這時從ActivityY跳轉(zhuǎn)到SecondActivity,那應(yīng)該是gsq.test任務(wù)棧只有SecondActivity,ActivityX已經(jīng)沒有了。因為其啟動模式是singleTask,在啟動它時發(fā)現(xiàn)已經(jīng)有一個實例存在,就把它所在的任務(wù)棧上面的Activity都清空了并將其置于棧頂。
還有一點需要提一下,在上面,F(xiàn)irstActivity是App1的lunch Activity,但是由于SecondActivity并沒有指定MAIN和LAUNCHER過濾器,故在FirstActivity跳轉(zhuǎn)到SecondActivity時,按下home鍵,再點開App1,回到的是FirstActivity。
大致就先寫這么多吧,好像有點長,廢話有點多,估計也有錯別字,不要太在意~~~
android 中怎樣能夠清除activity堆棧,也就是退出整個應(yīng)用
如果退出整個程序,如下操作:方式一:Intent intent=new Intent(Intent.ACTION_MAIN);intent.addCategory(Intent.CATEGORY_HOME);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);this.startActivity(intent);System.exit(0); 方式二:android.os.Process.killProcess(android.os.Process.myPid()); android 完全退出程序有幾個activity,有一需求是在一個activityA點擊back鍵退出系統(tǒng)而不是跳到之前的activity首先想到的是清空activityA的堆棧,使用intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 但是該activityA不是已經(jīng)存在于堆棧底端的,所以清除的只是堆棧中該activityA上面的activity,但后退后還是會返回堆棧中該activityA下面的activity。
手機調(diào)試Android程序出異常時不打印堆棧信息
打印堆棧是調(diào)試的常用方法,一般在系統(tǒng)異常時,我們可以將異常情況下的堆棧打印出來,這樣十分方便錯誤查找。實際上還有另外一個非常有用的功能:分析代碼的行為。android代碼太過龐大復(fù)雜了,完全的靜態(tài)分析經(jīng)常是無從下手,因此通過打印堆棧的動態(tài)分析也十分必要。
Android打印堆棧的方法,簡單歸類一下
1. zygote的堆棧dump
實際上這個可以同時dump java線程及native線程的堆棧,對于java線程,java堆棧和native堆棧都可以得到。
使用方法很簡單,直接在adb shell或串口中輸入:
[plain] view plaincopy
kill -3 pid
輸出的trace會保存在 /data/anr/traces.txt文件中。這個需要注意,如果沒有 /data/anr/這個目錄或/data/anr/traces.txt這個文件,需要手工創(chuàng)建一下,并設(shè)置好讀寫權(quán)限。
如果需要在代碼中,更容易控制堆棧的輸出時機,可以用以下命令獲取zygote的core dump:
[java] view plaincopy
Process.sendSignal(pid, Process.SIGNAL_QUIT);
原理和命令行是一樣的。
不過需要注意兩點:
adb shell可能會沒有權(quán)限,需要root。
android 4.2中關(guān)閉了native thread的堆棧打印,詳見 dalvik/vm/Thread.cpp的dumpNativeThread方法:
[cpp] view plaincopy
dvmPrintDebugMessage(target,
"\"%s\" sysTid=%d nice=%d sched=%d/%d cgrp=%s\n",
name, tid, getpriority(PRIO_PROCESS, tid),
schedStats.policy, schedStats.priority, schedStats.group);
dumpSchedStat(target, tid);
// Temporarily disabled collecting native stacks from non-Dalvik
// threads because sometimes they misbehave.
//dvmDumpNativeStack(target, tid);
Native堆棧的打印被關(guān)掉了!不過對于大多數(shù)情況,可以直接將這個注釋打開。
2. debuggerd的堆棧dump
debuggerd是android的一個daemon進程,負(fù)責(zé)在進程異常出錯時,將進程的運行時信息dump出來供分析。debuggerd生 成的coredump數(shù)據(jù)是以文本形式呈現(xiàn),被保存在 /data/tombstone/ 目錄下(名字取的也很形象,tombstone是墓碑的意思),共可保存10個文件,當(dāng)超過10個時,會覆蓋重寫最早生成的文件。從4.2版本開 始,debuggerd同時也是一個實用工具:可以在不中斷進程執(zhí)行的情況下打印當(dāng)前進程的native堆棧。使用方法是:
[plain] view plaincopy
debuggerd -b pid
這可以協(xié)助我們分析進程執(zhí)行行為,但最最有用的地方是:它可以非常簡單的定位到native進程中鎖死或錯誤邏輯引起的死循環(huán)的代碼位置。
3. java代碼中打印堆棧
Java代碼打印堆棧比較簡單, 堆棧信息獲取和輸出,都可以通過Throwable類的方法實現(xiàn)。目前通用的做法是在java進程出現(xiàn)需要注意的異常時,打印堆棧,然后再決定退出或挽救。通常的方法是使用exception的printStackTrace()方法:
[java] view plaincopy
try {
...
} catch (RemoteException e) {
e.printStackTrace();
...
}
當(dāng)然也可以只打印堆棧不退出,這樣就比較方便分析代碼的動態(tài)運行情況。Java代碼中插入堆棧打印的方法如下:
[java] view plaincopy
Log.d(TAG,Log.getStackTraceString(new Throwable()));
4. C++代碼中打印堆棧
C++也是支持異常處理的,異常處理庫中,已經(jīng)包含了獲取backtrace的接口,Android也是利用這個接口來打印堆棧信息的。在Android的C++中,已經(jīng)集成了一個工具類CallStack,在libutils.so中。使用方法:
[cpp] view plaincopy
#include utils/CallStack.h
...
CallStack stack;
stack.update();
stack.dump();
使用方式比較簡單。目前Andoid4.2版本已經(jīng)將相關(guān)信息解析的很到位,符號表查找,demangle,偏移位置校正都做好了。
[plain] view plaincopy
5. C代碼中打印堆棧
C代碼,尤其是底層C庫,想要看到調(diào)用的堆棧信息,還是比較麻煩的。 CallStack肯定是不能用,一是因為其實C++寫的,需要重新封裝才能在C中使用,二是底層庫反調(diào)上層庫的函數(shù),會造成鏈接器循環(huán)依賴而無法鏈接。 不過也不是沒有辦法,可以通過android工具類CallStack實現(xiàn)中使用的unwind調(diào)用及符號解析函數(shù)來處理。
這里需要注意的是,為解決鏈接問題,最好使用dlopen方式,查找需要用到的接口再直接調(diào)用,這樣會比較簡單。如下為相關(guān)的實現(xiàn)代碼,只需要在要 打印的文件中插入此部分代碼,然后調(diào)用getCallStack()即可,無需包含太多的頭文件和修改Android.mk文件:
[cpp] view plaincopy
#define MAX_DEPTH 31
#define MAX_BACKTRACE_LINE_LENGTH 800
#define PATH "/system/lib/libcorkscrew.so"
typedef ssize_t (*unwindFn)(backtrace_frame_t*, size_t, size_t);
typedef void (*unwindSymbFn)(const backtrace_frame_t*, size_t, backtrace_symbol_t*);
typedef void (*unwindSymbFreeFn)(backtrace_symbol_t*, size_t);
static void *gHandle = NULL;
static int getCallStack(void){
ssize_t i = 0;
ssize_t result = 0;
ssize_t count;
backtrace_frame_t mStack[MAX_DEPTH];
backtrace_symbol_t symbols[MAX_DEPTH];
unwindFn unwind_backtrace = NULL;
unwindSymbFn get_backtrace_symbols = NULL;
unwindSymbFreeFn free_backtrace_symbols = NULL;
// open the so.
if(gHandle == NULL) gHandle = dlopen(PATH, RTLD_NOW);
// get the interface for unwind and symbol analyse
if(gHandle != NULL) unwind_backtrace = (unwindFn)dlsym(gHandle, "unwind_backtrace");
if(gHandle != NULL) get_backtrace_symbols = (unwindSymbFn)dlsym(gHandle, "get_backtrace_symbols");
if(gHandle != NULL) free_backtrace_symbols = (unwindSymbFreeFn)dlsym(gHandle, "free_backtrace_symbols");
if(!gHandle ||!unwind_backtrace ||!get_backtrace_symbols || !free_backtrace_symbols ){
ALOGE("Error! cannot get unwind info: handle:%p %p %p %p",
gHandle, unwind_backtrace, get_backtrace_symbols, free_backtrace_symbols );
return result;
}
count= unwind_backtrace(mStack, 1, MAX_DEPTH);
get_backtrace_symbols(mStack, count, symbols);
for (i = 0; i count; i++) {
char line[MAX_BACKTRACE_LINE_LENGTH];
const char* mapName = symbols[i].map_name ? symbols[i].map_name : "unknown";
const char* symbolName =symbols[i].demangled_name ? symbols[i].demangled_name : symbols[i].symbol_name;
size_t fieldWidth = (MAX_BACKTRACE_LINE_LENGTH - 80) / 2;
if (symbolName) {
uint32_t pc_offset = symbols[i].relative_pc - symbols[i].relative_symbol_addr;
if (pc_offset) {
snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d pc %08x %.*s (%.*s+%u)",
i, symbols[i].relative_pc, fieldWidth, mapName,
fieldWidth, symbolName, pc_offset);
} else {
snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d pc %08x %.*s (%.*s)",
i, symbols[i].relative_pc, fieldWidth, mapName,
fieldWidth, symbolName);
}
} else {
snprintf(line, MAX_BACKTRACE_LINE_LENGTH, "#%02d pc %08x %.*s",
i, symbols[i].relative_pc, fieldWidth, mapName);
}
ALOGD("%s", line);
}
free_backtrace_symbols(symbols, count);
return result;
}
對sched_policy.c的堆棧調(diào)用分析如下,注意具體是否要打印,在哪里打印,還可以通過pid、uid、property等來控制一下,這樣就不會被淹死在trace的汪洋大海中。
[plain] view plaincopy
D/SchedPolicy( 1350): #00 pc 0000676c /system/lib/libcutils.so
D/SchedPolicy( 1350): #01 pc 00006b3a /system/lib/libcutils.so (set_sched_policy+49)
D/SchedPolicy( 1350): #02 pc 00010e82 /system/lib/libutils.so (androidSetThreadPriority+61)
D/SchedPolicy( 1350): #03 pc 00068104 /system/lib/libandroid_runtime.so (android_os_Process_setThreadPriority(_JNIEnv*, _jobject*, int, int)+7)
D/SchedPolicy( 1350): #04 pc 0001e510 /system/lib/libdvm.so (dvmPlatformInvoke+112)
D/SchedPolicy( 1350): #05 pc 0004d6aa /system/lib/libdvm.so (dvmCallJNIMethod(unsigned int const*, JValue*, Method const*, Thread*)+417)
D/SchedPolicy( 1350): #06 pc 00027920 /system/lib/libdvm.so
D/SchedPolicy( 1350): #07 pc 0002b7fc /system/lib/libdvm.so (dvmInterpret(Thread*, Method const*, JValue*)+184)
D/SchedPolicy( 1350): #08 pc 00060c30 /system/lib/libdvm.so (dvmCallMethodV(Thread*, Method const*, Object*, bool, JValue*, std::__va_list)+271)
D/SchedPolicy( 1350): #09 pc 0004cd34 /system/lib/libdvm.so
D/SchedPolicy( 1350): #10 pc 00049382 /system/lib/libandroid_runtime.so
D/SchedPolicy( 1350): #11 pc 00065e52 /system/lib/libandroid_runtime.so
D/SchedPolicy( 1350): #12 pc 0001435e /system/lib/libbinder.so (android::BBinder::transact(unsigned int, android::Parcel const, android::Parcel*, unsigned int)+57)
D/SchedPolicy( 1350): #13 pc 00016f5a /system/lib/libbinder.so (android::IPCThreadState::executeCommand(int)+513)
D/SchedPolicy( 1350): #14 pc 00017380 /system/lib/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+183)
D/SchedPolicy( 1350): #15 pc 0001b160 /system/lib/libbinder.so
D/SchedPolicy( 1350): #16 pc 00011264 /system/lib/libutils.so (android::Thread::_threadLoop(void*)+111)
D/SchedPolicy( 1350): #17 pc 000469bc /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+63)
D/SchedPolicy( 1350): #18 pc 00010dca /system/lib/libutils.so
D/SchedPolicy( 1350): #19 pc 0000e3d8 /system/lib/libc.so (__thread_entry+72)
D/SchedPolicy( 1350): #20 pc 0000dac4 /system/lib/libc.so (pthread_create+160)
D/SchedPolicy( 1350): #00 pc 0000676c /system/lib/libcutils.so
D/SchedPolicy( 1350): #01 pc 00006b3a /system/lib/libcutils.so (set_sched_policy+49)
D/SchedPolicy( 1350): #02 pc 00016f26 /system/lib/libbinder.so (android::IPCThreadState::executeCommand(int)+461)
D/SchedPolicy( 1350): #03 pc 00017380 /system/lib/libbinder.so (android::IPCThreadState::joinThreadPool(bool)+183)
D/SchedPolicy( 1350): #04 pc 0001b160 /system/lib/libbinder.so
D/SchedPolicy( 1350): #05 pc 00011264 /system/lib/libutils.so (android::Thread::_threadLoop(void*)+111)
D/SchedPolicy( 1350): #06 pc 000469bc /system/lib/libandroid_runtime.so (android::AndroidRuntime::javaThreadShell(void*)+63)
D/SchedPolicy( 1350): #07 pc 00010dca /system/lib/libutils.so
D/SchedPolicy( 1350): #08 pc 0000e3d8 /system/lib/libc.so (__thread_entry+72)
D/SchedPolicy( 1350): #09 pc 0000dac4 /system/lib/libc.so (pthread_create+160)
6. 其它堆棧信息查詢
Android中的activity的堆棧有什么作用
我的理解是堆棧就是后進先出,那么稍微想像一下,你打開的Activity是一層一層往上蓋的,當(dāng)你退出當(dāng)前這個Activity的時候,使用堆棧機制才會顯示你底下那一層的Activity,提高Activity復(fù)用率吧。如果你覺得這個Activity可以不用再保留那么也給你提供了相應(yīng)的打開另一個Activity之后就清理掉自己的方法。這樣做的用戶體驗會比較好吧;那么反過來講如果沒有采用堆棧機制,在這么有限的顯示區(qū)域里應(yīng)該怎么去分配多個Activity呢?
理解任務(wù)和后臺堆棧(活動四)
任務(wù)是用戶在執(zhí)行特定作業(yè)時與之交互的活動的集合。
活動按堆棧排列 - (返回棧) - 按每個活動打開的順序排列。 例如,電子郵件應(yīng)用可能有一個活動來顯示新消息列表。 當(dāng)用戶選擇消息時,將打開一個新活動以查看該消息。 此新活動將添加到后臺堆棧。 如果用戶按下“返回”按鈕,則表示新活動已完成并從堆棧中彈出。 以下視頻概述了后端堆棧的工作原理。
當(dāng)應(yīng)用程序在多窗口環(huán)境中同時運行時,在Android 7.0(API級別24)及更高版本中受支持,系統(tǒng)會為每個窗口單獨管理任務(wù); 每個窗口可能有多個任務(wù)。 對于在Chromebook上運行的Android應(yīng)用程序也是如此:系統(tǒng)基于每個窗口管理任務(wù)或任務(wù)組。
設(shè)備主屏幕是大多數(shù)任務(wù)的起始位置。 當(dāng)用戶觸摸應(yīng)用程序啟動器中的圖標(biāo)(或主屏幕上的快捷方式)時,該應(yīng)用程序的任務(wù)將進入前臺。 如果應(yīng)用程序不存在任務(wù)(最近未使用該應(yīng)用程序),則會創(chuàng)建一個新任務(wù),該應(yīng)用程序的“主”活動將作為堆棧中的根活動打開。
當(dāng)前活動從另一個活動開始時,新活動將被推到堆棧頂部并獲得焦點。 之前的活動仍在堆棧中,但已停止。 當(dāng)活動停止時,系統(tǒng)將保留其用戶界面的當(dāng)前狀態(tài)。 當(dāng)用戶按下“返回”按鈕時,當(dāng)前活動將從堆棧頂部彈出(活動被銷毀),之前的活動將恢復(fù)(其UI的先前狀態(tài)將恢復(fù))。 堆棧中的活動永遠不會重新排列,只能在當(dāng)前活動啟動時從堆棧中推送并彈出到堆棧中,并在用戶使用“返回”按鈕離開時彈出。 因此,后堆棧作為“后進先出”對象結(jié)構(gòu)操作。 圖1顯示了這種行為,時間軸顯示了活動之間的進度以及每個時間點的當(dāng)前后棧。
如果用戶繼續(xù)按Back,則彈出堆棧中的每個活動以顯示前一個活動,直到用戶返回主屏幕(或任務(wù)開始時運行的任何活動)。 從堆棧中刪除所有活動后,該任務(wù)不再存在。
任務(wù)是一個內(nèi)聚單元,當(dāng)用戶開始新任務(wù)或通過主頁按鈕進入主屏幕時,可以移動到“后臺”。在后臺,任務(wù)中的所有活動都會停止,但任務(wù)的后臺堆棧保持不變 - 任務(wù)在發(fā)生另一項任務(wù)時完全失去焦點,如圖2所示。然后任務(wù)可以返回到“前臺“所以用戶可以從中斷的地方繼續(xù)前進。例如,假設(shè)當(dāng)前任務(wù)(任務(wù)A)在其堆棧中有三個活動 - 在當(dāng)前活動下有兩個活動。用戶按下主頁按鈕,然后從應(yīng)用啟動器啟動新應(yīng)用。出現(xiàn)主屏幕時,任務(wù)A進入后臺。當(dāng)新應(yīng)用程序啟動時,系統(tǒng)會使用自己的一系列活動為該應(yīng)用程序(任務(wù)B)啟動任務(wù)。在與該應(yīng)用程序交互之后,用戶再次返回Home并選擇最初啟動任務(wù)A的應(yīng)用程序。現(xiàn)在,任務(wù)A進入前臺 - 其堆棧中的所有三個活動都完好無損,并且堆棧頂部的活動將恢復(fù)。此時,用戶還可以通過返回主頁并選擇啟動該任務(wù)的應(yīng)用程序圖標(biāo)(或從“最近”屏幕中選擇應(yīng)用程序的任務(wù))切換回任務(wù)B.這是Android上的多任務(wù)處理的一個示例。
圖2.兩個任務(wù):任務(wù)B在前臺接收用戶交互,而任務(wù)A在后臺,等待恢復(fù)。
圖3.單個活動多次實例化。
由于后備堆棧中的活動永遠不會重新排列,如果您的應(yīng)用程序允許用戶從多個活動啟動特定活動,則會創(chuàng)建該活動的新實例并將其推送到堆棧(而不是帶來任何先前的活動實例) 到頂部)。 因此,您的應(yīng)用中的一個活動可能會被多次實例化(甚至來自不同的任務(wù)),如圖3所示。因此,如果用戶使用“后退”按鈕向后導(dǎo)航,則活動的每個實例都按順序顯示 被打開(每個都有自己的UI狀態(tài))。 但是,如果您不希望多次實例化活動,則可以修改此行為。 有關(guān)如何執(zhí)行此操作將在后面的“管理任務(wù)”一節(jié)中討論。
總結(jié)活動和任務(wù)的默認(rèn)行為:
Android管理任務(wù)和后臺堆棧的方式,如上所述 - 通過將所有活動連續(xù)啟動到同一任務(wù)和“后進先出”堆棧 - 對于大多數(shù)應(yīng)用程序而言非常有用,您不必?fù)?dān)心 關(guān)于您的活動如何與任務(wù)相關(guān)聯(lián)或它們?nèi)绾未嬖谟诤笈_堆棧中。 但是,您可能決定要中斷正常行為。 也許您希望應(yīng)用程序中的活動在啟動時開始新任務(wù)(而不是放在當(dāng)前任務(wù)中); 或者,當(dāng)你開始一個活動時,你想要提出它的現(xiàn)有實例(而不是在后面的堆棧頂部創(chuàng)建一個新的實例); 或者,您希望在用戶離開任務(wù)時清除除了根活動之外的所有活動的后臺堆棧。
您可以使用activity清單元素中的屬性以及傳遞給startActivity()的intent中的標(biāo)記來執(zhí)行這些操作。
在這方面,您可以使用的主要activity屬性是:
您可以使用的主要意圖標(biāo)志是:
在以下部分中,您將了解如何使用這些清單屬性和意圖標(biāo)志來定義活動與任務(wù)的關(guān)聯(lián)方式以及它們在后端堆棧中的行為方式。
另外,單獨討論的是在“最近”屏幕中如何表示和管理任務(wù)和活動的注意事項。 有關(guān)詳細信息,請參閱最近屏幕。 通常,您應(yīng)該允許系統(tǒng)在“最近”屏幕中定義您的任務(wù)和活動的表示方式,而不需要修改此行為。
啟動模式允許您定義活動的新實例與當(dāng)前任務(wù)的關(guān)聯(lián)方式。 您可以通過兩種方式定義不同的啟動模式:
因此,如果活動A啟動活動B,活動B可以在其清單中定義它應(yīng)該如何與當(dāng)前任務(wù)相關(guān)聯(lián)(如果有的話),活動A也可以請求活動B應(yīng)該如何與當(dāng)前任務(wù)相關(guān)聯(lián)。 如果兩個活動都定義了活動B應(yīng)該如何與任務(wù)相關(guān)聯(lián),則活動A的請求(如意圖中所定義)將遵循活動B的請求(如其清單中所定義)。
在清單文件中聲明活動時,可以使用activity元素的launchMode屬性指定活動應(yīng)如何與任務(wù)關(guān)聯(lián)。
launchMode屬性指定有關(guān)如何將活動啟動到任務(wù)的說明。 您可以為launchMode屬性分配四種不同的啟動模式:
"standard" (the default mode)
默認(rèn)。 系統(tǒng)在啟動它的任務(wù)中創(chuàng)建活動的新實例,并將意圖路由到該實例。 活動可以多次實例化,每個實例可以屬于不同的任務(wù),一個任務(wù)可以有多個實例。
"singleTop"
果活動的實例已存在于當(dāng)前任務(wù)的頂部,則系統(tǒng)通過調(diào)用其onNewIntent()方法將意圖路由到該實例,而不是創(chuàng)建活動的新實例?;顒涌梢远啻螌嵗?,每個實例可以屬于不同的任務(wù),一個任務(wù)可以有多個實例(但只有當(dāng)后端堆棧頂部的活動不是活動的現(xiàn)有實例時)。
例如,假設(shè)任務(wù)的后向堆棧由根活動A組成,活動B,C和D位于頂部(堆棧為A-B-C-D; D位于頂部)。意圖到達類型D的活動。如果D具有默認(rèn)的“標(biāo)準(zhǔn)”啟動模式,則啟動該類的新實例并且堆棧變?yōu)锳-B-C-D-D。但是,如果D的啟動模式是“singleTop”,則現(xiàn)有的D實例通過onNewIntent()接收意圖,因為它位于堆棧的頂部 - 堆棧仍然是A-B-C-D。但是,如果意圖到達類型B的活動,則即使其啟動模式為“singleTop”,也會將新的B實例添加到堆棧中。
"singleTask"
系統(tǒng)創(chuàng)建新任務(wù)并在新任務(wù)的根目錄下實例化活動。 但是,如果活動的實例已存在于單獨的任務(wù)中,則系統(tǒng)會通過調(diào)用其onNewIntent()方法將意圖路由到現(xiàn)有實例,而不是創(chuàng)建新實例。 一次只能存在一個活動實例。
"singleInstance"
與“singleTask”相同,但系統(tǒng)不會在持有實例的任務(wù)中啟動任何其他活動。 活動始終是其任務(wù)的唯一成員; 任何由此開始的活動都在一個單獨的任務(wù)中打開。
作為另一個示例,Android瀏覽器應(yīng)用程序聲明Web瀏覽器活動應(yīng)始終在其自己的任務(wù)中打開 - 通過在activity元素中指定singleTask啟動模式。這意味著,如果您的應(yīng)用發(fā)出打開Android瀏覽器的意圖,則其活動不會與您的應(yīng)用放在同一任務(wù)中。相反,要么為瀏覽器啟動新任務(wù),要么如果瀏覽器已經(jīng)在后臺運行任務(wù),則該任務(wù)將被提前處理新意圖。
無論活動是在新任務(wù)中啟動還是在與啟動它的活動相同的任務(wù)中啟動,“返回”按鈕始終會將用戶帶到上一個活動。但是,如果啟動指定singleTask啟動模式的活動,則如果后臺任務(wù)中存在該活動的實例,則將整個任務(wù)帶到前臺。此時,后端堆?,F(xiàn)在包括堆棧頂部提出的任務(wù)中的所有活動。圖4說明了這種情況。
圖4.表示如何將具有啟動模式“singleTask”的活動添加到后臺堆棧。 如果活動已經(jīng)是具有自己的后臺堆棧的后臺任務(wù)的一部分,那么整個后臺堆棧也會在當(dāng)前任務(wù)的基礎(chǔ)上出現(xiàn)。
有關(guān)在清單文件中使用啟動模式的更多信息,請參閱activity元素文檔,其中詳細討論了launchMode屬性和接受的值。
啟動活動時,您可以通過在傳遞給startActivity()的intent中包含標(biāo)志來修改活動與其任務(wù)的默認(rèn)關(guān)聯(lián)。 您可以用來修改默認(rèn)行為的標(biāo)志是:
FLAG_ACTIVITY_NEW_TASK
在新任務(wù)中啟動活動。 如果任務(wù)已在為您正在啟動的活動運行,則該任務(wù)將返回到前臺,并恢復(fù)其上一個狀態(tài),并且活動將在onNewIntent()中接收新的意圖。
這會產(chǎn)生與上一節(jié)中討論的“singleTask”launchMode值相同的行為。
FLAG_ACTIVITY_SINGLE_TOP
如果正在啟動的活動是當(dāng)前活動(在后臺堆棧的頂部),則現(xiàn)有實例將接收對onNewIntent()的調(diào)用,而不是創(chuàng)建活動的新實例。
這會產(chǎn)生與上一節(jié)中討論的“singleTop”launchMode值相同的行為。
FLAG_ACTIVITY_CLEAR_TOP
如果正在啟動的活動已在當(dāng)前任務(wù)中運行,則不會啟動該活動的新實例,而是銷毀其上的所有其他活動,并將此意圖傳遞給活動的恢復(fù)實例(現(xiàn)在開啟) top),通過onNewIntent())。
生成此行為的launchMode屬性沒有任何值。
FLAG_ACTIVITY_CLEAR_TOP通常與FLAG_ACTIVITY_NEW_TASK結(jié)合使用。 當(dāng)一起使用時,這些標(biāo)志是一種在另一個任務(wù)中定位現(xiàn)有活動并將其置于可以響應(yīng)意圖的位置的方法。
親和力指示活動喜歡屬于哪個任務(wù)。 默認(rèn)情況下,同一應(yīng)用程序中的所有活動都具有彼此的關(guān)聯(lián)。 因此,默認(rèn)情況下,同一個應(yīng)用程序中的所有活動都希望處于同一任務(wù)中。 但是,您可以修改活動的默認(rèn)關(guān)聯(lián)。 在不同應(yīng)用中定義的活動可以共享親和力,或者可以為同一應(yīng)用中定義的活動分配不同的任務(wù)親和力。
您可以使用activity元素的taskAffinity屬性修改任何給定活動的親緣關(guān)系。
taskAffinity屬性采用字符串值,該值必須與manifest元素中聲明的默認(rèn)包名稱唯一,因為系統(tǒng)使用該名稱來標(biāo)識應(yīng)用程序的默認(rèn)任務(wù)關(guān)聯(lián)。
親和力在兩種情況下起作用:
如果用戶長時間離開任務(wù),系統(tǒng)將清除除根活動之外的所有活動的任務(wù)。 當(dāng)用戶再次返回任務(wù)時,僅還原根活動。 系統(tǒng)以這種方式運行,因為在很長一段時間之后,用戶可能已經(jīng)放棄了之前正在做的事情并返回任務(wù)以開始新的事情。
您可以使用一些活動屬性來修改此行為:
alwaysRetainTaskState:
如果在任務(wù)的根活動中將此屬性設(shè)置為“true”,則不會發(fā)生剛才描述的默認(rèn)行為。 即使經(jīng)過很長一段時間,任務(wù)仍會保留堆棧中的所有活動。
clearTaskOnLaunch:
如果在任務(wù)的根活動中將此屬性設(shè)置為“true”,則只要用戶離開任務(wù)并返回到該任務(wù),就會將堆棧清除為根活動。 換句話說,它與alwaysRetainTaskState相反。 即使在離開任務(wù)片刻之后,用戶也始終以初始狀態(tài)返回任務(wù)。
finishOnTaskLaunch:
此屬性類似于clearTaskOnLaunch,但它在單個活動上運行,而不是整個任務(wù)。 它還可以導(dǎo)致任何活動消失,包括根活動。 當(dāng)它設(shè)置為“true”時,活動仍然是當(dāng)前會話的任務(wù)的一部分。 如果用戶離開然后返回任務(wù),它將不再存在。
您可以將活動設(shè)置為任務(wù)的入口點,方法是為其指定一個過濾器,其中“android.intent.action.MAIN”作為指定的操作,“android.intent.category.LAUNCHER”作為指定的類別。 例如:
這種意圖過濾器會導(dǎo)致活動的圖標(biāo)和標(biāo)簽顯示在應(yīng)用程序啟動器中,從而為用戶提供啟動活動并返回其在啟動后隨時創(chuàng)建的任務(wù)的方法。
第二種能力很重要:用戶必須能夠離開任務(wù),然后使用此活動啟動器返回該任務(wù)。因此,僅當(dāng)活動具有ACTION_MAIN和CATEGORY_LAUNCHER過濾器時,才應(yīng)使用將活動標(biāo)記為始終啟動任務(wù)的兩種啟動模式“singleTask”和“singleInstance”。例如,想象一下,如果缺少過濾器會發(fā)生什么:intent會啟動“singleTask”活動,啟動新任務(wù),并且用戶會花一些時間在該任務(wù)中工作。然后用戶按下主頁按鈕。該任務(wù)現(xiàn)在發(fā)送到后臺并且不可見?,F(xiàn)在用戶無法返回任務(wù),因為它未在應(yīng)用啟動器中顯示。
對于您不希望用戶能夠返回活動的情況,請將activity元素的finishOnTaskLaunch設(shè)置為“true”(請參閱清除后臺堆棧)。
有關(guān)如何在“概述”屏幕中表示和管理任務(wù)和活動的更多信息,請參見“最近用戶”屏幕。
文章名稱:android堆棧,手機 堆棧
文章轉(zhuǎn)載:http://www.dlmjj.cn/article/dsdopoo.html