- 《Android Service基础》
- 《Android Service回调和配置》
- 《Android Service aidl使用及进阶》
- 《Android Service更多知识》
- 《Android 中的 IntentService 类详解》
- 《Android Service aidl分析》
- 《Android Service 流程分析》
在介绍正文之前,我们先看看几个知识点进程、线程和Android中的进程间通讯(IPC)。
- 线程:线程时CPU调度的最小单元,同时线程也是一种有限的系统资源。
- 进程:进程一般表示一个执行单元,在PC和移动设备中一般可以表示一个程序或者应用(在一般情况下,一个应用程序是在同一进程中运行,但是也有可能一个应用有多个进程,比如:Android中可以在清单文件中为四大组件指定运行进程(process属性),而不是运行在默认进程。)。
- Android中的进程间通讯方式
- Bundle:通过Intent打开四大组件时,Bundle可以用来传递数据(四大组件之间传递,数据类型有限)
- 文件共享:通过共享文件传递数据(不适合高并发,且进程之间的数据传递实时性不强)
- AIDL方式:功能强大,支持并发和实时性,支持一对多传输(使用较为复杂)
- Messenger:AIDL的一种简化形式(不支持并发,请求串行处理,且为一对一,数据类型有限,和Bundle一样)
- ContentProvider:Android四大组件之一,强大的数据源处理能力,支持并发和一对多(主要用于对数据源的CURD操作)
- Socket:套接字,功能强大,通过网络传输字节流数据,支持一对多并发实时通讯(实现较为复杂,主要用于网络数据交换)
在前面我们已经说过了使用AIDL进行IPC通讯,这篇文章主要说明一下我们使用aidl后缀时,系统帮我们做了哪些事情。我们根据以下 IRemoteBookBinder.aidl
文件来做分析:
interface IRemoteBookBinder {
void addBookIn(in BookBean bookBean);
void addBookOut(out BookBean bookBean);
void addBookInOut(inout BookBean bookBean);
List<BookBean> getBookList();
}
我们创建了 IRemoteBookBinder.aidl
文件,重新 build
后,Android studio会帮我们自动生成 IRemoteBookBinder.java
文件,Android Studio自动生成的java文件路径在 app\build\generated\aidl_source_output_dir\debug\out\包名
中。文件内容比较长,这里就不直接将代码完全粘贴出来了,在下面分步说明。下图为 IRemoteBookBinder.java
类组织图:
强烈建议:在看具体分析时,结合Android Studio中自动生成的类来看,效果要好于查看贴出来的代码。
内部类 Default
public static class Default implements com.renj.service.aidl.IRemoteBookBinder {
@Override
public void addBookIn(com.renj.service.bean.BookBean bookBean) throws android.os.RemoteException {
}
@Override
public void addBookOut(com.renj.service.bean.BookBean bookBean) throws android.os.RemoteException {
}
@Override
public void addBookInOut(com.renj.service.bean.BookBean bookBean) throws android.os.RemoteException {
}
@Override
public java.util.List<com.renj.service.bean.BookBean> getBookList() throws android.os.RemoteException {
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
该类实现了 IRemoteBookBinder
类,作为IRemoteBookBinder
接口的默认实现,在实际中并无太多实际意义。
IRemoteBookBinder.aidl
文件中声明的方法
public void addBookIn(com.renj.service.bean.BookBean bookBean) throws android.os.RemoteException;
public void addBookOut(com.renj.service.bean.BookBean bookBean) throws android.os.RemoteException;
public void addBookInOut(com.renj.service.bean.BookBean bookBean) throws android.os.RemoteException;
public java.util.List<com.renj.service.bean.BookBean> getBookList() throws android.os.RemoteException;
这几个方法都是在 IRemoteBookBinder.aidl
进行了声明的,直接作为接口的声明方法,具体有子类实现。
内部类 Stub
public static abstract class Stub extends android.os.Binder implements com.renj.service.aidl.IRemoteBookBinder {
private static final java.lang.String DESCRIPTOR = "com.renj.service.aidl.IRemoteBookBinder";
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
public static com.renj.service.aidl.IRemoteBookBinder asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.renj.service.aidl.IRemoteBookBinder))) {
return ((com.renj.service.aidl.IRemoteBookBinder) iin);
}
return new com.renj.service.aidl.IRemoteBookBinder.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_addBookIn: {
data.enforceInterface(descriptor);
com.renj.service.bean.BookBean _arg0;
if ((0 != data.readInt())) {
_arg0 = com.renj.service.bean.BookBean.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBookIn(_arg0);
reply.writeNoException();
return true;
}
case TRANSACTION_addBookOut: {
data.enforceInterface(descriptor);
com.renj.service.bean.BookBean _arg0;
_arg0 = new com.renj.service.bean.BookBean();
this.addBookOut(_arg0);
reply.writeNoException();
if ((_arg0 != null)) {
reply.writeInt(1);
_arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE);
} else {
reply.writeInt(0);
}
return true;
}
case TRANSACTION_addBookInOut: {
// ...
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
java.util.List<com.renj.service.bean.BookBean> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.renj.service.aidl.IRemoteBookBinder {
// ...
}
static final int TRANSACTION_addBookIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBookOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_addBookInOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
public static boolean setDefaultImpl(com.renj.service.aidl.IRemoteBookBinder impl) {
if (Stub.Proxy.sDefaultImpl == null && impl != null) {
Stub.Proxy.sDefaultImpl = impl;
return true;
}
return false;
}
public static com.renj.service.aidl.IRemoteBookBinder getDefaultImpl() {
return Stub.Proxy.sDefaultImpl;
}
}
以上代码,去掉了 Proxy
内部类的实现,在后面再说,然后就是去掉了 TRANSACTION_addBookInOut
部分的实现,因为这部分就是TRANSACTION_addBookIn
和TRANSACTION_addBookOut
的结合。
Stub 类继承了 android.os.Binder 并且实现了 IRemoteBookBinder 接口,她就是真正的Binder类,是在Service中的onBind()
方法返回给客户端的Binder类(一般我们在onBind()方法中返回的都是 .Stub 的子类,如 RemoteBookBinderImpl extends IRemoteBookBinder.Stub
),也是客户端 ServiceConnection
回调方法 onServiceConnected()
中的参数 service
private ServiceConnection bindConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iRemoteBookBinder = IRemoteBookBinder.Stub.asInterface(service);
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
DESCRIPTOR
Binder 的唯一标识,一般使用当前类的全路径名表示,如上面的 "com.renj.service.aidl.IRemoteBookBinder"。
构造方法
将本身对象(this)和唯一标识(DESCRIPTOR)通过 attachInterface()
传递给父类 Binder
类,这里需要注意,后面会有地方用到。
public void attachInterface(@Nullable IInterface owner, @Nullable String descriptor) {
mOwner = owner;
mDescriptor = descriptor;
}
asInterface(android.os.IBinder obj)
将服务端的Binder对象转换成客户端所需要的AIDL接口对象, ServiceConnection
回调方法 onServiceConnected()
中就是使用这个方法对 service 参数进行转换。这个方法内部的转换是区分进程的,如果客户端和服务端在同一个进程,那么返回的就是服务端的Stub对象本身,否则返回的就是系统封装后的 IRemoteBookBinder.Stub.Proxy
类。注意创建 IRemoteBookBinder.Stub.Proxy(obj)
对象时,参数 obj ,就是 onBind()
方法返回的Binder对象,也就是回调 onServiceConnected()
方法中的参数 service。
在asInterface()
方法内部是通过 android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
方法判断客户端和服务端是否在同一进程的,queryLocalInterface()
方法在 Binder
中的实现:
public @Nullable IInterface queryLocalInterface(@NonNull String descriptor) {
if (mDescriptor != null && mDescriptor.equals(descriptor)) {
return mOwner;
}
return null;
}
上述方法中的 mDescriptor
和 mOwner
就是通过 Stub
的构造方法设置的值。
asBinder()
方法
该方法返回当前的类对象,也就是Binder
方法id
static final int TRANSACTION_addBookIn = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBookOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
static final int TRANSACTION_addBookInOut = (android.os.IBinder.FIRST_CALL_TRANSACTION + 2);
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 3);
这里就是aidl接口中的方法,为他们都声明一个id,用于在 onTransact()
方法中确定调用的是哪个方法。
onTransact()
方法
该方法运行在服务端的Binder线程池中,当客户端跨进程调用服务器方法时,远程请求会通过系统的封装(Proxy
)来调用该方法,然后由该方法根据方法id调用对应的方法,该方法的声明为:
boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
- code:调用的方法id
- data:传递进来的数据
- reply:需要返回的数据
- flags:额外标记,正常传0(1:单向RPC,无返回值)
- 返回值:Boolean类型,true表示调用成功,false表示调用失败,可以在这个方法内做权限控制。
我们在《Android Service aidl使用及进阶》说过,非基本数据类型的数据的走向标记in、out、inout的作用,这里再次进行说明一下,因为以下内容需要明白他们的作用才能更好的理解。
-
in:服务端只能接收输入,不能输出给客户端。当客户端传递BookBean给服务端时,服务端能获取到BookBean对象信息,但是服务端进行修改之后,客户端不会发生改变,还是原来的。
-
out:服务端不能接收输入,只能输出给客户端。当客户端传递BookBean给服务端时,服务端获取到的BookBean对象之后,并不能获取BookBean里面的字段信息(都是默认值),但是服务端修改了这些值后,客户端的信息会发生改变,变成服务端修改之后的值。
-
inout:服务端既能接收输入,也能输出给客户端。当客户端传递BookBean给服务端时,服务端能获取到BookBean对象信息;服务端修改了这些值后,客户端的信息也会发生改变,变成服务端修改之后的值。
下面对onTransact()
内对方法 addBookIn()
和 addBookOut()
方法具体说明:
-
TRANSACTION_addBookIn
表示调用的是
addBookIn()
方法,该方法在 IRemoteBookBinder.aidl 中的定义是void addBookIn(in BookBean bookBean);
,注意关键字in
;data.enforceInterface(descriptor); // 服务端标识,这个数据该由谁处理 com.renj.service.bean.BookBean _arg0; // 声明一个新的对象 if ((0 != data.readInt())) { // 判断data是否有数据需要读取 // 根据传递进来的data创建一个新的对象并赋值给 _arg0 _arg0 = com.renj.service.bean.BookBean.CREATOR.createFromParcel(data); } else { _arg0 = null; } this.addBookIn(_arg0); // 调用我们定义方法,具体由子类实现,将新的对象 _arg0 作为参数传递给方法 reply.writeNoException(); // 没有发生异常 return true; // 返回成功
当确定调用的
addBookIn()
方法,先将 Binder 的唯一标识对数据标记,然后服务器创建一个新的对象,因为使用了 in 关键字,所以需要读取老的数据,然后调用抽象addBookIn()
方法(由子类实现),并且将新建的对象作为参数传递,并没有将新的数据写入到reply
中,因为没有 out 关键字。 -
TRANSACTION_addBookOut
表示调用的是
addBookOut()
方法,该方法在 IRemoteBookBinder.aidl 中的定义是void addBookOut(out BookBean bookBean);
,注意关键字out
;data.enforceInterface(descriptor); // 服务端标识,这个数据该由谁处理 com.renj.service.bean.BookBean _arg0; // 声明一个新的对象 _arg0 = new com.renj.service.bean.BookBean(); // 因为只有out关键字,所以不会传递老的对象中的数据 this.addBookOut(_arg0); // 调用我们定义的方法,传递新的对象,所有字段都是默认值 reply.writeNoException(); // 没有发生异常 if ((_arg0 != null)) { reply.writeInt(1); // 当数据发送变化后,修改标记 // 把变化后的数据写入 reply 中 _arg0.writeToParcel(reply, android.os.Parcelable.PARCELABLE_WRITE_RETURN_VALUE); } else { reply.writeInt(0); } return true; // 返回成功
当确定调用的
addBookOut()
方法,先将 Binder 的唯一标识对数据标记,然后服务器创建一个新的对象,因为使用了 out 关键字,所以只会创建一个包含默认值的东西,而不会读取老的数据,然后调用抽象addBookOut()
方法(由子类实现),并且将新建的对象作为参数传递,方法调用完成后,就修改的数据写入 reply 中。对于IRemoteBookBinder.aidl 中的定义的
addBookInOut()
方法,也差不多。只是有 in 和 out 两个关键字,所以既能传入原始数据,也会返回修改后的数据。 -
TRANSACTION_getBookList
表示调用的是
getBookList()
方法,该方法在 IRemoteBookBinder.aidl 中的定义是List<BookBean> getBookList();
data.enforceInterface(descriptor); java.util.List<com.renj.service.bean.BookBean> _result = this.getBookList(); reply.writeNoException(); reply.writeTypedList(_result);
这个方法直接调用抽象
getBookList()
方法(由子类实现),然后将结果写入到 reply 中返回。
setDefaultImpl()
和 getDefaultImpl()
方法
setDefaultImpl()
方法是设置默认aidl接口实现,当远程调用 transact()
方法(实际最终调用的就是内部类 Stub 的 onTransact()
方法)中的对应方法,返回结果为false,并且通过 getDefaultImpl()
方法获取到的默认实现不为 null
就会调用默认实现的对应方法。
内部类 Stub 的内部类 Proxy
private static class Proxy implements com.renj.service.aidl.IRemoteBookBinder {
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 addBookInOut(com.renj.service.bean.BookBean bookBean) 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 ((bookBean != null)) {
_data.writeInt(1);
bookBean.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
boolean _status = mRemote.transact(Stub.TRANSACTION_addBookInOut, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
getDefaultImpl().addBookInOut(bookBean);
return;
}
_reply.readException();
if ((0 != _reply.readInt())) {
bookBean.readFromParcel(_reply);
}
} finally {
_reply.recycle();
_data.recycle();
}
}
@Override
public java.util.List<com.renj.service.bean.BookBean> 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.renj.service.bean.BookBean> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
if (!_status && getDefaultImpl() != null) {
return getDefaultImpl().getBookList();
}
_reply.readException();
_result = _reply.createTypedArrayList(com.renj.service.bean.BookBean.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
public static com.renj.service.aidl.IRemoteBookBinder sDefaultImpl;
}
首先使用 Proxy
的情况是因为我们在 Stub
的 asInterface()
方法中判断了客户端和服务端是不是在同一个进程,不是在同一个进程时,才返回 Proxy
对象。
构造方法
在解析 asInterface()
方法时,就说明了Proxy
的构造方法需要一个参数 obj
,就是 onBind()
方法返回的Binder对象,也就是回调 onServiceConnected()
方法中的参数 service。
asBinder()
和 getInterfaceDescriptor()
-
asBinder()
方法返回通过构造方法传递进来的 Binder 对象 -
getInterfaceDescriptor()
方法返回 Stub 的DESCRIPTOR
字段,Binder 的唯一标识。
方法调用
就是当我们在客户端通过 service 调用方法时,实际上调用的就是 Proxy 中的方法,理由通过前面的分析我们已经知道了(对asInterface(android.os.IBinder obj)
方法分析)。
对于 Stub
类,我们分析了 addBookIn()
和 addBookOut()
方法,那么在 Proxy
类中,我们分析一下 addBookInOut()
方法。
-
void addBookInOut(com.renj.service.bean.BookBean bookBean)
方法android.os.Parcel _data = android.os.Parcel.obtain(); // 创建参数对象 android.os.Parcel _reply = android.os.Parcel.obtain(); // 创建返回值对象 try { _data.writeInterfaceToken(DESCRIPTOR); // 唯一标识 if ((bookBean != null)) { // 参数不为null时,将客户端传递的参数数据写到新建对象中(因为关键字为 inout) _data.writeInt(1); bookBean.writeToParcel(_data, 0); } else { _data.writeInt(0); } // 通过 mRemote 调用 transact() 方法,传递调用方法id(Stub中定义的方法id),输入输出对象和标志位, // 这些参数就时 Stub#onTransact()方法所接收的参数 boolean _status = mRemote.transact(Stub.TRANSACTION_addBookInOut, _data, _reply, 0); // Stub#onTransact()方法返回失败,并且设置了默认aidl接口实现,就调用默认的实现 if (!_status && getDefaultImpl() != null) { getDefaultImpl().addBookInOut(bookBean); return; } _reply.readException(); // 因为有是 inout 关键字,所以需要将修改后的结果重写更新到客户端的对象中 if ((0 != _reply.readInt())) { bookBean.readFromParcel(_reply); } } finally { _reply.recycle(); _data.recycle(); }
上面的代码中,已经对代码做了注释了,我们发现在
Proxy
中的方法内,其实就是对参数和结果数据的处理,并没有其他的逻辑,然后直接调用了mRemote
的transact()
方法,mRemote
就是 Binder,这个我们是知道的,那看看Binder#transact()
方法做了什么操作了public final boolean transact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException { if (false) Log.v("Binder", "Transact: " + code + " to " + this); if (data != null) { data.setDataPosition(0); } // 调用了 onTransact 方法 boolean r = onTransact(code, data, reply, flags); if (reply != null) { reply.setDataPosition(0); } return r; }
在
Binder#transact()
中调用了onTransact()
方法,这个方法也就是我们刚刚在分析Stub
类时分析的方法。到了这里,我们就已经知道了客户端在跨进程调用服务端方法时的流程,以及 in、out和inout关键字作用的原理了。 -
java.util.List<com.renj.service.bean.BookBean> getBookList()
方法android.os.Parcel _data = android.os.Parcel.obtain(); android.os.Parcel _reply = android.os.Parcel.obtain(); java.util.List<com.renj.service.bean.BookBean> _result; try { _data.writeInterfaceToken(DESCRIPTOR); boolean _status = mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0); if (!_status && getDefaultImpl() != null) { return getDefaultImpl().getBookList(); } _reply.readException(); // 将返回 _reply 对象封装成客户端需要的类型 _result = _reply.createTypedArrayList(com.renj.service.bean.BookBean.CREATOR); } finally { _reply.recycle(); _data.recycle(); } return _result;
和
addBookInOut()
方法类似,封装参数,然后调用mRemote
的transact()
方法,最后将结果_reply
对象封装成客户端需要的类型返回。
网友评论