http://blog.csdn.net/dct8888/article/details/52064160
問題描述:
我們通過廣播來啟動(dòng)Activity的時(shí)候如果不設(shè)置intent的FLAG_ACTIVITY_NEW_TASK屬性,就會(huì)報(bào)這個(gè)異常:
android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag.
就是說在activity上下文之外調(diào)用startActivity需要FLAG_ACTIVITY_NEW_TASK屬性。
問題分析:
Context是什么:Context描述的是一個(gè)應(yīng)用程序環(huán)境的信息,這是一個(gè)抽象(abstract class)類,Android提供了該抽象類的很多具體實(shí)現(xiàn)類,比如我們常用的Service,Application,Activity。通過它我們可以獲取應(yīng)用程序的資源和類,也包括一些應(yīng)用級(jí)別操作,例如:?jiǎn)?dòng)一個(gè)Activity,發(fā)送廣播,接受Intent信息等。
http://img.blog.csdn.net/20160729135731338
Activity上下文之外是什么意思:通過debug發(fā)現(xiàn)在Receiver->onReceive的時(shí)候傳進(jìn)來的context是ReceiverRestrictedContext,然而ReceiverRestrictedContext的代碼很簡(jiǎn)單,里面沒有startActivity方法,而ReceiverRestrictedContext是繼承自ContextWrapper,ContextWrapper的startActivity方法如下:
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
if((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
thrownew AndroidRuntimeException(
"Calling startActivity() from outside of an Activity "
+" context requires the FLAG_ACTIVITY_NEW_TASK flag."
+" Is this really what you want?");
}
mMainThread.getInstrumentation().execStartActivity(
getOuterContext(), mMainThread.getApplicationThread(), null,
(Activity)null, intent, -1, options);
}
可以看到如果我們?cè)赗eceiver中使用context的startActivity方法的話,這個(gè)方法會(huì)在執(zhí)行真正的調(diào)用之前會(huì)檢查一下有沒有設(shè)置這個(gè)FLAG_ACTIVITY_NEW_TASK的標(biāo)志,沒有設(shè)置的話就報(bào)上面所說的那個(gè)異常。那么為什么我們?cè)贏ctivity中直接startActivity方法就不會(huì)報(bào)這個(gè)異常呢?這是因?yàn)锳ctivity類在自己的實(shí)現(xiàn)中已經(jīng)覆蓋了父類的startActivity方法去除了這個(gè)檢查。至于這個(gè)標(biāo)志最終用在哪里我還沒有跟蹤到,因?yàn)樽罱K啟動(dòng)activity調(diào)用的是native方法,如果后面有時(shí)間研究這一塊我再補(bǔ)充上來。下面第三點(diǎn)主要從表層結(jié)合谷歌的文檔分析一下FLAG_ACTIVITY_NEW_TASK到底有什么作用。
@Override
publicvoid startActivity(Intent intent) {
mBase.startActivity(intent);
}
可已看到,實(shí)際上調(diào)用的是mBase的startActivity方法,通過圖一可以看到,mBase的基礎(chǔ)實(shí)現(xiàn)類就是ContextImpl類,于是定位到ContextImpl的startActivity方法中:
淺談設(shè)置FLAG_ACTIVITY_NEW_TASK的意義:首先需要了解一下什么是任務(wù)堆棧,大家可以直接看谷歌的文檔,這里已經(jīng)解釋的很清楚了:打開鏈接。那么FLAG_ACTIVITY_NEW_TASK的意義可以這么理解,一般來說當(dāng)我們從launcher中啟動(dòng)一個(gè)應(yīng)用進(jìn)入到ActivityA中,系統(tǒng)會(huì)為這個(gè)應(yīng)用生成一個(gè)新任務(wù)堆棧并置于前臺(tái),ActivityA被放入棧底,之后從ActivityA啟動(dòng)另一個(gè)ActivityB,如果不設(shè)置什么附加屬性,ActivityB默認(rèn)也放到和ActivityA這個(gè)堆棧中,這樣當(dāng)你按返回時(shí),B出棧,A呈現(xiàn)出來了,這個(gè)應(yīng)該很好理解。那現(xiàn)在假如ActivityA啟動(dòng)一個(gè)Service或者發(fā)一個(gè)廣播,這都是后臺(tái)的活,和我們的任務(wù)棧沒有關(guān)系,現(xiàn)在假如我們?cè)趶V播中需要啟動(dòng)一個(gè)Activity,當(dāng)然需要為這個(gè)Activity指定或分配一個(gè)任務(wù)棧,F(xiàn)LAG_ACTIVITY_NEW_TASK的意義就是這個(gè),官方的文檔解釋為:“Start the activity in a new task. If a task is already running for the activity you are now starting, that task is brought to the foreground with its last state restored and the activity receives the new intent in onNewIntent()
.”
問題總結(jié):
在Activity上下文之外啟動(dòng)Activity需要給Intent設(shè)置FLAG_ACTIVITY_NEW_TASK標(biāo)志,不然會(huì)報(bào)異常。