AIDL 自动生成java代码——从分析内部类Stub、Proxy入手
前言
使用AIDL接口可以使得我们在调用其他进程的对象的方法,像调用自己进程本地对象的方法一样简单。本文将从AIDL接口文件自动生成的java文件来分析调用过程,虽然分析可能不够底层,但一定能对理解AIDL起到帮助。
代码
AIDL文件如下(IListener的接口文件不重要,这里它是个空接口都可以)
package com.java.prac;
import com.java.prac.IListener;
interface IService {
void registerListener(in IListener listener);
void unregisterListener(in IListener listener);
byte SerTestIn(in byte[] pa);
byte SerTestOut(out byte[] pa);
byte SerTestInout(inout byte[] pa);
}
对应自动生成的java文件放在后面了。
分析
首先对自动生成的java文件进行分析。不关注其内部类,单看IService接口的声明方法,它已经声明好了aidl文件里的5个方法。需要注意的是IService接口继承了IInterface接口。
IService interface
public interface IService extends android.os.IInterface
{
//忽略内部类
public void registerListener(com.java.prac.IListener listener) throws android.os.RemoteException;
public void unregisterListener(com.java.prac.IListener listener) throws android.os.RemoteException;
public byte SerTestIn(byte[] pa) throws android.os.RemoteException;
public byte SerTestOut(byte[] pa) throws android.os.RemoteException;
public byte SerTestInout(byte[] pa) throws android.os.RemoteException;
}
而IInterface接口的定义如下,IService接口并没有去实现asBinder方法,这说明实现asBinder的任务交给实现IService接口的派生类。这也暗示了asBinder方法在不同的派生类下有着不同的实现。
package android.os;
public interface IInterface
{
public abstract android.os.IBinder asBinder();
}
其次,IService接口发现有两个内部类,一个是IService$Stub,一个是IService$Stub$Proxy,两个内部类都implement了IService接口。但很明显,一个是用来在service所处的进程中继承并实现AIDL接口的(这个才是真正的方法实现),另一个是用来作为cilent来访问调用远程进程的service的方法的。
IService$Stub内部类
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.java.prac.IService
{
private static final java.lang.String DESCRIPTOR = "com.java.prac.IService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.java.prac.IService interface,
* generating a proxy if needed.
*/
public static com.java.prac.IService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.java.prac.IService))) {
return ((com.java.prac.IService)iin);
}
return new com.java.prac.IService.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{ //省略
case TRANSACTION_SerTestIn:
{
data.enforceInterface(descriptor);
byte[] _arg0;
_arg0 = data.createByteArray();
byte _result = this.SerTestIn(_arg0);
reply.writeNoException();
reply.writeByte(_result);
return true;
}
//省略
}
}
static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_SerTestIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_SerTestOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_SerTestInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
}
- stub类是一个抽象类,需要派生类才能生成对象。
- 这个类同时继承Binder并实现了IService方法,意味着这个类可以在Binder和IService之间强制转换类型。
- 从onTransact方法中可以看到,switch case的逻辑里是根据stub定义的常量来进入的,但是却没有找到谁调用了这个方法。
- switch case的逻辑里,调用方法最终调用的是
byte _result = this.SerTestIn(_arg0)
,这说明IService$Stub是用来在service所处的进程中继承并实现AIDL接口的。因为方法的真正实现在自己的派生类对象里,所以会调用this的方法。 - asBinder方法里返回this,因为自己的派生类对象将会是真正的service实现,所以就可以理直气壮地返回自己了。
- asInterface是一个static方法,是一个工具方法。这个方法根据IBinder参数来作判断。当返回
return ((com.java.prac.IService)iin)
时,说明IBinder参数是stub的派生类对象,Binder里对应的源码如下:
//frameworks/base/core/java/android/os/Binder.java
//这个函数属于Binder类
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
但mOwner的定义为private IInterface mOwner;
,它通过this.attachInterface(this, DESCRIPTOR);
来赋值给mOwner。
//frameworks/base/core/java/android/os/Binder.java
//这个函数属于Binder类
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
这样,一个stub派生类对象几经转手,最后以IService引用而存在着。这样做的好处是只会暴露IService的方法给外部。
而当返回return new com.java.prac.IService.Stub.Proxy(obj);
时,说明iin==null
了,一般是这种情况。这说明传进来的IBinder参数其对应的类也实现了queryLocalInterface
方法,但是却返回的是null。对应的源码如下(所以这种情况下的obj的实际类型为BinderProxy):
//frameworks/base/core/java/android/os/Binder.java
//这个函数属于BinderProxy类
public IInterface queryLocalInterface(String descriptor) {
return null;
}
- 总结一下就是,当调用方和service实现方处于同一个进程时,会返回
return ((com.java.prac.IService)iin);
;当调用方和service实现方处于不同进程时,会返回return new com.java.prac.IService.Stub.Proxy(obj);
。当然,这是因为asInterface方法传入参数的实际类型的不同导致的。
IService$Stub$Proxy内部类
private static class Proxy implements com.java.prac.IService
{
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 byte SerTestIn(byte[] pa) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
byte _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeByteArray(pa);
mRemote.transact(Stub.TRANSACTION_SerTestIn, _data, _reply, 0);
_reply.readException();
_result = _reply.readByte();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
//省略
}
- proxy已经不是抽象类了,可以不需要派生类,就直接生成对象了。
- 很明显,proxy的构造函数在stub里面的
return new com.java.prac.IService.Stub.Proxy(obj);
被调用到了。 - 由于proxy对象是在远程调用方使用的,所以asBinder方法就应该返回这个IBinder类型的mRemote成员(实际类型为BinderProxy)。
- 从switch case的逻辑看,调用
mRemote.transact
方法时的第一个参数,刚好可以被stub的switch逻辑用来处理各种case。transact
的各个实参刚好和onTransact
的形参对应上了。 mRemote.transact
方法会调用到transactNative(code, data, reply, flags);
即用JNI调用c++代码去,之后会通过系统调用进入内核空间(即用户态转到内核态),最后将服务实现方的线程唤醒,并调用相应函数。
系统图
从系统图中可以看出Android中设计模式的优良应用,IInterface接口中定义了一个方法asBinder可以返回IBinder类型,而IBinder接口中定义了一个queryLocalInterface方法可以返回IInterface类型。这两个方法刚好相反,使得IBinder类型和IInterface类型可以相互转换。
而为了可以相互转换,它们的派生类:Stub同时继承了IService和Binder;Proxy虽然只继承了IService,但拥有一个BinderProxy的成员。
自动生成的java代码
package com.java.prac;
public interface IService extends android.os.IInterface
{
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.java.prac.IService
{
private static final java.lang.String DESCRIPTOR = "com.java.prac.IService";
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.java.prac.IService interface,
* generating a proxy if needed.
*/
public static com.java.prac.IService asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.java.prac.IService))) {
return ((com.java.prac.IService)iin);
}
return new com.java.prac.IService.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
{
java.lang.String descriptor = DESCRIPTOR;
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
case TRANSACTION_registerListener:
{
data.enforceInterface(descriptor);
com.java.prac.IListener _arg0;
_arg0 = com.java.prac.IListener.Stub.asInterface(data.readStrongBinder());
this.registerListener(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_unregisterListener:
{
data.enforceInterface(descriptor);
com.java.prac.IListener _arg0;
_arg0 = com.java.prac.IListener.Stub.asInterface(data.readStrongBinder());
this.unregisterListener(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_SerTestIn:
{
data.enforceInterface(descriptor);
byte[] _arg0;
_arg0 = data.createByteArray();
byte _result = this.SerTestIn(_arg0);
reply.writeNoException();
reply.writeByte(_result);
return true;
}
case TRANSACTION_SerTestOut:
{
data.enforceInterface(descriptor);
byte[] _arg0;
int _arg0_length = data.readInt();
if ((_arg0_length<0)) {
_arg0 = null;
}
else {
_arg0 = new byte[_arg0_length];
}
byte _result = this.SerTestOut(_arg0);
reply.writeNoException();
reply.writeByte(_result);
reply.writeByteArray(_arg0);
return true;
}
case TRANSACTION_SerTestInout:
{
data.enforceInterface(descriptor);
byte[] _arg0;
_arg0 = data.createByteArray();
byte _result = this.SerTestInout(_arg0);
reply.writeNoException();
reply.writeByte(_result);
reply.writeByteArray(_arg0);
return true;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.java.prac.IService
{
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 void registerListener(com.java.prac.IListener 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.java.prac.IListener 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();
}
}
@Override public byte SerTestIn(byte[] pa) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
byte _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeByteArray(pa);
mRemote.transact(Stub.TRANSACTION_SerTestIn, _data, _reply, 0);
_reply.readException();
_result = _reply.readByte();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public byte SerTestOut(byte[] pa) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
byte _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((pa==null)) {
_data.writeInt(-1);
}
else {
_data.writeInt(pa.length);
}
mRemote.transact(Stub.TRANSACTION_SerTestOut, _data, _reply, 0);
_reply.readException();
_result = _reply.readByte();
_reply.readByteArray(pa);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override public byte SerTestInout(byte[] pa) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
byte _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeByteArray(pa);
mRemote.transact(Stub.TRANSACTION_SerTestInout, _data, _reply, 0);
_reply.readException();
_result = _reply.readByte();
_reply.readByteArray(pa);
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_registerListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_unregisterListener = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_SerTestIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_SerTestOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
static final int TRANSACTION_SerTestInout = (android.os.IBinder.FIRST_CALL_TRANSACTION + 4);
}
public void registerListener(com.java.prac.IListener listener) throws android.os.RemoteException;
public void unregisterListener(com.java.prac.IListener listener) throws android.os.RemoteException;
public byte SerTestIn(byte[] pa) throws android.os.RemoteException;
public byte SerTestOut(byte[] pa) throws android.os.RemoteException;
public byte SerTestInout(byte[] pa) throws android.os.RemoteException;
}