Android binder

3 篇文章 0 订阅
3 篇文章 0 订阅

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.aidl
package com.breezehan.ipc;

parcelable Book;

IBookManager.aidl

// IBookManager.aidl
package 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) {

    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值