android面試題-為什么service里面startActivity拋異常?activity不會


源碼分析相關面試題

Activity相關面試題

與XMPP相關面試題

與性能優化相關面試題

與登錄相關面試題

與開發相關面試題

與人事相關面試題

在service里面啟動activity,但是會發現報如下異常:

必須添加FLAG_ACTIVITY_NEW_TASK這個標記就可以了,那么為什么在activity里面不需要呢?接下來通過從源碼角度帶大家分析。

啟動activity有兩種形式

1)直接調用Context類的startActivity方法;這種方式啟動的Activity沒有Activity棧,因此不能以standard方式啟動,必須加上FLAG_ACTIVITY_NEW_TASK這個Flag,服務就是通過Context調用。

2)調用被Activity類重載過的startActivity方法,通常在我們的Activity中直接調用這個方法就是這種形式;

Context.startActivity源碼分析

我們查看Context類的startActivity方法,發現這竟然是一個抽象類;查看Context的類繼承關系圖如下:

我們看到諸如Activity,Service等並沒有直接繼承Context,Activity繼承了ContextThemeWrapper,Service而是繼承了ContextWrapper;

現在從源碼分析ContextWrapper的實現:

@Override
public void startActivity(Intent intent) {
mBase.startActivity(intent);
}

這個mBase是什么呢?這里我先直接告訴你,它的真正實現是ContextImpl類;至於為什么,有一條思路:在任意mBase打一個斷點就能看到實現。

Context.startActivity最終使用了ContextImpl里面的方法,代碼如下:

 @Override
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);
}

源碼分析:

1)大家看看拋出來的異常是不是還是熟悉的味道。

2)通過判斷可知當前的intent.getFlags是否帶有FLAG_ACTIVITY_NEW_TASK這個標記,如果沒有拋出異常,因為源碼使用了&運算符,只有兩個位都是1,結果才是1,所以可知service沒有帶FLAG_ACTIVITY_NEW_TASK標記,才拋出異常。

3)真正的startActivity使用了Instrumentation類的execStartActivity方法;繼續跟蹤:

 public ActivityResult execStartActivity(
Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {
......
try {
......
int result = ActivityManagerNative.getDefault()
.startActivity(whoThread, who.getBasePackageName(), intent,
intent.resolveTypeIfNeeded(who.getContentResolver()),
token, target != null ? target.mEmbeddedID : null,
requestCode, 0, null, options);
checkStartActivityResult(result, intent);
} catch (RemoteException e) {
throw new RuntimeException("Failure from system", e);
}
return null;

源碼分析:

1)到這里我們發現真正調用的是ActivityManagerNative的startActivity方法;

Activity.startActivity源碼分析

@Override
public void startActivity(Intent intent) {
this.startActivity(intent, null);
}

源碼可知:

1)調用當前類的startActivity方法,代碼如下:

@Override
public void startActivity(Intent intent, @Nullable Bundle options) {
if (options != null) {
startActivityForResult(intent, -1, options);
} else {
// Note we want to go through this call for compatibility with
// applications that may have overridden the method.
startActivityForResult(intent, -1);
}
}

源碼可知

1)調用了startActivityForResult

public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
......
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
......

源碼可知

1)可以看到,其實通過Activity和ContextImpl類啟動Activity並無本質不同,他們都通過Instrumentation這個輔助類調用到了ActivityManagerNative的方法。

2)只是Activity不會去檢查標記,所以並不會拋出異常。

至此源碼分析完畢。

  • 歡迎關注微信公眾號,長期推薦技術文章和技術視頻

  • 微信公眾號名稱:Android干貨程序員


注意!

本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。



 
粤ICP备14056181号  © 2014-2021 ITdaan.com