Android異常之Service啟動Activity

Android異常之Service啟動Activity

在Activity中其中startActivity這個大家應該是非常熟悉的在塔;那么從service里面調(diào)用startActivity話郁季,會怎么樣呢芒率?

會出現(xiàn)下面的異常:

android.util.AndroidRuntimeException: Calling startActivity() from outside of an Activity??context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?

也就是在service里面啟動Activity的話卡啰,必須添加FLAG_ACTIVITY_NEW_TASK flag。

那么下面的話餐曼,我們將從下面幾個方面分析這個問題篮灼。

1.? ? 這個異常怎么產(chǎn)生的?

2.? ? 解決這個異常后會出現(xiàn)問題?

3.? ? 為什么Activity.startActivity()不會出現(xiàn)這個問題疾渴?

4.? ? Android 為什么要這么設計千贯?

下面,一一分析

一.? ? Context的繼承關系圖

首先來看一張圖搞坝, 這張圖表示了Context里面的基本繼承關系搔谴。

1.? ? 最上面的是Context.java,它其實是一個抽象類桩撮,它有兩個重要的子類ContextImpl和ContextWrapper

2.? ? ContextImpl敦第,是Context功能實現(xiàn)的主要類,

3.? ? ContextWrapper店量,顧名思義芜果,它只是一個包裝而已。主要功能實現(xiàn)都是通過調(diào)用ContextImpl去實現(xiàn)的融师。

4.? ? ContextThemeWrapper右钾,包括一些主題的包裝,由于Service沒有主題旱爆,所以直接繼承ContextWrapper舀射;但是Activity就需要繼承ContextThemeWrapper

二.? ? 異常如何產(chǎn)生

1.? ? 找到報錯的代碼

文件:

frameworks\base\core\java\android\app\ContextImpl.java

代碼:public void startActivity(Intent intent, Bundle options) {

warnIfCallingFromSystemProcess();

if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {

throw new 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);

}在下面的if條件判斷,如果不包含F(xiàn)LAG_ACTIVITY_NEW_TASK就會報這個錯誤if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {

...

}那么service.startActivity(Intent intent)怎么會調(diào)用這里來的呢怀伦?

要回答這個問題脆烟,我們分析下service.startActivity()做了什么,其實房待,service.startActivity調(diào)用的是ContextWrapper.startActivity()邢羔,因為service繼承自ContextWrapper

2.??代碼文件

frameworks\base\core\java\android\content\ContextWrapper.java

代碼:public void startActivity(Intent intent, Bundle options) {

mBase.startActivity(intent, options);

}ContextWrapper.startActivity的話,是直接調(diào)用的

mBase.startActivity(intent, options);

那么這個mBase是什么呢吴攒?又是什么時候賦值的呢张抄?其實mBase是在ContextWrapper的attachBaseContext的時候初始化的。如下:protected void attachBaseContext(Context base) {

if (mBase != null) {

throw new IllegalStateException("Base context already set");

}

mBase = base;

}那又是誰調(diào)用attachBaseContext的呢洼怔?

是在service創(chuàng)建的時候署惯,在ActivityThread里面調(diào)用,如下:

3. 代碼文件

frameworks\base\core\java\android\app\ActivityThread.java

代碼:private void handleCreateService(CreateServiceData data) {

LoadedApk packageInfo = getPackageInfoNoCheck(

data.info.applicationInfo, data.compatInfo);

Service service = null;

try {

java.lang.ClassLoader cl = packageInfo.getClassLoader();

service = (Service) cl.loadClass(data.info.name).newInstance();

} catch (Exception e) {

....

}

try {

if (localLOGV) Slog.v(TAG, "Creating service " + data.info.name);

ContextImpl context = ContextImpl.createAppContext(this, packageInfo);

context.setOuterContext(service);

Application app = packageInfo.makeApplication(false, mInstrumentation);

service.attach(context, this, data.info.name, data.token, app,

ActivityManagerNative.getDefault());

service.onCreate();

mServices.put(data.token, service);

....

} catch (Exception e) {

...

}

}抽出主要代碼分析ActivityThread. handleCreateService()方法里面主要做這幾件事

3.1 通過pms找到要啟動的Service配置信息镣隶,然后通過反射生成Service對象

3.2 創(chuàng)建ContextImpl對象极谊,然后調(diào)用service.attach方法設置到ContextWrapper.java的mBaseContext變量里面。

那現(xiàn)在就明白了安岂,service.startActivity()->ContextWrapper.startActivity()->ContextImpl.startActivity()

然后再ContextImpl.startActivity里面會檢查Intent的參數(shù)是否包含F(xiàn)LAG_ACTIVITY_NEW_TASK轻猖,從而出現(xiàn)這個異常。

三.? ? 解決這個異常后會出現(xiàn)問題域那?

有些同學就會說了咙边,在Service里面啟動Activity必須要有FLAG_ACTIVITY_NEW_TASK參數(shù),那么我們添加上不就可以了?如下:

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

那么這樣會帶來什么問題呢败许?

這樣帶來的問題就是在最近任務列表里面會出現(xiàn)兩個相同的應用程序王带,比如你是在電話本里面啟動的,那么最近任務列表就會出現(xiàn)兩個電話本市殷;因為有兩個Task嘛愕撰!

那怎么解決呢?其實也非常好解決醋寝,只要在新的Task里面的Activity里面配置android:excludeFromRecents="true"就可以了搞挣。表示這個Activity不會顯示在最近列表里面。

四.? ? Activity.startActivity()為什么不出現(xiàn)這個異常呢音羞?

要回答這個問題囱桨,需要看下Activity.startActivity()調(diào)用到哪里去了

代碼文件:

frameworks\base\core\java\android\app\Activity.java

代碼:public void startActivity(Intent intent) {

this.startActivity(intent, null);

}接下來會調(diào)用startActivityForResult()->然后一路調(diào)用到Ams去啟動Activity;

原來如此,Activity重寫了startActivity()方法...

五.? ? Android 為什么要這么設計?

那現(xiàn)在來回答這個問題黄选,為什么Android在Service 里面啟動Activity要強制規(guī)定使用參數(shù)FLAG_ACTIVITY_NEW_TASK呢蝇摸?

我們可以來做這樣一個假設,我們有這樣一個需求:

我們在電話本里面啟動一個Service办陷,然后它執(zhí)行5分鐘后貌夕,啟動一個Activity

那么很有可能用戶在5分鐘后已經(jīng)不在電話本程序里面操作了,有可能去上網(wǎng)民镜,打開瀏覽器程序了啡专。

5分鐘后,此時當前的Task是瀏覽器的task制圈,那么彈出Activity们童,如果這個Activity在當前Task的話,也就是瀏覽器的Task鲸鹦;那么用戶就會覺得莫名其妙慧库;因為彈出的Activity和瀏覽器在一個Task,本來這個Activity應該屬于電話本的馋嗜。

所以齐板,對于Service而言,干脆強制定義啟動的Activity要創(chuàng)建一個新的Task.

這種設計葛菇,我覺得還是比較合理的甘磨。


借鑒自http://bbs.51cto.com/thread-1133875-1.html

最后編輯于
?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市眯停,隨后出現(xiàn)的幾起案子济舆,更是在濱河造成了極大的恐慌,老刑警劉巖莺债,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件滋觉,死亡現(xiàn)場離奇詭異签夭,居然都是意外死亡,警方通過查閱死者的電腦和手機椎瘟,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進店門覆致,熙熙樓的掌柜王于貴愁眉苦臉地迎上來侄旬,“玉大人肺蔚,你說我怎么就攤上這事±芨幔” “怎么了宣羊?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長汰蜘。 經(jīng)常有香客問我仇冯,道長,這世上最難降的妖魔是什么族操? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任苛坚,我火速辦了婚禮,結果婚禮上色难,老公的妹妹穿的比我還像新娘泼舱。我一直安慰自己,他們只是感情好枷莉,可當我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布娇昙。 她就那樣靜靜地躺著,像睡著了一般笤妙。 火紅的嫁衣襯著肌膚如雪冒掌。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天蹲盘,我揣著相機與錄音股毫,去河邊找鬼。 笑死召衔,一個胖子當著我的面吹牛铃诬,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播薄嫡,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼氧急,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了毫深?” 一聲冷哼從身側(cè)響起吩坝,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎哑蔫,沒想到半個月后钉寝,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體弧呐,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年嵌纲,在試婚紗的時候發(fā)現(xiàn)自己被綠了俘枫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡逮走,死狀恐怖鸠蚪,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情师溅,我是刑警寧澤茅信,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站墓臭,受9級特大地震影響蘸鲸,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜窿锉,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一酌摇、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧嗡载,春花似錦窑多、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至判沟,卻和暖如春耿芹,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背挪哄。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工吧秕, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人迹炼。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓砸彬,卻偏偏與公主長得像,于是被迫代替她去往敵國和親斯入。 傳聞我的和親對象是個殘疾皇子砂碉,可洞房花燭夜當晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容

  • Android插件化基礎的主要內(nèi)容包括 Android插件化基礎1-----加載SD上APKAndroid插件化基...
    隔壁老李頭閱讀 4,593評論 2 35
  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,116評論 25 707
  • 本文出自 Eddy Wiki ,轉(zhuǎn)載請注明出處:http://eddy.wiki/interview-androi...
    eddy_wiki閱讀 3,267評論 0 20
  • 昨日大年三十刻两,被母上來來回回的忙碌聲音給吵了起來增蹭,仔細回想原來今天已經(jīng)是大年三十了,要不是看到母上大人各種忙碌準備...
    喵柒閱讀 163評論 0 0
  • 從今天起我全程分享一套住房從毛坯到成品的全過程磅摹,我會以設計師的視角來給大家講講整個裝修分解過程及每個過程的一些注意...
    掌裝寶閱讀 238評論 0 0