Android面试题-源码分析为什么service里面startActivity抛异常?activity不会

我们有时候需要在service里面启动activity,但是会发现报如下异常:

img

必须添加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的类继承关系图如下:

img

我们看到诸如Activity,Service等并没有直接继承Context,Activity继承了ContextThemeWrapper,Service而是继承了ContextWrapper;

现在从源码分析ContextWrapper的实现:

  1. @Override
  2. public void startActivity(Intent intent) {
  3. mBase.startActivity(intent);
  4. }

这个mBase是什么呢?这里我先直接告诉你,它的真正实现是ContextImpl类;至于为什么,有一条思路:在任意mBase打一个断点就能看到实现。

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

  1. @Override
  2. public void startActivity(Intent intent, Bundle options) {
  3. warnIfCallingFromSystemProcess();
  4. if ((intent.getFlags()&Intent.FLAG_ACTIVITY_NEW_TASK) == 0) {
  5. throw new AndroidRuntimeException(
  6. "Calling startActivity() from outside of an Activity "
  7. + " context requires the FLAG_ACTIVITY_NEW_TASK flag."
  8. + " Is this really what you want?");
  9. }
  10. mMainThread.getInstrumentation().execStartActivity(
  11. getOuterContext(), mMainThread.getApplicationThread(), null,
  12. (Activity) null, intent, -1, options);
  13. }

源码分析:

1)大家看看抛出来的异常是不是还是熟悉的味道。

2)通过判断可知当前的intent.getFlags是否带有FLAG_ACTIVITY_NEW_TASK这个标记,如果没有抛出异常,因为源码使用了&运算符,只有两个位都是1,结果才是1,所以可知service没有带FLAG_ACTIVITY_NEW_TASK标记,才抛出异常。

3)真正的startActivity使用了Instrumentation类的execStartActivity方法;继续跟踪:

  1. public ActivityResult execStartActivity(
  2. Context who, IBinder contextThread, IBinder token, Activity target,Intent intent, int requestCode, Bundle options) {
  3. ......
  4. try {
  5. ......
  6. int result = ActivityManagerNative.getDefault()
  7. .startActivity(whoThread, who.getBasePackageName(), intent,
  8. intent.resolveTypeIfNeeded(who.getContentResolver()),
  9. token, target != null ? target.mEmbeddedID : null,
  10. requestCode, 0, null, options);
  11. checkStartActivityResult(result, intent);
  12. } catch (RemoteException e) {
  13. throw new RuntimeException("Failure from system", e);
  14. }
  15. return null;

源码分析:

1)到这里我们发现真正调用的是ActivityManagerNative的startActivity方法;

Activity.startActivity源码分析

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

源码可知:

1)调用当前类的startActivity方法,代码如下:

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

源码可知

1)调用了startActivityForResult

  1. public void startActivityForResult(Intent intent, int requestCode, @Nullable Bundle options) {
  2. ......
  3. Instrumentation.ActivityResult ar =
  4. mInstrumentation.execStartActivity(
  5. this, mMainThread.getApplicationThread(), mToken, this,
  6. intent, requestCode, options);
  7. ......

源码可知

1)可以看到,其实通过Activity和ContextImpl类启动Activity并无本质不同,他们都通过Instrumentation这个辅助类调用到了ActivityManagerNative的方法。

2)只是Activity不会去检查标记,所以并不会抛出异常。

至此源码分析完毕。