Android binder

来源:互联网 发布:拆分系统源码下载 编辑:程序博客网 时间:2024/06/06 02:32

Binder在Android的重要性就不说了。看了很多文章,自己想总结一番,发现还是太菜,有那么多大牛已经说的很好,且都有系统性讲解。所以本篇参照《Android开发艺术探索》,重要记录下binder的使用,感受下其在IPC中的媒介作用,本篇作为笔记使用。

推荐文章

  1. 罗升阳Android进程间通信(IPC)机制Binder简要介绍和学习计划
  2. Gityuan Binder系列—开篇
  3. 图文详解 Android Binder跨进程通信的原理
  4. Android Binder 完全解析(一)概述

实在看不下去,就看第3篇吧。
这里借用一张图。
IPC-Binder

明白几点
1. 各个进程相互隔离,数据不可共享。此处指的时用户空间。
2. 每个进程可分为用户空间、内核空间。用户空间数据不可共享,内核空间可以。
3. 1/2/3步骤的虚线表示我们开发中感受到的流程,也是Android想实现的CS流程。实际过程并不能直接联系。
4. Client、Server、Service Manager在用户空间,Binder驱动在内核空间。
5. 用户可以实现的时应用层内容,Client、Server;Service Manager和Binder是Android平台已经实现好了的。

下面我们看下Android中怎样使用Binder实现IPC。
< Android开发艺术探索 >

直观来说,Binder是Android中的一个类,它实现了IBinder接口。从IPC角度来说,Binder是Android中的一种跨进程通信方式,Binder还可以理解为一种虚拟的物理设备,他的设备驱动是/dev/binder,该通信方式linux中没有;从Android Framework 角度来说,Binder是ServiceManager 连接各种Manager(ActivityManager、WindowManager等)和相应ManagerService的桥梁;从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当binderService的时候,服务端会返回一个包含了服务端业务调用的Binder对象,通过这个Binder对象,客户端就可以获取服务端提供的服务和数据,这里的服务包括普通服务和AIDL服务。

在Android开发中,Binder一般用在service,而一般的service的Binder并不涉及进程间通信,这里我们主要分析AIDL。

在Android studio 中创建aidl

AIDL是Android中定义的接口语言,用它能实现IPC跨进程通信。
studio中生成aidl还是很方便的,使用自动生成即可。
studio_aidl

之后,studio会自动为我们在main下创建一个aidl文件夹,里面的包名还是跟项目包名一致,看图。
aidl_book_manager

此时的IBookManager里面还没有东西,basicTypes只是一个举例,看英文解释(举例一些在AIDL中可以作为参数和返回值的基本类型),其实哪能就这些,还玩儿不玩儿了,可参考官方说明Android 接口定义语言 (AIDL)。

interface IBookManager {    /**     * Demonstrates some basic types that you can use as parameters     * and return values in AIDL.     */    void basicTypes(int anInt, long aLong, boolean aBoolean, float aFloat,            double aDouble, String aString);}

分别在aild的对象包名下面,创建Book.java、Book.aidl,修改IBookManager.aidl,代码如下。
Book

package com.breezehan.ipc;import android.os.Parcel;import android.os.Parcelable;public class Book implements Parcelable {    private int bookId;    private String bookName;    public Book(int bookId, String bookName) {        this.bookId = bookId;        this.bookName = bookName;    }    protected Book(Parcel in) {        bookId = in.readInt();        bookName = in.readString();    }    public static final Creator<Book> CREATOR = new Creator<Book>() {        @Override        public Book createFromParcel(Parcel in) {            return new Book(in);        }        @Override        public Book[] newArray(int size) {            return new Book[size];        }    };    @Override    public int describeContents() {        return 0;    }    @Override    public void writeToParcel(Parcel dest, int flags) {        dest.writeInt(bookId);        dest.writeString(bookName);    }}

Book.aidl

// Book.aidlpackage com.breezehan.ipc;parcelable Book;

IBookManager.aidl

// IBookManager.aidlpackage com.breezehan.ipc;import com.breezehan.ipc.Book;interface IBookManager {    List<Book> getBookList();    void addBook(in Book book);}

然后我们make project,就能在build中看到系统为我们生成的完整的IBookManager
build_aidl

注意,注意!有人说,毛线啊,我都报错了好不!
book引用

那是啊,没有错才有鬼呢。
因为默认我们的java文件都是在main->java下面的,你写在了adil下面,又没告诉studio,那肯定找不到了。
需要我们在module的build.gradle中设置。

android {    compileSdkVersion 25    buildToolsVersion "25.0.3"    defaultConfig {       ***    }    sourceSets {        main {            java.srcDirs = ['src/main/java', 'src/main/aidl']        }    } }

分析IBookManager

上面三个类Book实现parcelable接口,Book.aidl是因为adil的特殊情况需要添加;IBookManager是一个接口,此处虽然和Book在同一个包中,但是仍需import,也是aidl特殊之处。
系统会根据IBookManager中的信息(比如里面我们添加了两个方法)生成对应的Binder类,看一下build->generated->source->aidl->com.breezehan.ipc

/* * This file is auto-generated.  DO NOT MODIFY. * Original file: D:\\android_work\\workspace_studio\\AndroidKnowledgeSummary\\ipc\\src\\main\\aidl\\com\\breezehan\\ipc\\IBookManager.aidl */package com.breezehan.ipc;public interface IBookManager extends android.os.IInterface {    /**     * Local-side IPC implementation stub class.     */    public static abstract class Stub extends android.os.Binder implements com.breezehan.ipc.IBookManager {        // 1.Binder的唯一标识,一般使用当前类名表示,带包名。        private static final java.lang.String DESCRIPTOR = "com.breezehan.ipc.IBookManager";        /**         * Construct the stub at attach it to the interface.         */        public Stub() {            this.attachInterface(this, DESCRIPTOR);        }        // 2. 用于将服务端的Binder对象转换成客户端所需的AIDL接口类型的对象,这里转换时区分进程的,如果客户端和服务端处于同一进程,那么返回的就是Stub对象本身,否则返回的就是系统封装后的Stub.Proxy对象。        /**         * Cast an IBinder object into an com.breezehan.ipc.IBookManager interface,         * generating a proxy if needed.         */        public static com.breezehan.ipc.IBookManager asInterface(android.os.IBinder obj) {            if ((obj == null)) {                return null;            }            android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);            if (((iin != null) && (iin instanceof com.breezehan.ipc.IBookManager))) {                return ((com.breezehan.ipc.IBookManager) iin);            }            return new com.breezehan.ipc.IBookManager.Stub.Proxy(obj);        }        // 3.返回当前binder对象        @Override        public android.os.IBinder asBinder() {            return this;        }        // 4.这个方法运行在服务端的Binder线程池中,当客户端发起跨进程请求时,远程请求会被系统底层封装后交由此方法来处理。服务端通过code判断客户端请求的方法是哪个,接着从data取出目标方法所需参数(有参数的话),然后执行目标方法。当目标方法执行完毕后,就像reply中写入返回值(目标方法有返回值的话)。另外,onTransact有个boolean的返回值,当是false的时候,客户端的请求会失败,因此我们可以利用这个特性来做权限验证,毕竟我们也不希望随便一个进程就能调用我们的服务。        @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.breezehan.ipc.Book> _result = this.getBookList();                    reply.writeNoException();                    reply.writeTypedList(_result);                    return true;                }                case TRANSACTION_addBook: {                    data.enforceInterface(DESCRIPTOR);                    com.breezehan.ipc.Book _arg0;                    if ((0 != data.readInt())) {                        _arg0 = com.breezehan.ipc.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.breezehan.ipc.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;            }            // 5.这个方法运行在客户端,当客户端远程调用此方法时:首先创建本方法所需的输入型Parcel对象_data、输出型Parcel对象_reply以及返回值对象List;然后把该方法的参数信息写入_data中(如果有参数的话);接着调用transact方法发起RPC(远程过程调用)请求,同时当前线程挂起;然后服务端的onTransact方法调用,知道RPC过程返回后,当前线程继续执行,并从_reply中取出RPC过程的返回结果;然后返回_reply中的数据。            @Override            public java.util.List<com.breezehan.ipc.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.breezehan.ipc.Book> _result;                try {                    _data.writeInterfaceToken(DESCRIPTOR);                    mRemote.transact(Stub.TRANSACTION_getBookList, _data, _reply, 0);                    _reply.readException();                    _result = _reply.createTypedArrayList(com.breezehan.ipc.Book.CREATOR);                } finally {                    _reply.recycle();                    _data.recycle();                }                return _result;            }            @Override            public void addBook(com.breezehan.ipc.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.breezehan.ipc.Book> getBookList() throws android.os.RemoteException;    public void addBook(com.breezehan.ipc.Book book) throws android.os.RemoteException;}

上面代码是自动生成的,这里只是格式化了一下。IBookManager本身是一个接口,这里又继承了IInterface接口(IInterface里面只有一个asBinder方法),但是想要在Binder中传输的接口都需要继承IInterface。
1. 里面有两个方法getBookList和addBook,这是我们在IBookManager.aidl文件中声明的。
2. 同时此处声明了一个内部类Stub,它是一个Binder,跨进程时,通过代理proxy的transact,最终在onTransact中根据不同id(不同id对应IBookManager的方法)执行相应的不同方法;同一个进程时,直接执行IBookManager中的方法。

两点需要注意:
1. 客户端发起远程请求时,会挂起一直到服务端返回数据,所以如果一个远程方法时很耗时的,不要在UI线程中发起此远程请求;
2. 由于服务端的Binder方法已经在Binder线程池,所以不管Binder方法是否耗时都应该采取同步的方式,因为已经在一个线程中了。下面是Binder机制图(来自Android开发艺术探索):
binder工作机制

自己实现Binder

如果我们想要实现完整的IPC,需要一个Service

public class BookService extends Service{    private List<Book> mBookList = new ArrayList<>();    private IBinder binder = new IBookManager.Stub() {        @Override        public List<Book> getBookList() throws RemoteException {            return mBookList;        }        @Override        public void addBook(Book book) throws RemoteException {            mBookList.add(book);        }    };    @Nullable    @Override    public IBinder onBind(Intent intent) {        return binder;    }}

当然,清单文件中需配置

 <service            android:name=".BookService"            android:process=":remote" />

调用

    private ServiceConnection mServiceConnection = new ServiceConnection() {        @Override        public void onServiceConnected(ComponentName name, IBinder service) {            IBookManager iBookManager = IBookManager.Stub.asInterface(service);            try {                iBookManager.addBook(new Book(1, "Java编程思想"));                iBookManager.addBook(new Book(2, "Android开发艺术探索"));                List<Book> bookList = iBookManager.getBookList();                Log.d(TAG, "onServiceConnected:" + bookList.toString());            } catch (RemoteException e) {                e.printStackTrace();            }        }        @Override        public void onServiceDisconnected(ComponentName name) {        }    };    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        bindService(new Intent(this, BookService.class), mServiceConnection, Context.BIND_AUTO_CREATE);    }

上面即是一个完整的IPC过程。
使用AIDL,我们不用关心很多东西,系统会帮助规范生成一系列的内容,那么我们能不能不借助AIDL,直接使用binder做到IPC呢。
先看service中binder的使用

private IBinder binder = new IBookManager.Stub() {        @Override        public List<Book> getBookList() throws RemoteException {            return mBookList;        }        @Override        public void addBook(Book book) throws RemoteException {            mBookList.add(book);        }    };

所以我们可以实现一个Stub的对象,并且实现了IBookManager的方法;总体上,一个Stub,一个IBookManager就可以完事儿了。其他还一样,如Service的onBind。

手动实现Binder步骤
(1)、声明一个AIDL性质的接口,只需要继承IInterface即可。

public interface IUserManager extends IInterface {    public static final String DESCRIPTOR = "com.breezehan.ipc.IUserManager";    public final static int TRANSACTION_getUserList = IBinder.FIRST_CALL_TRANSACTION + 0;    public final static int TRANSACTION_addUser = IBinder.FIRST_CALL_TRANSACTION + 1;    public List<User> getUserList() throws RemoteException;    public void addUser(User user) throws RemoteException;}

看,是不是感觉像回事儿,当前此处 两个方法对象的id常量,我们仿照系统自动生成的写法,就是一个规则。无论多少方法,我们类似添加即可。
其中的public、static、final其实不明写,接口中这些都是默认的。
(2)实现Stub类,和Stub类中的Proxy代理类,不过此处写出来和系统生成几乎是一样的。

public class UserManagerImpl extends Binder implements IUserManager {    private List<User> implList = new ArrayList<>();    public UserManagerImpl() {        this.attachInterface(this, DESCRIPTOR);    }    public static IUserManager asInterface(IBinder obj) {        if (obj == null) {            return null;        }        IInterface iIn = obj.queryLocalInterface(DESCRIPTOR);        if (iIn != null && iIn instanceof IUserManager) {            return (IUserManager) iIn;        }        return new UserManagerImpl.Proxy(obj);    }    @Override    public List<User> getUserList() throws RemoteException {        //逻辑代码        return implList;    }    @Override    public void addUser(User user) throws RemoteException {        //逻辑代码        implList.add(user);    }    @Override    public IBinder asBinder() {        return this;    }    @Override    protected boolean onTransact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {        switch (code) {            case INTERFACE_TRANSACTION:                reply.writeString(DESCRIPTOR);                return true;            case TRANSACTION_addUser:                data.enforceInterface(DESCRIPTOR);                User _arg0;                if (0 != data.readInt()) {                    _arg0 = User.CREATOR.createFromParcel(data);                } else {                    _arg0 = null;                }                addUser(_arg0);                reply.writeNoException();                return true;            case TRANSACTION_getUserList:                data.enforceInterface(DESCRIPTOR);                List<User> _result = getUserList();                reply.writeNoException();                reply.writeTypedList(_result);                return true;        }        return super.onTransact(code, data, reply, flags);    }    static class Proxy implements IUserManager {        private IBinder mRemote;        public Proxy(IBinder remote) {            this.mRemote = remote;        }        public java.lang.String getInterfaceDescriptor() {            return DESCRIPTOR;        }        @Override        public List<User> getUserList() throws RemoteException {            Parcel _data = Parcel.obtain();            Parcel _reply = Parcel.obtain();            List<User> _result;            try {                _data.writeInterfaceToken(DESCRIPTOR);                mRemote.transact(TRANSACTION_getUserList, _data, _reply, 0);                _reply.readException();                _result = _reply.createTypedArrayList(User.CREATOR);            } finally {                _reply.recycle();                _data.recycle();            }            return _result;        }        @Override        public void addUser(User user) throws RemoteException {            Parcel _data = Parcel.obtain();            Parcel _reply = Parcel.obtain();            try {                _data.writeInterfaceToken(DESCRIPTOR);                if (user != null) {                    _data.writeInt(1);                    user.writeToParcel(_data, 0);                } else {                    _data.writeInt(0);                }                mRemote.transact(TRANSACTION_addUser, _data, _reply, 0);                _reply.readException();            } finally {                _reply.recycle();                _data.recycle();            }        }        @Override        public IBinder asBinder() {            return mRemote;        }    }}

完整代码参考手动实现Binder

结语

搞了半天,自己写的跟系统生成的差不多吗。手动去写,加深下Binder的理解,也知道AIDL并不是实现Binder的必须,只不过是系统的快速实现。原理都一样。
当Binder服务端因为某些原因异常终止,客户端会调用失败,最关键的是我们不知道两者连接是否已经断裂。这就需要给Binder设置一个死亡代理,当Binder停止时,客户端会收到一个通知,这时就可以做一些操作。

private IBookManager iBookManager;private IBinder.DeathRecipient deathRecipient = new IBinder.DeathRecipient() {    @Override    public void binderDied() {        if (iBookManager != null) {            iBookManager.asBinder().unlinkToDeath(deathRecipient, 0);            iBookManager = null;            //重新连接或者其他操作        }    }};private ServiceConnection mServiceConnection = new ServiceConnection() {    @Override    public void onServiceConnected(ComponentName name, IBinder service) {        iBookManager = IBookManager.Stub.asInterface(service);        try {            service.linkToDeath(deathRecipient, 0);            iBookManager.addBook(new Book(1, "Java编程思想"));            iBookManager.addBook(new Book(2, "Android开发艺术探索"));            List<Book> bookList = iBookManager.getBookList();            Log.d(TAG, "onServiceConnected:" + bookList.toString());        } catch (RemoteException e) {            e.printStackTrace();        }    }    @Override    public void onServiceDisconnected(ComponentName name) {    }};