Binder
Binder的组成结构
Binder的架构如图所示
ServiceManager负责把Binder Server注册到一个容器中。
我们可以这样理解Client、Server 、ServiceManager、Binder Driver之间的关系:
把ServiceManager比喻成电话局,存储着每个住宅的座机电话。张三给李四打电话,拨打电话号码,会转接到电话局,电话局的接线员查询到这个号码的地址,因为李四的电话号码之间在电话局注册过,所以能够拨通;如果没有注册,就会提示该号码不存在。
张三就是Client,李四就是Server,电话局就是ServiceManager,而接线员就是Binder Driver。
Binder的通信过程
Binder的通信过程和交互路线如下图所示
-
Server 在 ServiceManager 这个容器中注册
-
Client 想要调用 Server 的 add 方法,就需要先获取 Server 对象, 但是 ServiceManager 不会把真正的 Server 对象返回给 Client,而是把 Server 的一个代理对象返回给 Client ,也就是 Proxy
-
Client 调用 Proxy 的 add 方法,Binder驱动会帮他去调用 Server 的 add 方法,并把结果返回给 Client 。
AIDL
创建 Book 类
在主包下面建一个 aidl 包专门存放进程通讯用到的数据, Book 类继承 Parcelable 接口(原因:AIDL 只能传送继承 Parcelable 接口的类)
package com.example.zs.ipcdemo.aidl;
import android.os.Parcel;
import android.os.Parcelable;
public class Book implements Parcelable {
private String bookId;
private String bookName;
public String getBookId() {
return bookId;
}
public void setBookId(String bookId) {
this.bookId = bookId;
}
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public static Creator<Book> getCREATOR() {
return CREATOR;
}
public Book(String bookId, String bookName) {
this.bookId = bookId;
this.bookName = bookName;
}
protected Book(Parcel in) {
bookId = in.readString();
bookName = in.readString();
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(bookId);
dest.writeString(bookName);
}
public void readFromParcel(Parcel in) {
bookId = in.readString();
bookName = in.readString();
}
@Override
public int describeContents() {
return 0;
}
public static final Creator<Book> CREATOR = new Creator<Book>() {
@Override
public Book createFromParcel(Parcel in) {
return new Book(in);
}
@Override
public Book[] newArray(int size) {
return new Book[size];
}
};
}
创建 Book.aidl 、IBookManager.aidl
为什么要有 Book.aidl 类,因为只有将类在 aidl 中声明时候,AIDL 才能调用 Book 类。所以说要让 AIDL 能够传送自定义类需要 1、继承 Parcelable 接口 2、创建同名 .aidl 文件声明自己。
package com.example.zs.ipcdemo.aidl;
parcelable Book;
IBookManager.aidl 设置让客户端允许调用的接口。
package com.example.zs.ipcdemo.aidl;
import com.example.zs.ipcdemo.aidl.Book;
interface IBookManager{
List<Book> getBookList();
void addBook(in Book book);
}
这里面有个地方要注意一下 addBook 方法中有 in 这个类型,下面我们简单说一下 AIDL 文件中 in 类型out 类型和 inout 数据的区别。
- in :客户端的参数输入:是把实参的值赋值给行参 那么对行参的修改,不会影响实参的值 。
- out :服务端的参数输入:传递以后,行参和实参都是同一个对象,只是他们名字不同而已,对行参的修改将影响实参的值。
- inout:这个可以叫输入输出参数,客户端可输入、服务端也可输入。客户端输入了参数到服务端后,服务端也可对该参数进行修改等,最后在客户端上得到的是服务端输出的参数。
我们手动 ReBuild 一下我们的项目系统在这个目录下app\build\generated\source\aidl\debug 为我们自动生成 IBookManger.java ,接下来让我们看一下 IBookManger.java 的庐山真面目。
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: E:\\GitHub\\IPCDemo\\app\\src\\main\\aidl\\com\\example\\zs\\ipcdemo\\aidl\\IBookManager.aidl
*/
package com.example.zs.ipcdemo.aidl;
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.example.zs.ipcdemo.aidl.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.example.zs.ipcdemo.aidl.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.example.zs.ipcdemo.aidl.IBookManager interface,
* generating a proxy if needed.
*/
public static com.example.zs.ipcdemo.aidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.example.zs.ipcdemo.aidl.IBookManager))) {
return ((com.example.zs.ipcdemo.aidl.IBookManager) iin);
}
return new com.example.zs.ipcdemo.aidl.IBookManager.Stub.Proxy(obj);
}
@Override
public android.os.IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
java.util.List<com.example.zs.ipcdemo.aidl.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.example.zs.ipcdemo.aidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.zs.ipcdemo.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
private static class Proxy implements com.example.zs.ipcdemo.aidl.IBookManager {
private android.os.IBinder mRemote;
Proxy(android.os.IBinder remote) {
mRemote = remote;
}
@Override
public android.os.IBinder asBinder() {
return mRemote;
}
public java.lang.String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public java.util.List<com.example.zs.ipcdemo.aidl.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.zs.ipcdemo.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.example.zs.ipcdemo.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(com.example.zs.ipcdemo.aidl.Book book) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(Stub.TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void registerListener(com.example.zs.ipcdemo.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_registerListener, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public void unregisterListener(com.example.zs.ipcdemo.aidl.IOnNewBookArrivedListener listener) throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeStrongBinder((((listener != null)) ? (listener.asBinder()) : (null)));
mRemote.transact(Stub.TRANSACTION_unregisterListener, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public java.util.List<com.example.zs.ipcdemo.aidl.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.example.zs.ipcdemo.aidl.Book book) throws android.os.RemoteException;
}
刚看有点蒙逼,别急我们一步一步分析,IBookManager 文件中,包括 IBookManager.aidl 的两个接口,以及 Stub 和 Proxy 两个实现了 IBookManager 接口的类,其中 Stub 是定义在 IBookManager 接口中的,而 Proxy 则定义在 Stub 类中。
Client 执行流程
1、当服务连接建立完成后,客户端执行代码。IBookManager.Stub.asInterface(service)
这句代码的作用是判断传入的参数 IBinder
对象和自己是否在同一个进程如果不是则把这个IBinder
参数包装成一个 Proxy
对象,这时调用 Stub
的 getBookList()
方法,间接调用 Proxy
的 getBookList()
方法,看一下代码。
// 连接服务
private ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(final ComponentName name, IBinder service) {
// IBinder 通过 asInterface 判断
// asInterface方法的作用是判断参数——也就是IBinder对象,和自己是否在同一个进程:
// 是: 则直接转换、直接使用,接下来就跟 Binder 跨进程通信无关啦
// 否: 则把这个IBinder参数包装成一个 Proxy 对象,这时调用 Stub 的方法,间接调用Proxy的方法
IBookManager bookManager = IBookManager.Stub.asInterface(service);
try {
mBookManager = bookManager;
bookManager.getBookList();
Log.d(TAG, "get books count:" + bookManager.getBookList().size());
bookManager.registerListener(arrivedListener);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mBookManager = null;
}
};
public static com.example.zs.ipcdemo.aidl.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
// 作用是判断传入的参数 IBinder 对象和自己是否在同一个进程
if (((iin != null) && (iin instanceof com.example.zs.ipcdemo.aidl.IBookManager))) {
return ((com.example.zs.ipcdemo.aidl.IBookManager) iin);
}
return new com.example.zs.ipcdemo.aidl.IBookManager.Stub.Proxy(obj);
}
2、Proxy
在自己的 getBookList()
方法中,会使用 Parcelable
来准备数据,把函数名称、函数参数都写入 _data
,让 _reply
接收函数返回值。最后使用 IBinder
的 transact
() 方法,把数据就传给 Binder
的 Server
端了。
@Override
public java.util.List<com.example.zs.ipcdemo.aidl.Book> getBookList() throws android.os.RemoteException {
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.util.List<com.example.zs.ipcdemo.aidl.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
// 通知服务器调用该方法
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
/** 调用mRemote.transact()方法后,Binder驱动会挂起当前客户端线程,
* 并向服务端发送一个消息,这个消息就包括客户端传递的参数。
* 服务端接收到消息,解析数据执行完相关任务后会把执行结果写入reply中,
**/ 然后向Binder驱动发送一个notify消息,Binder驱动从挂起处唤醒客户端线程继续执行。
_reply.readException();
_result = _reply.createTypedArrayList(com.example.zs.ipcdemo.aidl.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
mRemote
指什么?
当一个Server Binder
被创建时其在Binder驱动中同时会创建一个mRemote
引用指向该Server。Client
端要访问Server
时首先要获取Server在Binder中的引用mRemote
,获取引用后就可以通过mRemote.transact()
方法向服务端发送消息。
transact
做了以下几件事
- 以线程间消息通信的模式向服务端发送客户端传递过来的参数。
- 挂起当前客户端线程,等待服务端线程执行完毕后的通知(notify)
- 接收服务端线程通知,继续执行客户端线程,并返回执行结果
Server 执行流程
Server 则是通过 onTransact() 方法接收 Client 进程传过来的数据,包括函数名称、函数参数,找到对应的函数,这里是 getBookList() ,把参数喂进去,得到结果,返回。所以 onTransact() 函数经历了读数据、执行要调用的函数、把执行结果再写数据的过程。
@Override
public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException {
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(DESCRIPTOR);
return true;
}
// 客户端调用指令 getBookList()
case TRANSACTION_getBookList: {
data.enforceInterface(DESCRIPTOR);
// 调用本地服务 getBookList() 方法
java.util.List<com.example.zs.ipcdemo.aidl.Book> _result = this.getBookList();
reply.writeNoException();
// 将数据传给客户端
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(DESCRIPTOR);
com.example.zs.ipcdemo.aidl.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.example.zs.ipcdemo.aidl.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
return super.onTransact(code, data, reply, flags);
}
四大组件的工作原理
Activity的工作原理
App是怎样启动的
在手机屏幕上点击某个App的图标,假设是斗鱼app,这个APP的首页面就会展示在我们面前,看似简单的操作,背后经历了Activity和AMS的反反复复的通信过程。首选要搞清楚手机屏幕是一个Activity,这个Activity所在的app,业界称之为Launcher,Launcher是各大手机厂商提供的。
Launcher为每个app的图标提供了启动这个app所需要的Intent信息,如:包名、首页面地址、category等等,这些信息是app安装的时候,PackageManagerService
从各个apk包中的AndroidManifest
文件中读取到的。
启动流程
①Launcher通知AMS,要启动斗鱼app,而且指定了启动app的哪个页面
②AMS收到消息后,把要启动的页面记录下来
③Launcher当前页面进入Paused状态,然后通知AMS,“你去找对应的app吧”
④AMS检查斗鱼app是否已经启动了,是,则唤起斗鱼app,否,就要启动一个新的进程,AMS所在的新进程中创建一个ActivityThread对象,启动相对应的main函数
⑤app启动后,通知AMS,“我启动好了”
⑥AMS翻出之前记录下的值(要启动的页面),告诉斗鱼app,启动这个页面
⑦斗鱼app启动首页,创建Context并与首页Activity关联,然后调用启动页面的Activity的onCreate函数
流程至此完成,分为两部分,1~3 阶段:Launcher和AMS相互通信,4~7阶段:斗鱼app与AMS相互通信
第一阶段:Launcher通知AMS
上图就是点击Launcher上的app之后,会调用Launcher的startActivitySafely方法,其实还是会调用Activity的startActivity方法,intent中带着要启动app所需要的关键信息,如:启动的页面类名等等,所以为什么要在Manifest中,给首页面指定的action和category了,就是在启动的时候Launcher会找到这个关键信息进行启动
启动app时候,调来调去最终会调用startActivityForResult方法,startActivityForResult的代码如下:
public void startActivityForResult(@RequiresPermission Intent intent, int requestCode,
@Nullable Bundle options) {
if (mParent == null) {
options = transferSpringboardActivityOptions(options);
Instrumentation.ActivityResult ar =
mInstrumentation.execStartActivity(
this, mMainThread.getApplicationThread(), mToken, this,
intent, requestCode, options);
if (ar != null) {
mMainThread.sendActivityResult(
mToken, mEmbeddedID, requestCode, ar.getResultCode(),
ar.getResultData());
}
if (requestCode >= 0) {
mStartedActivity = true;
}
cancelInputsAndStartExitTransition(options);
} else {
if (options != null) {
mParent.startActivityFromChild(this, intent, requestCode, options);
} else {
mParent.startActivityFromChild(this, intent, requestCode);
}
}
}
在以上代码中,我们发现了一个mMainThread变量,这是一个
ActivityThread
类型的变量,就是主线程,也就是UI线程,它代表了整个app,读者不禁要问,ActivityThread
代表了App应用程序,那么Application
类代表什么?其实,Application
对于android系统来说并没有那么重要,它只是个上下文,Application
是app的上下文,Context
是Activity
的上下文。
通过
ActivityThread
的getApplicationThread
方法取到一个Binder
对象,这个对象类型为ApplicationThread
,代表了launcher
所在的app进程,mToken
也是一个Binder
对象,代表launcher
这个Activity,也通过Instumentation
传给AMS,AMS查询记录就知道是谁向AMS发送请求了。
查看
mInstrumentation.execStartActivity
方法,借助execStartActivity
,Activity会把数据传递给ActivityManagerNative
。
ServiceManager
是一个容器,AMN通过getDefault
方法,从ServiceManager中取得一个名为activity的对象,然后把它包装成一个ActivityManagerPoxy
对象(AMP),AMP就是AMS的代理对象。
AMN的getDefault方法返回类型为IActivityManager
,而不是AMP,AMN和AMP都实现了IActivityManager
接口,AMS继承了AMN。
第二阶段:AMS处理Launcher传过来的信息
1:Binder(也就是AMN和AMP)和AMS通信,肯定每次是做不同的事,比如这次Launcher要启动斗鱼app,那么会发送类型为START_ACTIVITY的请求给AMS,同时会告诉AMS要启动哪个Activity
2:AMS说:“好了,我知道了”,然后会检查AndroidManifest文件,检查是否存在要启动的Activity,如果不存在就抛出Activity not found异常信息
3:AMS通知Launcher:“没你什么事了,洗洗睡吧”,那么AMS是通过什么途径告诉Launcher的呢?结论是:AMS通过ApplicationThreadProxy发送消息,而App端则通过ApplicationThread来接受这个消息。
第三阶段:Launcher去休眠,然后通知AMS,“我休眠了
ApplicationThread(APT)接收到来自AMS的消息后,调用AcctivityThread的sendMessage方法,向Launcher的主线程消息队列发送PAUSE_ACTIVITY消息,发送消息是通过一个名为H的Handler类完成的,AMS给Activity发送的所有消息,以及给其他三大组件发送的消息,都是经过H类,既然都经过这条路,我们就可以从这里做插件化技术。
第四阶段:AMS启动新的进程
AMS接下里要启动斗鱼App的首页,因为此app不在后台进程中,所以要启动一个新的进程,这里调用的事Process.start方法,并且指定了ActivityThread的main函数为入口函数。
第五阶段:新进程启动,以ActivityThread的main函数为入口
启动新进程,就是启动一个新的app,为这个进程创建ActivityThread对象,这就是我们熟悉的UI主线程。创建好UI线程后,立刻进入ActivityThread的main函数,接下来要做两件具有重大意义的事:
-
①创建一个主线程Looper,也就是MainLooper,MainLooper就是在这里创建的
-
②创建Applicaition,Application也是在这里创建的
主线程在收到BIND_APPLICATION
消息后,根据传递过来的ApplicationInfo
创建一个对应的LoadedApk
对象(标志当前apk信息),然后创建ContextImpl
对象(标志当前进程环境),紧接着通过反射创建目标Application,并调用其attach方法,将ContextImpl对象设置为目标Applcation的上下文环境,最后调用Application的onCreate
函数,做一些初始化工作
App的灵魂是ActivityTread
,也就是主线程,app开发人员用不到,但使用反射是可以修改这个类的一些行为的。
创建App的最后就是要告诉AMS,“我启动好了”,同时把自己的ActivityThread
对象发送给AMS,AMS电话簿中就多了这个新的App登记信息,AMS以后就通过这个ActivityThread对象,向这个App发送消息。
第六阶段:AMS告诉新App要启动哪个Acctivity
AMS把传入的ActivityThread对象转为一个ApplicationThread对象,用于以后和这个App跨进程通信,在第六阶段AMS从过去记录中翻出来要启动哪个Activity,然后通过ATP告诉App
第七阶段:启动斗鱼首页Activity
在Binder的另一端,App通过APT接收到AMS的消息,仍然在H的handleMessage方法的swicth语句中处理,每次都是APT执行AcctivityThread的sendMessage方法,在这个方法中,把消息拼装一下,然后扔给H的swicth语句分析,来决定要执行ActivityThread的那个方法。
handleLaunchActivity方法要做的是哪些事?
①通过Instrumentati0on的newActivity方法,创建要启动的Activity实例。
②为这个Acctivity创建一个上下文Context对象,并与Activity进行关联
③通过Instrumentaction的callActivityOnCreate方法,执行Activity的onCreate方法,从而启动Activity,至此App启动完毕,这个流程是经过多次握手,App和AMS频繁的向对方发送消息,而发送消息的机制,是建立在Binder的基础之上的。
App内部的页面跳转
接下来我们看App内部页面的跳转。
从ActivityA跳转到ActivityB,其实可以把ActivityA看作是Launcher,那么这个跳转过程,和App的启动过程就很像了。
有了前面的分析基础,会发现,这个过程不需要重新启动一个新的进程,所以可以省略App启动过程中的一些步骤,流程简化为:
- ActivityA向AMS发送一个启动ActivityB的消息。
- AMS保存ActivityB的信息,然后通知App,你可以休眠了(onPaused)。
- ActivityA进入休眠,然后通知AMS,我休眠了。
- AMS发现ActivityB所在的进程就是ActivityA所在的进程,所以不需要重新启动新的进程,所以它就会通知App,启动ActivityB。
- App启动ActivityB。
以上的分析,仅限于ActivityA和ActivityB在相同的进程中,如果在Manifest中指定这两个Activity不在同一个进程中,那么就又是另一套流程了,但是整体流程大同小异。
Context家族史
Activity和Service都有Context,这三个类,还有Application,其实是亲戚一家子。
Activity因为有了一层Theme,所以中间有个ContextThemeWrapper,相当于它是Service和Application的侄子。
ContextWrapper只是一个包装类,没有任何具体的实现,真正的逻辑都在ContextImpl里面。
一个应用包含的Context个数:Service个数+Activity个数+1(Application类本身对应一个Context对象)。
应用程序中包含多个ContextImpl对象,而其内部变量mPackageInfo指向同一个PackageInfo对象。
Activity 和 Context的区别
我们就拿Activity举例子,看看Activity和Context的联系和区别。
我们知道,跳转到一个新的Activity要这么写:
我们还知道,也可以在Activity中使用getApplicationContext
方法获取Context上下文信息,然后使用Context 的startActivity方法,启动一个新的Activity:
这二者的区别是什么?
Activity中:
@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);
}
}
在Activity中无论是使用哪一种startActivity方法都会调用到Activity自身的方法,所以是一样的。
然而在其他的Context子类,例如ContextImpl.java中的实现,会检查有没有设置Flag:FLAG_ACTIVITY_NEW_TASK,否则会报错:
ContextImpl中:
@Override
public void startActivity(Intent intent, Bundle options) {
warnIfCallingFromSystemProcess();
// Calling start activity from outside an activity without FLAG_ACTIVITY_NEW_TASK is
// generally not allowed, except if the caller specifies the task id the activity should
// be launched in. A bug was existed between N and O-MR1 which allowed this to work. We
// maintain this for backwards compatibility.
final int targetSdkVersion = getApplicationInfo().targetSdkVersion;
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);
}