看书《Android插件化开发指南》带来的思考
asInterface判断是否为同一个进程的依据
《Android插件化开发指南》中有一句话:
从 Client 看,对于 AIDL 的使用者,我们写程序:
MyAidl.Stub.asInterface(某IBinder对象).sum(1, 2);
asInterface 方法的作用是判断参数,也就是 IBinder 对象和自己是否在同一个进程··· ···
由此,想到了以下三个问题:
问题1:“某IBinder对象” 指的是谁?
答:从结果来看分为 同进程调用 和 跨进程调用 两种情况,以下面的代码段为例,
MyAidl.Stub.asInterface(mRemote).sum(1,2);
1)同进程调用
mRemote 为 MyAidl.Stub 的子类实例。比如说 mRemote 是 AMS 的实例,其是 IActivityManager.Stub 的子类。
2)跨进程调用
mRemote 为 android.os.BinderProxy 类型
那么,参数 mRemote 是怎么赋值的呢?
1、以 AMN.getDefault() 为例,其对应的 mRemote 为 IBinder b = ServiceManager.getService("activity")
//android.os.ServiceManager.java
private static HashMap<String, IBinder> sCache = new HashMap<String, IBinder>();
public static IBinder getService(String name) {
try {
IBinder service = sCache.get(name);
//getService("activity")走的是else,具体原因见下面的 1.1
if (service != null) {
return service;
} else {
//代码流程见 1.2
return getIServiceManager().getService(name);
}
} catch (RemoteException e) {
Log.e(TAG, "error in getService", e);
}
return null;
}
public static void initServiceCache(Map<String, IBinder> cache) {
if (sCache.size() != 0) {
throw new IllegalStateException("setServiceCache may only be called once");
}
sCache.putAll(cache);
}
1.1 getService("activity")走的是else,具体原因见下面
initServiceCache 的调用链如下所示:
1、
SystemServer.main
SystemService.run
SystemServer.createSystemContext
ActivityThread.attach(true)
AMS.attachApplication
AMS.attachApplicationLocked
ActivityThread.bindApplication
ServiceManager.initServiceCache
2、
ActivityThread.main
ActivityThread.attach(false)
AMS.attachApplication
AMS.attachApplicationLocked
ActivityThread.bindApplication
ServiceManager.initServiceCache
initServiceCache 的参数来源于 bindApplicaton 的参数 AMS.getCommonServiceLocked
///frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java
private HashMap<String, IBinder> getCommonServicesLocked(boolean isolated) {
// Isolated processes won't get this optimization, so that we don't
// violate the rules about which services they have access to.
if (isolated) {
if (mIsolatedAppBindArgs == null) {
mIsolatedAppBindArgs = new HashMap<>();
mIsolatedAppBindArgs.put("package", ServiceManager.getService("package"));
}
return mIsolatedAppBindArgs;
}
if (mAppBindArgs == null) {
mAppBindArgs = new HashMap<>();
// Setup the application init args
mAppBindArgs.put("package", ServiceManager.getService("package"));
mAppBindArgs.put("window", ServiceManager.getService("window"));
mAppBindArgs.put(Context.ALARM_SERVICE,
ServiceManager.getService(Context.ALARM_SERVICE));
}
return mAppBindArgs;
}
可见,sCache 中仅仅包含了 package、window、alarm服务,不包含 activity 服务,所以在 sCache 中找不到activity。如果不在 system_server 进程,package、window、alarm也是Binder代理,只不过是先保存起来了。
1.2 getIServiceManager().getService("activity")
返回一个远端 Binder 代理
ServiceManager.getService的流程分析
综上,我的理解是,
1)asInterface(某IBinder对象)中的 IBinder 对象,是通过 ServiceManager.getService("activity") 或者 ServiceManager.getService("package") 等等根据具体请求的服务类型而得到的;
2)当前进程为 system_server 进程时,ServiceManager.getService("activity") 直接返回 AMS 实例的引用(见system_service进程里 调用SystemManager.getService("activity") 直接返回ams的引用?)
3)当前进程不是 system_server 进程时,ServiceManager.getService("activity") 返回一个远程 Binder 代理
问题2:AMS,为什么不能直接实例化AMS然后调用其StartActivity方法呢,而是要调用 asInterface(某IBinder对象).startActivity 方法呢?
答:Android进程学习笔记:https://blog.csdn.net/aaqian1/article/details/109295579
代码段共享 指的是 “机器中有数个进程运行相同的一个程序,那么它们就可以使用相同的代码段 ”。但是 system_server 进程中的类(如AMS)可以理解为一个新的应用程序中的代码,所以并不属于共享的代码段,所以不能在自己的代码中直接实例化AMS,或者直接调用其中AMS中的静态方法和变量。
同时,根据问题1也可得知,与AMS同进程时,asInterface的参数为 AMS 的实例;与AMS跨进程时,asInterface的参数为一个远程Binder代理对象。
所以,引出了问题3。
问题3:asInterface()方法,是怎么判断出 AMS 是在不同的进程中的呢?
//android.app.ActivityManagerNative
private static final Singleton<IActivityManager> gDefault = new Singleton<IActivityManager>() {
protected IActivityManager create() {
IBinder b = ServiceManager.getService("activity");
if (false) {
Log.v("ActivityManager", "default service binder = " + b);
}
IActivityManager am = asInterface(b);
if (false) {
Log.v("ActivityManager", "default service = " + am);
}
return am;
}
};
static public IActivityManager asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IActivityManager in =
(IActivityManager)obj.queryLocalInterface(descriptor);
if (in != null) { //如果in不为null,说明是同一个进程;否则,不是同一个进程
return in;
}
return new ActivityManagerProxy(obj);
}
从上述代码可以看出,通过判断 ServiceManager.getService(“activity”).queryLocalInterface(descriptor) 即可判断出,是否在同一个进程中。
由问题1可知,同进程时,调用的是 (AMS实例).queryLocalInterface(descriptor) ;跨进程时,调用的是(远程Binder代理对象).queryLocalInterface(descriptor) 。
接下来将以VA为例,AMS将替换为VAMS
还是以下述代码段为例:
MyAidl.Stub.asInterface(mRemote).sum(1,2);
1)同进程调用
同进程时,mRemote 为 MyAidl.Stub 的子类实例
此时 mRemote.queryLocalInterface 方法来源于超类 android.os.Binder,参数来源于父类 MyAidl.Stub 中定义的 DESCRIPTOR。
//android.os.Binder.java
private IInterface mOwner;
private String mDescriptor;
public void attachInterface(IInterface owner, String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
public IInterface queryLocalInterface(String descriptor) {
if (mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
Binder 中的 attachInterface 方法是在 MyAidl.Stub 的构造函数中调用的。MyAidl.Stub 是 Binder 的子类。
Stub 构造函数是在 子类 VActivityManagerService 实例化时执行的,在VirtualApp中是在 BinderProvider 的 onCreate 方法中初始化这些服务的。
//MyAidl.Stub
public Stub(){
this.attachInterface(this, DESCRIPTOR); //子类实例化,父类的构造函数中的this指向子类对象
}
所以,同进程时,queryLocalInterface 方法的返回值为 VAMS 实例对象本身!
之前卡住我好几天的难点的一个点找到了,子类实例化时调用父类构造函数时,父类构造函数的this指向子类对象。参考:Java学习1-子类实例化时调用父类构造函数时this指向
下面是我自己写的一个例子:
public interface Stub {
public class People{
public People() {
System.out.println("People::constructor:::" + this);
}
}
class Student extends People{
public Student() {
System.out.println("Student::constructor:::" + this);
}
}
}
public class Test {
public static void main(String[] args) {
new Stub.Student();
}
}
运行Test.java,结果如下:
People::constructor:::leetcode.Stub$Student@4c203ea1
Student::constructor:::leetcode.Stub$Student@4c203ea1
2)跨进程调用
跨进程时,mRemote 为 android.os.BinderProxy 类型
BinderProxy 为 final 类,其 queryLocalInterface 方法直接返回 null 值:
public IInterface queryLocalInterface(String descriptor) {
return null;
}
所以 asInterface 方法判断出 AMS 与应用程序不在同一个进程 是依靠 asInterface 的参数 mRemote。
同进程时,mRemote 为 AMS 的实例,mRemote.queryLocalInterface(Descriptor) 返回的是 AMS 实例对象本身;跨进程时,mRemote 为 BinderProxy 类型,mRemote.queryLocalInterface(Descriptor) 返回null。