[Android]Binder跨进程通信理解

2018-09-26  本文已影响0人  黑森林中的小木屋

建议在看本文之前,先阅读文章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、调用结束,结果为:


image.png
上一篇下一篇

猜你喜欢

热点阅读