android中的binder机制

在android跨进程通信中binder必不可少,从Activty的启动源码就可以看到Activity的周期控制就是ActivityThread和AMS之间的binder跨进程通信,谈到binder机制就绕不过AIDL;下面开始介绍;

1.AIDL代码实例

2.AIDL源码

3.AIDL图解

1.AIDL代码实例

项目地址

首先要了解binder机制,b必然绕不开AIDL;那么接着看一个AIDL的简单实例

  • 1.service端:
public class IBookservice extends Service{

    private ArrayList<Book> books;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        books = new ArrayList<>();
        return binder;
    }

    //Stub
    IBinder binder = new IBookManager.Stub(){
        @Override
        public List<Book> getBookList() throws RemoteException {
            if (books.size() == 0){
                return null;
            }
            return books;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            books.add(book);
        }
    };
}
  • 2.client端
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    private int index;

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取的是一个代理对象 proxy
            iBookManager = IBookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };
    private IBookManager iBookManager;
    private TextView txt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        initView();
        bindService();
    }

    private void initView() {
        Button btn = findViewById(R.id.btn);
        txt = findViewById(R.id.txt);
        btn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        txt.setText("");
        Book book = new Book(String.valueOf(index), "java 基础");
        index++;
        try {
            iBookManager.addBook(book);
            List<Book> bookList = iBookManager.getBookList();
            for (int i = 0;i < bookList.size();i++){
                txt.append(bookList.get(i).toString() + "---");
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }


    }


    private void bindService() {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.example.ipcclient",
                "com.example.ipcclient.IBookservice"));
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
    }


}

以上是一个简单的AIDL实例,只粘贴主要逻辑代码;如果是初学者,可以看完整代码:
项目地址
大体就是服务端进行图书的存储.addBook(book)和图书列表获取.getBookList();然后客户端通过binder跨进程向service传参实现图书存储和获取图书列表(即跨进程调用service端的方法)

2.AIDL源码

看完上面的代码,对于读者应该有了解到android的binder就是跨进程通信的一种载体,即通过binder(即AIDL)可以实现不同的client跨进程调用service中相应方法的目的
那么我们紧接这来看源码;

2.1.service端和client通过service的bind来实现链接

service:通过onbind方法 返回binder

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        books = new ArrayList<>();
        return binder;
    }

    //Stub
    IBinder binder = new IBookManager.Stub(){
        @Override
        public List<Book> getBookList() throws RemoteException {
            if (books.size() == 0){
                return null;
            }
            return books;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            books.add(book);
        }
    };

client:通过binderService获取service传来的binder,通过操作binder实现跨进程

 private void bindService() {
        Intent intent = new Intent();
        intent.setComponent(new ComponentName("com.example.ipcclient",
                "com.example.ipcclient.IBookservice"));
        bindService(intent,conn, Context.BIND_AUTO_CREATE);
}

 ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取的是一个代理对象 proxy
            iBookManager = IBookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

这样通过andoroid–service的bind就实现了客户端和服务端的连接;

2.2AIDL源码

首先需要明确一点,service端和client端跨进程通讯的前提是共有的AIDL文件:
首先来看AIDL编译之后build/generated/source/aidl/debug下apt编译之后的binder实现源码:

package com.example.ipcclient;
public interface IBookManager extends android.os.IInterface
{

    public static abstract class Stub extends android.os.Binder implements com.example.ipcclient.IBookManager
    {
        private static final java.lang.String DESCRIPTOR = "com.example.ipcclient.IBookManager";

    public Stub()
    {
        this.attachInterface(this, DESCRIPTOR);
    }

    public static com.example.ipcclient.IBookManager asInterface(android.os.IBinder obj)
    {
        if ((obj==null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof com.example.ipcclient.IBookManager))) {
        return ((com.example.ipcclient.IBookManager)iin);
    }
        return new com.example.ipcclient.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
    {
        switch (code)
        {
            case INTERFACE_TRANSACTION:
            {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_getBookList:
            {
                data.enforceInterface(DESCRIPTOR);
                java.util.List<com.example.ipcclient.Book> _result = this.getBookList();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            case TRANSACTION_addBook:
            {
                data.enforceInterface(DESCRIPTOR);
                com.example.ipcclient.Book _arg0;
                if ((0!=data.readInt())) {
                    _arg0 = com.example.ipcclient.Book.CREATOR.createFromParcel(data);
                }
                else {
                    _arg0 = null;
                }
                this.addBook(_arg0);
                reply.writeNoException();
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }
        private static class Proxy implements com.example.ipcclient.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;
        }

        @Override public java.util.List<com.example.ipcclient.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.example.ipcclient.Book> _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                _reply.readException();
                _result = _reply.createTypedArrayList(com.example.ipcclient.Book.CREATOR);
            }
            finally {
                _reply.recycle();
                _data.recycle();
        }
            return _result;
        }
        @Override public void addBook(com.example.ipcclient.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);
        }

    public java.util.List<com.example.ipcclient.Book> getBookList() throws android.os.RemoteException;
    public void addBook(com.example.ipcclient.Book book) throws android.os.RemoteException;
}

IBookManager继承自IInterface,其中有两个重要的类 StubProxy ,Stub是一个继承自IBookManager的抽象类,但是并没有实现其方法,可以被子类化,用于进程内使用;ProxyStub的内部类,在跨进程通信中其中核心作用;

  • 首先在service中实例化化Stub,并且初始化其方法:
        @Override
        public List<Book> getBookList() throws RemoteException {
            if (books.size() == 0){
                return null;
            }
            return books;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            books.add(book);
        }
    };

作为binder传递给Client,Client通过bindservice可获取:

    ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            //获取的是一个代理对象 proxy
            iBookManager = IBookManager.Stub.asInterface(service);
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {

        }
    };

这里在ServiceConnection—>onServiceConnected(ComponentName name, IBinder service)获取service传递的binder,然后获取实例IBookManager.Stub.asInterface(service);

  public static com.example.ipcclient.IBookManager asInterface(android.os.IBinder obj)
    {
        if ((obj==null)) {
        return null;
    }
    android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
    if (((iin!=null)&&(iin instanceof com.example.ipcclient.IBookManager))) {
        return ((com.example.ipcclient.IBookManager)iin);
    }
        return new com.example.ipcclient.IBookManager.Stub.Proxy(obj);
    }

需要注意的是如果是进程内的通信我们直接使用Stub即可,但是跨进程就返回 Proxy ,所以我们 client 端操作的其实是本地的一个代理,我们继续看上面代码的返回:

return new com.example.ipcclient.IBookManager.Stub.Proxy(obj);


 Proxy(android.os.IBinder remote)
        {
            mRemote = remote;
        }

这里的obj就是service的Binder即Stub,到这里你就会明白,进一步讲Client操作的就是一个传入Service端Stub的本地代理类;
紧接着,我们调用.asInterface返回实例的.addBook:

@Override public void addBook(com.example.ipcclient.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();
                }
            }

这里逻辑就很明显了;首先

    android.os.Parcel _data = android.os.Parcel.obtain();
    android.os.Parcel _reply = android.os.Parcel.obtain();

其中_data是参数,_reply是结果;就是说当我addBook(“java jvm”),_data就是”java jvm”这个参数,_reply就是添加”java jvm”之后的图书list的result;由于addBook没有返回值,那么我们来看 getBookList()

@Override public java.util.List<com.example.ipcclient.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.example.ipcclient.Book> _result;
            try {
                _data.writeInterfaceToken(DESCRIPTOR);
                mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);
                _reply.readException();
                _result = _reply.createTypedArrayList(com.example.ipcclient.Book.CREATOR);
            }
            finally {
                _reply.recycle();
                _data.recycle();
        }
            return _result;
        }

这个方法就一目了然了;
上面代码的核心代码:

mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);

这里就是Service端的Stub,所以这里就是回调Service端Stub的transact方法,即Binder源码的transact方法:

  /**
     * Default implementation rewinds the parcels and calls onTransact.  On
     * the remote side, transact calls into the binder to do the IPC.
     */
    public final boolean transact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (false) Log.v("Binder", "Transact: " + code + " to " + this);

        if (data != null) {
            data.setDataPosition(0);
        }
        boolean r = onTransact(code, data, reply, flags);
        if (reply != null) {
            reply.setDataPosition(0);
        }
        return r;
    }

    protected boolean onTransact(int code, Parcel data, Parcel reply,
            int flags) throws RemoteException {
        if (code == INTERFACE_TRANSACTION) {
            reply.writeString(getInterfaceDescriptor());
            return true;
        } else if (code == DUMP_TRANSACTION) {
            ParcelFileDescriptor fd = data.readFileDescriptor();
            String[] args = data.readStringArray();
            if (fd != null) {
                try {
                    dump(fd.getFileDescriptor(), args);
                } finally {
                    IoUtils.closeQuietly(fd);
                }
            }
            // Write the StrictMode header.
            if (reply != null) {
                reply.writeNoException();
            } else {
                StrictMode.clearGatheredViolations();
            }
            return true;
        } else if (code == SHELL_COMMAND_TRANSACTION) {
            ParcelFileDescriptor in = data.readFileDescriptor();
            ParcelFileDescriptor out = data.readFileDescriptor();
            ParcelFileDescriptor err = data.readFileDescriptor();
            String[] args = data.readStringArray();
            ShellCallback shellCallback = ShellCallback.CREATOR.createFromParcel(data);
            ResultReceiver resultReceiver = ResultReceiver.CREATOR.createFromParcel(data);
            try {
                if (out != null) {
                    shellCommand(in != null ? in.getFileDescriptor() : null,
                            out.getFileDescriptor(),
                            err != null ? err.getFileDescriptor() : out.getFileDescriptor(),
                            args, shellCallback, resultReceiver);
                }
            } finally {
                IoUtils.closeQuietly(in);
                IoUtils.closeQuietly(out);
                IoUtils.closeQuietly(err);
                // Write the StrictMode header.
                if (reply != null) {
                    reply.writeNoException();
                } else {
                    StrictMode.clearGatheredViolations();
                }
            }
            return true;
        }
        return false;
    }

代码比较长,其实就是client调Service端的transact–>onTransact;由于Service端有重写nTransact,所以我们应该看最终的源码:

 @Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
    {
        switch (code)
        {
            case INTERFACE_TRANSACTION:
            {
                reply.writeString(DESCRIPTOR);
                return true;
            }
            case TRANSACTION_getBookList:
            {
                data.enforceInterface(DESCRIPTOR);
                java.util.List<com.example.ipcclient.Book> _result = this.getBookList();
                reply.writeNoException();
                reply.writeTypedList(_result);
                return true;
            }
            case TRANSACTION_addBook:
            {
                data.enforceInterface(DESCRIPTOR);
                com.example.ipcclient.Book _arg0;
                if ((0!=data.readInt())) {
                    _arg0 = com.example.ipcclient.Book.CREATOR.createFromParcel(data);
                }
                else {
                    _arg0 = null;
                }
                this.addBook(_arg0);
                reply.writeNoException();
                return true;
            }
        }
        return super.onTransact(code, data, reply, flags);
    }

到这里的OnTransact中使用一个责任链,通过swich来区别code,进行不同的操作,这里的code用来区分调用方法,即将_data传入相应方法,然后将结果result写入_reply,最后由Client本地代理中对应方法return或者结束操作;

到这里一整个跨进程的binder操作就结束了,作为梳理我们给出图解,继续往下看;

3.AIDL图解

这里写图片描述

这个图就是对binder机制的一个概括;
android binder机制深究还会有很多,暂时浅谈到此;
共同进步 : )

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值