1. 一个应用中有多少个Context,它们之间的区别是什么?
Context数量=Activity数量+Service数量+Application
从图中我们知道:
- Context其实是一个抽象类,它有两个子类:
ContextImpl
、ContextWrapper
,ContextImpl
是context
的功能具体实现类,而ContextWrapper
则是一个包装类,主要是调用ContextImpl
里面的具体实现 ContextThemeWrapper
、Service
、Application
都继承自ContextWrapper
Activity
具有主题因此继承了ContextThemeWrapper
2. Service/广播中是否可以启动Activity?为什么?
在Activity中启动如下:
Intent intent=new Intent(this,TargetActivity);
startActivity(intent)
那么在Service中如何这么用会出现什么问题呢?
java.lang.RuntimeException: Error receiving broadcast Intent { act=com....}...Caused by: android.util.AndroidRuntimeException:
Calling startActivity() from outside of an Activity context requires the FLAG_ACTIVITY_NEW_TASK flag. Is this really what you want?
问题说是确少flags FLAG_ACTIVITY_NEW_TASK
那么加上
Intent intent=new Intent(this,TargetActivity);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
正常启动…
分析:
根据在Activity中startActivity的启动源码得知启动过程中并未对FALG进行检查
@Override
public void startActivityForResult(
String who, Intent intent, int requestCode, @Nullable Bundle options) {
Uri referrer = onProvideReferrer();
if (referrer != null) {
intent.putExtra(Intent.EXTRA_REFERRER, referrer);
}
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, who,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, who, requestCode,
ar.getResultCode(), ar.getResultData());
}
cancelInputsAndStartExitTransition(options);
}
而Service
、BroadcastReciver
中呢(ContextImpl
)
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
final int targetSdkVersion = getApplicationInfo().targetSdkVersion;
//[9.0系统源码]
//[6.0的时候只有这一项条件]如果没有addFlags FALG_ACTIVITY_NEW_TACK (intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0 为true
//[9.0新增](targetSdkVersion < Build.VERSION_CODES.N
// || targetSdkVersion >= Build.VERSION_CODES.P) 28 9.0=>版本<24 7.0
//[7.0新增] options != null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) false 因为 options=null 有bug 导致有的版本可绕过次异常
//[9.0修改为] options == null && ActivityOptions.fromBundle(options).getLaunchTaskId() == -1) false
if ((intent.getFlags() & Intent.FLAG_ACTIVITY_NEW_TASK) == 0
&& (targetSdkVersion < Build.VERSION_CODES.N
|| targetSdkVersion >= Build.VERSION_CODES.P)
&& (options == null
|| ActivityOptions.fromBundle(options).getLaunchTaskId() == -1)) {
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);
}
有此我们可知:在Android7.0、8.0因为options判断的bug导致可以绕过flag的检查,因此我们最好还是加上flag标记。
为什么要如何设计?
之所以将Activity与其他组件以不同的方式启动Activity,是因为如果在其他组件启动Activity时,此时任务栈是其他程序的任务栈,导致要启动的Activity在其他的程序的任务栈中了,因此需要创建新的Task.