一 Binder概述
简单介绍下什么是 Binder。Binder 是一种进程间通信机制,基于开源的 OpenBinder 实现;OpenBinder 起初由 Be Inc. 开发,后由 Plam Inc. 接手。Hackborn加入谷歌后,他在OpenBinder 的基础上开发出了 Android Binder (以下简称 Binder),用来完成 Android 的进程通信。
1 为什么要学习Binder
作为一个Android开发者,其实我们每天都会和Binder打交道,只不过有时候我们不会注意到,譬如:
- startActivity的时候,传递的对象为什么需要序列化,是如何调用到AMS服务的startActivity方法的。
- bindService为什么回调的是一个Ibinder对象。
- 多进程应用,各个进程之间是如何通信的。
- AIDL是如何使用的。
- 应用程序是如何和系统进程进行通信的。
2 为什么选择Binder
我们知道在Linux已经提供了管道、消息队列、共享内存和Socket等IPC机制。Android系统是基于Linux内核的,那么为什么Android还要提供Binder来实现IPC呢?主要是基于性能、稳定性和安全性几个方面的原因。我们先来看看其他几种机制的优缺点然后和Binder进行对比。
2.1 管道
2.2 消息队列
2.3 Socket
2.4 内存共享
内存映射
总结
性能
稳定性
安全性
二 Binder架构和通信模型
1 架构
我们知道Binder 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder 驱动。其中 Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。
其中 Service Manager 和 Binder 驱动由系统提供,而 Client、Server 由应用程序来实现。 Client、Server 和 ServiceManager 均是通过系统调用 open、 mmap 和 ioctl 来访问设备文件 /dev/binder,从而实现与 Binder 驱动的交互来间接的实现跨进程通信。
2 通信模型
由上面Binder的结构可知,Binder通信模型和网络通信有异曲同工之妙。网络通信模型如下图:
在角色方面, Client进程就等同于网络请求的Client 端,服务器就是Server进程,路由器相当于Binder驱动,而DNS服务器相当于ServiceManager进程。 Binder 驱动 就如同路由器一样,是整个通信的核心;驱动负责进程之间 Binder 通信的建立, Binder 在进程之间的传递, Binder 引用计数管理,数据包在进程之间的传递和交互等一系列底层支持。
ServiceManager 和 DNS 类似,作用是将字符形式的 Binder 名字转化成 Client 中对该 Binder 的引用,使得 Client 能够通过 Binder 的名字获得对 Binder 实体的引用。注册了名字的 Binder 叫实名 Binder,就像网站一样除了有 IP 地址以外还有自己的网址(域名)。Server 创建了 Binder,并为它起一个可读易记得名字,比如AMS的binder,我们就取了一个名字叫做“activity” ,将这个 Binder 实体连同名字一起以数据包的形式通过 Binder 驱动发送给 ServiceManager ,通知 ServiceManager 注册一个名为“activity”的 Binder,它位于某个 Server 中。而ServcieManager中就存储了一个表格,这个表格中就有名字和binder引用对应的信息item。
总结:
3 源码中Binder是如何创建的?
public static final Runnable zygoteInit(int targetSdkVersion, long[] disabledCompatChanges,
String[] argv, ClassLoader classLoader) {
if (RuntimeInit.DEBUG) {
Slog.d(RuntimeInit.TAG, "RuntimeInit: Starting application from zygote");
}
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ZygoteInit");
RuntimeInit.redirectLogStreams();
//初始化运行环境
RuntimeInit.commonInit();
//启动Binder,在androidRuntime.cpp中注册
ZygoteInit.nativeZygoteInit();
//通过反射创建程序入口函数的Method对象,并返回Runnable对象
//ActvityThread.mian()
return RuntimeInit.applicationInit(targetSdkVersion, disabledCompatChanges, argv,
classLoader);
}
virtual void onZygoteInit()
{
sp<ProcessState> proc = ProcessState::self();
ALOGV("App process: starting thread pool.\n");
proc->startThreadPool();
}
ProcessState::ProcessState(const char *driver)
: mDriverName(String8(driver))
, mDriverFD(open_driver(driver))//打开binder的虚拟驱动
, mVMStart(MAP_FAILED)
, mThreadCountLock(PTHREAD_MUTEX_INITIALIZER)
, mThreadCountDecrement(PTHREAD_COND_INITIALIZER)
, mExecutingThreadsCount(0)
, mMaxThreads(DEFAULT_MAX_BINDER_THREADS)
, mStarvationStartTimeMs(0)
, mBinderContextCheckFunc(nullptr)
, mBinderContextUserData(nullptr)
, mThreadPoolStarted(false)
, mThreadPoolSeq(1)
, mCallRestriction(CallRestriction::NONE)
{
// TODO(b/139016109): enforce in build system
#if defined(__ANDROID_APEX__)
LOG_ALWAYS_FATAL("Cannot use libbinder in APEX (only system.img libbinder) since it is not stable.");
#endif
if (mDriverFD >= 0) {
//调用mmap接口向Binder驱动中申请空间的内存
// mmap the binder, providing a chunk of virtual address space to receive transactions.
mVMStart = mmap(nullptr, BINDER_VM_SIZE, PROT_READ, MAP_PRIVATE | MAP_NORESERVE, mDriverFD, 0);
if (mVMStart == MAP_FAILED) {
// *sigh*
ALOGE("Using %s failed: unable to mmap transaction memory.\n", mDriverName.c_str());
close(mDriverFD);
mDriverFD = -1;
mDriverName.clear();
}
}
#ifdef __ANDROID__
LOG_ALWAYS_FATAL_IF(mDriverFD < 0, "Binder driver '%s' could not be opened. Terminating.", driver);
#endif
}
三 AIDL
1 简介
AIDL是Android Interface Definition Language的简写,即Android接口定义语言。AIDL就是Android实现IPC机制的方式之一,是一个Android IPC通信的一个代码规范。是对Binder协议的一个封装,可以更好的使用Binder进行IPC通信,其底层是Binder。
2 使用方法
由于AIDL的使用非常简单这里通过一个Demo来讲解一下AIDL。
- 服务端StudentService中定义IStudentAidlInterface.aidl文件和远程调用的方法。并且将AIDL整个文件拷贝到客户端相应位置(服务端和客户端的aidl文件包名必须保证一致)代码如下:
// IStudentAidlInterface.aidl package com.herrick.studentservice; interface IStudentAidlInterface { void setName(String name); String getName(); }
- 服务端StudentService中定义StudentService服务,在IStudentAidlInterface.Stub中实现需要远程调用的方法,并在onBind中返回IBinder实例。
package com.herrick.studentservice import android.app.Service import android.content.Intent import android.os.IBinder import android.os.ParcelFileDescriptor import android.util.Log class StudentService : Service() { private val mBinder by lazy { StudentBinder() } private var mName: String = "" override fun onBind(intent: Intent): IBinder { return mBinder } private inner class StudentBinder : IStudentAidlInterface.Stub() { override fun setName(name: String?) { Log.d("StudentService", "setName: $name") mName = "$name===Service" } override fun getName(): String { return mName } } }
- 在服务端AndroidManifest. xml 中配置service,android:enabled="true",android:exported="true"和action。同时要在客户端App的AndroidManifest. xml配置对应的权限,注意要保证包名一致,否则服务绑定失败。
服务端: <service android:name=".StudentService" android:enabled="true" android:exported="true"> <intent-filter> <action android:name="com.herrick.studentservice.aidlService" /> </intent-filter> </service> 客户端权限: <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <queries> <package android:name="com.herrick.studentservice" /> </queries> ...
- 在StudentClient端的MainActivity.kt中自定义ServiceConnection,然后将ServiceConnection传入bindService,获取到IBinder后实现远程调用。
package com.herrick.studentclient import android.content.ComponentName import android.content.Context import android.content.Intent import android.content.ServiceConnection import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.os.IBinder import android.util.Log import android.widget.Toast import com.herrick.studentclient.databinding.ActivityMainBinding import com.herrick.studentservice.IStudentAidlInterface class MainActivity : AppCompatActivity() { private var mStub: IStudentAidlInterface? =null private val TAG = "client_MainActivity" private lateinit var binding: ActivityMainBinding private val serviceConnection = object : ServiceConnection { override fun onServiceConnected(componentName: ComponentName, binder: IBinder) { Log.d(TAG, "onServiceConnected: ") mStub = IStudentAidlInterface.Stub.asInterface(binder) mStub?.apply { name = "赵四" Log.d(TAG, "获取名称: $name") } } override fun onServiceDisconnected(componentName: ComponentName?) { Log.d(TAG, "onServiceDisconnected: ") } } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.btn.setOnClickListener { bindService() } binding.btnCancel.setOnClickListener { unbindService(serviceConnection) } } override fun onDestroy() { super.onDestroy() unbindService(serviceConnection) } fun bindService() { if (mStub != null) { return } val intent = Intent("com.herrick.studentservice.aidlService") intent.setClassName("com.herrick.studentservice","com.herrick.studentservice.StudentService") try { val bindSucc = bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) if (bindSucc) { Toast.makeText(this, "bind ok", Toast.LENGTH_SHORT).show() } else { Toast.makeText(this, "bind fail", Toast.LENGTH_SHORT).show() } } catch (e: Exception) { e.printStackTrace() } } }
- aidl自动生成的文件定义完aidl文件,编译会自动生成一个java接口文件。 这个接口文件在build目录下,具体路径如下:
3 AIDL生成文件
打开生成的文件代码如下,这里只需要看那些核心代码就可以。
/*
* This file is auto-generated. DO NOT MODIFY.
*/
package com.herrick.studentservice;
public interface IStudentAidlInterface extends android.os.IInterface
{
/** Default implementation for IStudentAidlInterface. */
public static class Default implements com.herrick.studentservice.IStudentAidlInterface
{
@Override public void setName(java.lang.String name) throws android.os.RemoteException
{
}
@Override public java.lang.String getName() throws android.os.RemoteException
{
return null;
}
@Override
public android.os.IBinder asBinder() {
return null;
}
}
/** Local-side IPC implementation stub class. */
public static abstract class Stub extends android.os.Binder implements com.herrick.studentservice.IStudentAidlInterface
{
/** Construct the stub at attach it to the interface. */
public Stub()
{
this.attachInterface(this, DESCRIPTOR);
}
/**
* Cast an IBinder object into an com.herrick.studentservice.IStudentAidlInterface interface,
* generating a proxy if needed.
*/
public static com.herrick.studentservice.IStudentAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.herrick.studentservice.IStudentAidlInterface))) {
return ((com.herrick.studentservice.IStudentAidlInterface)iin);
}
return new com.herrick.studentservice.IStudentAidlInterface.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
{
java.lang.String descriptor = DESCRIPTOR;
if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
data.enforceInterface(descriptor);
}
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
}
switch (code)
{
case TRANSACTION_setName:
{
java.lang.String _arg0;
_arg0 = data.readString();
this.setName(_arg0);
reply.writeNoException();
break;
}
case TRANSACTION_getName:
{
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
break;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
return true;
}
private static class Proxy implements com.herrick.studentservice.IStudentAidlInterface
{
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 void setName(java.lang.String name) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
boolean _status = mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
@Override public java.lang.String getName() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
public static final java.lang.String DESCRIPTOR = "com.herrick.studentservice.IStudentAidlInterface";
public void setName(java.lang.String name) throws android.os.RemoteException;
public java.lang.String getName() throws android.os.RemoteException;
}
Stub 里面的关键函数1-asInterface:
public static com.herrick.studentservice.IStudentAidlInterface asInterface(android.os.IBinder obj)
{
if ((obj==null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin!=null)&&(iin instanceof com.herrick.studentservice.IStudentAidlInterface))) {
return ((com.herrick.studentservice.IStudentAidlInterface)iin);
}
return new com.herrick.studentservice.IStudentAidlInterface.Stub.Proxy(obj);//核心代码1
}
该核心代码就是创建一个Proxy对象,同时将IBinder对象的obj传值给Proxy。
Stub类中的关键函数2-onTransact():
@Override public boolean onTransact(int code, android.os.Parcel data, android.os.Parcel reply, int flags) throws android.os.RemoteException
{
java.lang.String descriptor = DESCRIPTOR;
if (code >= android.os.IBinder.FIRST_CALL_TRANSACTION && code <= android.os.IBinder.LAST_CALL_TRANSACTION) {
data.enforceInterface(descriptor);
}
switch (code)
{
case INTERFACE_TRANSACTION:
{
reply.writeString(descriptor);
return true;
}
}
switch (code)
{
case TRANSACTION_setName://核心代码2
{
java.lang.String _arg0;
_arg0 = data.readString();
this.setName(_arg0);
reply.writeNoException();
break;
}
case TRANSACTION_getName://核心代码2
{
java.lang.String _result = this.getName();
reply.writeNoException();
reply.writeString(_result);
break;
}
default:
{
return super.onTransact(code, data, reply, flags);
}
}
return true;
}
Proxy类里面的关键函数setName和getName
private static class Proxy implements com.herrick.studentservice.IStudentAidlInterface
{
...
//核心代码3
@Override public void setName(java.lang.String name) throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
try {
_data.writeInterfaceToken(DESCRIPTOR);
_data.writeString(name);
boolean _status = mRemote.transact(Stub.TRANSACTION_setName, _data, _reply, 0);
_reply.readException();
}
finally {
_reply.recycle();
_data.recycle();
}
}
//核心代码3
@Override public java.lang.String getName() throws android.os.RemoteException
{
android.os.Parcel _data = android.os.Parcel.obtain();
android.os.Parcel _reply = android.os.Parcel.obtain();
java.lang.String _result;
try {
_data.writeInterfaceToken(DESCRIPTOR);
boolean _status = mRemote.transact(Stub.TRANSACTION_getName, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
}
finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
static final int TRANSACTION_setName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 0);
static final int TRANSACTION_getName = (android.os.IBinder.FIRST_CALL_TRANSACTION + 1);
}
小结
总结
4 bindService的流程
之所以在这里分析bindService的流程是为了给大家展示一个app进程和另外一个进程的Service实现跨进程通信的过程,这个过程将给大家全面的展示开发中最常见的跨进程通信时Client端拿到Server端Binder的总流程。
基于android API 30 代码的执行流程如下:
四 Java与Native 通信的基本流程