建议在看本文之前,先阅读文章Binder学习指南
注:本文是在阅读上面文章后自己理解整理。以下内容皆为本人理解,不保证全部正确,请酌情参考
本文是不借助AIDL,自己手写的的跨进程通信模型
先上代码:
import android.os.*;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.Log;
public class Library extends Binder implements IBookManager {
public static String des = "a453826252.github.intercom.util.binder.Library";
public Library(){
this.attachInterface(this,des);//将自己注册到SM中
}
//执行在binder实体中
@Override
protected boolean onTransact(int code, @NonNull Parcel data, @Nullable Parcel reply, int flags) throws RemoteException {
try {
Thread.sleep(500);//验证client端在执行过程中会被吊起
}catch (Exception e){
}
Book book = Book.CREATOR.createFromParcel(data); //接收client端传入的参数
String msg = addBook(book);//调用本地方法
reply.writeString(msg); //写入返回值
reply.writeNoException();
return true;
}
public static class StubBinder{
public static IBookManager asInterface(IBinder iBinder){
if(iBinder == null){
return null;
}
IInterface iInterface = iBinder.queryLocalInterface(des);//根据des在SM中查找服务是否在client进程
if(iInterface != null && iInterface instanceof Library){
return (Library)iInterface;
}
//服务不在client进程中,IBinder对象为远端对象
return new StubBinderProxy(iBinder);
}
public static class StubBinderProxy implements IBookManager{
IBinder mRemote;
public StubBinderProxy(IBinder iBinder){
mRemote = iBinder; //代码能到这,说明肯定IBinder是远端对象
}
//运行在client端进程中,包装参数用的方法(可以理解为仓库的入口)
@Override
public String addBook(Book book) {
Parcel data = Parcel.obtain();
book.writeToParcel(data,0);
Parcel reply = Parcel.obtain();
//data.writeInterfaceToken(des);
try {
long currentTime = System.currentTimeMillis();
mRemote.transact(0,data,reply,0);//通过Binder驱动调用server进程中的Binder实体(远端对象)
Log.i("binderBook","take:"+(System.currentTimeMillis()-currentTime)+"mm");
return reply.readString(); //读取返回值
} catch (Exception e) {
e.printStackTrace();
return "sorry,i don't know";
}finally {
reply.recycle();
data.recycle();
}
}
@Override
public IBinder asBinder() {
return mRemote;
}
}
}
@Override
public IBinder asBinder() {
return this;
}
//在server端进程中,真正提供功能的方法(可以理解为仓库)
@Override
public String addBook(Book book) {
Log.i("binderBook",book.title+"|"+book.author);
Log.i("binderBook","addBookID:"+android.os.Process.myPid( ));
return "i have received";
}
}
import android.os.IInterface;
public interface IBookManager extends IInterface{
String addBook(Book book);
}
IBookManager
为一个接口,继承自IInterface
,声明服务端可以对外提供什么功能,本例中,只向外提供一个addBook
的功能(方法)
调用流程
1、在构造函数中向SM(ServerManager)注册,便于client端查找
2、client端拿到对象后需要调用asInterface()
方法确定该对象为实体对象还是代理对象
3、执行对象提供的addBook()
方法,获取返回值
上面第二步的代码为:
ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
IBookManager bookManager = Library.StubBinder.asInterface(service);//确定对象为实体对象还是代理对象
String msg = bookManager.addBook(new Book("title","auth"));//执行方法获取返回值
Log.i("binderBook",String.valueOf(msg));
}
@Override
public void onServiceDisconnected(ComponentName name) {
}
};
services代码(不是Server代码)
public class RemoteService extends Service {
@Override
public IBinder onBind(Intent intent) {
// TODO: Return the communication channel to the service.
return new Library();
}
}
第二、三步中,具体的调用流程是:
1、调用asInterface()
,根据标识符(des)查询Binder实体(服务对象)是否在本进程中,如果是,直接返回Binder实体,如果不是,则新建一个Binder代理对象。因为Binder代理对象也实现了IBookManager
接口,因此,代理对象中也有一个addBook()
方法,但是这个方法中只是将参数包装、序列化之后调用远端Binder实体的onTransact()
方法。当调用mRemote.transact(0,data,reply,0)
时,就已经在远端进程执行了,此时,client进程被挂起,直到远端执行完毕
2、onTransact()
方法根据code
参数确定要调用哪个方法(因为我就一个方法,就没有判断),然后从data
中获取参数,调用方法,将结果写入reply
中,返回。
3、回到client进程继续执行代码
Log.i("binderBook","take:"+(System.currentTimeMillis()-currentTime)+"mm");
return reply.readString(); //读取返回值
4、调用结束,结果为: