上一篇文章我们详细介绍了AIDL的使用。如果对AIDL的使用还不熟悉的话,可以去看看我上一篇的博客,上一篇博客写到,我们通过自己写好的aidl文件,系统会自动帮我们生成文件。下面,我们根据系统生成的代码,来简单分析一下Binder的工作原理。
/*
* This file is auto-generated. DO NOT MODIFY.
* Original file: D:\\maguifeng\\android-code\\AIDLService\\app\\src\\main\\aidl\\com\\demo\\aidlservice\\IBookManager.aidl
*/
package com.demo.aidlservice;
// Declare any non-default types here with import statements
public interface IBookManager extends android.os.IInterface {
/**
* Local-side IPC implementation stub class.
*/
public static abstract class Stub extends android.os.Binder implements com.demo.aidlservice.IBookManager {
private static final java.lang.String DESCRIPTOR = "com.demo.aidlservice.IBookManager";
/**
* Construct the stub at attach it to the interface.
*/
public Stub() {
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.demo.aidlservice.IBookManager interface,
* generating a proxy if needed.
*/
public static com.demo.aidlservice.IBookManager asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.demo.aidlservice.IBookManager))) {
return ((com.demo.aidlservice.IBookManager) iin);
}
return new com.demo.aidlservice.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 {
java.lang.String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
java.util.List<com.demo.aidlservice.Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(descriptor);
com.demo.aidlservice.Book _arg0;
if ((0 != data.readInt())) {
_arg0 = com.demo.aidlservice.Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy implements com.demo.aidlservice.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;
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
@Override
public java.util.List<com.demo.aidlservice.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.demo.aidlservice.Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(com.demo.aidlservice.Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(com.demo.aidlservice.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();
}
}
}
static final int TRANSACTION_getBookList = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_addBook = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
/**
* Demonstrates some basic types that you can use as parameters
* and return values in AIDL.
*/
public java.util.List<com.demo.aidlservice.Book> getBookList() throws android.os.RemoteException;
public void addBook(com.demo.aidlservice.Book book) throws android.os.RemoteException;
}
DESCRIPTOR
Binder唯一标识,一般用当前Binder的全路径包名来表示,例如上面的代码为:“com.demo.aidlservice.IBookManager”;
Stub()
Stub的构造方法,这个没什么好说的。
asInterface(android.os.IBinder obj)
将服务端的Binder对象转换成客户端所需要的AIDL接口类型的对象。此方便有两种返回值,如果是服务端和客户端处于同一个进程,那么返回的就是Stub本身。如果处于不同的进程,那么返回的是Stub.proxy对象
asBinder()
返回当前的Binder对象
onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags)
这个方法只有在多进程通信才会调用,同一个进程是不会调用。
这个方法运行在Binder的线程池中,当客户端发起请求时,远程请求会通过系统底层的一些封装来调用此方法。通过code来区分客户端请求的目标方法,data取出目标方法所需的参数,然后由reply写入返回值。onTransact的执行过程就是这样。注意:这个方法返回都是true,如果返回false,客户端请求会失败。因此,我们可以通过这个特性来做权限认证。如何做这个权限认证,这里不做详细的介绍。
Proxy&getBookList()
这个方法由客户端调用。内部的实现是:首先,会创建三个对象:输入Parcel对象_data,输出Parcel对象_reply,List对象_result。接着会将此方法的参数写入到_data中,然后调用transact方法,发起RPC远程调用请求,同时当前线程会被挂起。接着回调服务端onTransact方法。直到RPC过程完成后,当前线程继续执行,从_reply中取出RPC返回值。最后返回_reply的结果。
Proxy&addBook(Book book)
这个方法由客户端调用。实现的流程与getBookList()一样。不同的一点是,此方法并不需要从_reply取出返回值,所以没有返回值。
通过上面的分析,我们应该对Binder的工作机制有一个大概的了解。此外,还有两点需要注意:
1.如果远程请求耗时,不能在UI线程去发起远程请求
2.Binder方法应采取同步的方式去实现
为了更好的了解Binder的工作机制,画了Binder的工作机制图,相信会对Binder的工作机制会有更深的理解。
从上面的分析来看,我们可以不提供aidl文件来实现Binder.我们可以根据系统生成的文件,手动写一个Binder.
根据上面的思想, 手动实现一个Binder可以通过如下步骤来完成:
(1)定义一个接口继承IInterface接口。这个接口实现如下:
package com.demo.aidlservice2;
import android.os.IBinder;
import android.os.IInterface;
import android.os.RemoteException;
import java.util.List;
/**
*
*
* Created by MarvinMa
* Time : 2019/5/10-17:20
* Desc :
*
*/
public interface IBookManager extends IInterface {
String DESCRIPTOR = "com.demo.aidlservice2.IBookManager";
int TRANSACTION_getBookList = (IBinder.FIRST_CALL_TRANSACTION + 0);
int TRANSACTION_addBook = (IBinder.FIRST_CALL_TRANSACTION + 1);
List<Book> getBookList() throws RemoteException;
void addBook(Book book) throws RemoteException;
}
(2)定义一个类继承Binder并且实现上面定义的接口IBookManager
package com.demo.aidlservice2;
import android.os.Binder;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Parcel;
import android.os.RemoteException;
import java.util.List;
/**
*
*
* Created by MarvinMa
* Time : 2019/5/10-17:27
* Desc :
*
*/
public abstract class BookManagerImp extends Binder implements IBookManager {
public BookManagerImp() {
this.attachInterface(this, DESCRIPTOR);
}
public static IBookManager asInterface(IBinder obj) {
if ((obj == null)) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof IBookManager))) {
return ((IBookManager) iin);
}
return new BookManagerImp.Proxy(obj);
}
@Override
public IBinder asBinder() {
return this;
}
@Override
public boolean onTransact(int code, Parcel data,Parcel reply, int flags) throws RemoteException {
String descriptor = DESCRIPTOR;
switch (code) {
case INTERFACE_TRANSACTION: {
reply.writeString(descriptor);
return true;
}
case TRANSACTION_getBookList: {
data.enforceInterface(descriptor);
List<Book> _result = this.getBookList();
reply.writeNoException();
reply.writeTypedList(_result);
return true;
}
case TRANSACTION_addBook: {
data.enforceInterface(descriptor);
Book _arg0;
if ((0 != data.readInt())) {
_arg0 = Book.CREATOR.createFromParcel(data);
} else {
_arg0 = null;
}
this.addBook(_arg0);
reply.writeNoException();
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}
private static class Proxy extends BookManagerImp {
private IBinder mRemote;
Proxy(IBinder remote) {
mRemote = remote;
}
@Override
public IBinder asBinder() {
return mRemote;
}
public String getInterfaceDescriptor() {
return DESCRIPTOR;
}
@Override
public List<Book> getBookList() throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply = Parcel.obtain();
List<Book> _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
mRemote.transact(TRANSACTION_getBookList, _data, _reply, 0);
_reply.readException();
_result = _reply.createTypedArrayList(Book.CREATOR);
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
@Override
public void addBook(Book book) throws RemoteException {
Parcel _data = Parcel.obtain();
Parcel _reply =Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
if ((book != null)) {
_data.writeInt(1);
book.writeToParcel(_data, 0);
} else {
_data.writeInt(0);
}
mRemote.transact(TRANSACTION_addBook, _data, _reply, 0);
_reply.readException();
} finally {
_reply.recycle();
_data.recycle();
}
}
}
}
通过上面的代码可以发现,其实和系统生成的代码是并没有什么不同。手动去写的意义在于可以让我们更深的去理解Binder原理。所以建议大家,还是用aidl的方法自动生成代码。这样比较快捷高效,而且不容易出错。