Android多进程之Binder的使用

简介:

Binder是什么

  • Binder是Android的一个类,实现了IBinder接口
  • 从IPC角度来说,Binder是Android中的一种跨进程通信方式
  • Binder还可以理解为一种虚拟的物理设备,设备驱动是/dev/binder,Linux中没有
  • 从Android Framework角度来说,Binder是ServiceManger连接各种Manger(ActivityManager、WindowManager等)和相应的ManagerService的桥梁
  • 从Android应用层来说,Binder是客户端和服务端进行通信的媒介,当bindService的时候会返回服务端的Binder对象,通过这个Binder对象可以调用服务端的服务

使用Binder

生成Binder类
  • 可以通过2中方式生成Binder:通过AIDL文件让系统自动生成;我们自己手动编写Binder
通过AIDL文件生成Binder
  • 首先需要准备一个Parcelable对象
public class Book implements Parcelable {

    public int bookId;
    public String bookName;

    public Book(int bookId, String bookName) {
        this.bookId = bookId;
        this.bookName = bookName;
    }

    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];
        }
    };

    protected Book(Parcel in) {
        bookId = in.readInt();
        bookName = in.readString();
    }

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(bookId);
        dest.writeString(bookName);
    }
}
  • 然后编写AIDL文件

新建AIDL文件,AS中会自动创建aidl的目录

// Book.aidl
package com.xxq2dream.aidl;

parcelable Book;
// IBookManager.aidl
package com.xxq2dream.aidl;

// Declare any non-default types here with import statements
//必须显示导入需要的Parcelable对象
import com.xxq2dream.aidl.Book;

//除了基本类型,其他参数都要标上方向,in表示输入型参数,out表示输出型参数,inout表示输入输出参数
interface IBookManager {
    List<Book> getBookList();
    void addBook(in Book book);
}

最好把AIDL相关的文件都放在一个目录下

  • 文件编写完成以后通过Make Project命令就可以生成对应的Binder类

通过make让系统自动生成Binder类

生成的Binder类

模拟客户端和服务端的进程间通信
  • 首先编写一个Service类,并设置android:process属性,开启在不同的进程
public class BookManagerService extends Service {
    private static final String TAG = "BookManagerService";

    private CopyOnWriteArrayList<Book> mBookList = new CopyOnWriteArrayList<Book>();

    private Binder mBinder = new BookManagerImpl(){
        @Override
        public List<Book> getBookList() throws RemoteException {
            Log.e(TAG, "getBookList-->"+ System.currentTimeMillis());
            return mBookList;
        }

        @Override
        public void addBook(Book book) throws RemoteException {
            Log.e(TAG, "addBook-->");
            mBookList.add(book);
        }
    };

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        Log.e(TAG, "onBind-->"+ System.currentTimeMillis());
        return mBinder;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        Log.e(TAG, "onCreate-->"+ System.currentTimeMillis());
        mBookList.add(new Book(1, "Android"));
        mBookList.add(new Book(2, "IOS"));
    }
}
  • 注册Service
//AndroidManifest.xml
<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="@string/app_name"
    android:roundIcon="@mipmap/ic_launcher_round"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>

    <service
        android:name="com.xxq2dream.service.BookManagerService"
        android:process=":remote" />
</application>

可以看到2个进程

  • 客户端的话就简单在activity中通过bindService方法绑定服务端,然后通过返回的Binder调用服务端的方法
public class MainActivity extends AppCompatActivity {
    private static final String TAG = "MainActivity";

    private ServiceConnection mConnection = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
            Log.e(TAG, "ServiceConnection-->"+ System.currentTimeMillis());
            //通过服务端回传的Binder得到客户端所需要的AIDL接口类型的对象,即我们上面的IBookManager
            IBookManager bookManager = BookManagerImpl.asInterface(iBinder);
            try {
                // 通过AIDL接口类型的对象bookManager调用服务端方法
                List<Book> list = bookManager.getBookList();
                Log.e(TAG, "query book list, list type:" + list.getClass().getCanonicalName());
                Log.e(TAG, "query book list:" + list.toString());
                Book newBook = new Book(3, "Android 进阶");
                bookManager.addBook(newBook);
                Log.e(TAG, "add book:" + newBook);
                List<Book> newList = bookManager.getBookList();
                Log.e(TAG, "query book list:" + newList.toString());

            } catch (RemoteException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onServiceDisconnected(ComponentName componentName) {
            Log.e(TAG, "binder died");
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Intent intent = new Intent(this, BookManagerService.class);
        //绑定服务,后面会回调onServiceConnected方法
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        //解绑服务
        unbindService(mConnection);
        super.onDestroy();
    }
}
  • 通过以上的几个步骤我们就实现了一个简单的进程间通信的例子

客户端进程日志
服务端进程日志

调用过程
  • 通过打印的日志我们可以大概分析上面的例子中各个方法的调用过程,这里我们分析下调用服务端获取书本列表的过程
  • 客户端调用bindService方法后,BookManagerService创建,调用Binder类的构造方法创建Binder
  • 然后BookManagerService的onBind方法将创建的Binder返回给客户端
  • 客户端的onServiceConnected方法被调用,然后调用Binder的asInterface方法得到AIDL接口类型的对象bookManager
  • 调用bookManager的getBookList方法实际上调用的是Binder类中的Proxy类对应的getBookList方法
  • 在Proxy类对应的getBookList方法中调用Binder的transact方法发起远程过程调用请求,同时当前线程挂起,服务端的onTransact方法会被调用
  • 服务端的onTransact方法被调用,通过code找到具体要调用的方法,这里是TRANSACTION_getBookList
  • 最后会调用BookManagerService中的mBinder对象对应的getBookList方法,将书籍列表mBookList返回,返回的结果在Parcel变量reply中
  • Proxy类中的getBookList方法通过result = reply.createTypedArrayList(Book.CREATOR);获取到服务端返回的数据
  • 客户端onServiceConnected方法中接收到数据,调用过程结束
需要注意的地方
  • 客户端调用远程请求时客户端当前的线程会被挂起,直到服务端进程返回数据,所以不能在UI线程中发起远程请求
  • 客户端的onServiceConnected和onServiceDisconnected方法都运行在UI线程中,不可以在里面直接调用服务端的耗时方法
  • 服务端的Binder方法运行在Binder线程池中,所以Binder方法不管是否耗时都应该采用同步的方式去实现
  • 同上面一点,服务端的Binder方法需要处理线程同步的问题,上面的例子中CopyOnWriteArrayList支持并发读写,自动处理了线程同步
  • AIDL中能够使用的List只有ArrayList,但AIDL支持的是抽象的List。因此虽然服务端返回的是CopyOnWriteArrayList,但是在Binder中会按照List的规范去访问数据并最终形成一个新的ArrayList传递给客户端

结语

  • 以上只是Binder的简单应用,Binder的使用过程中还是有很多问题需要注意的
  • 比如Binder意外死亡以后怎么办?
  • 如何注册监听回调,当服务端有新消息后马上通知注册回调的客户端?如何解除注册?
  • 如何进行权限验证等

欢迎关注我的微信公众号,和我一起学习一起成长!
AntDream

目录
相关文章
|
11天前
|
安全 Java 定位技术
Android 浅度解析:AIDL & Binder (1)
Android 浅度解析:AIDL & Binder (1)
38 0
|
1月前
|
安全 Linux API
Android进程与线程
Android进程与线程
20 0
|
8月前
|
Java Linux Android开发
理解Android进程创建流程
理解Android进程创建流程
53 0
|
9月前
|
Java API Android开发
Android中Binder在项目中的具体使用详解
Android中Binder在项目中的具体使用详解
97 0
|
Unix Linux Android开发
Android C++系列:Linux进程间通信(二)
mmap可以把磁盘文件的一部分直接映射到内存,这样文件中的位置直接就有对应的内存 地址,对文件的读写可以直接用指针来做而不需要read/write函数。
73 0
|
Linux Android开发 C++
Android C++系列:Linux进程间通信(一)
每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不 到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用 户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这种机制称为进程 间通信(IPC,InterProcess Communication)。
59 0
|
Shell Linux Android开发
Android C++系列:Linux进程(三)
如果一个进程已经终止,但是它的父进程尚未调用wait或waitpid对它进行清理,这时 的进程状态称为僵尸(Zombie)进程。任何进程在刚终止时都是僵尸进程,正常情况下,僵 尸进程都立刻被父进程清理了,为了观察到僵尸进程
96 0
|
Linux Android开发 C++
Android C++系列:Linux进程(二)
用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支), 子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的 用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建 新进程,所以调用exec前后该进程的id并未改变。
139 0
|
Shell Linux C语言
Android C++系列:Linux进程(一)
我们知道,每个进程在内核中都有一个进程控制块(PCB)来维护进程相关的信 息,Linux内核的进程控制块是task_struct结构体。现在我们全面了解一下其中都有哪 些信息。
99 0
|
Linux 调度 Android开发
Android开启多进程及进程间通信的几种方式
开启多进程及进程间通信的几种方式
261 0

相关实验场景

更多