一:Binder认识
1. 为什么android选择binder 进程间通信
Android 系统是基于 Linux 内核的,Linux 已经提供了管道、消息队列、共享内存和 Socket 等 IPC 机制。那为什么 Android 还要提供 Binder 来实现 IPC 呢?主要是基于性能、稳定性和安全性几方面的原因。
共享内存: 两个或多个进程可同时访问同一块内存,需手动控制内存间互斥操作,同时安全性也较差。
管道: 管道分为命名管道和无名管道,以特殊文件作为中间介质,分为读端和写端,构成数据传递流水线。 1次通信2次数据复制,管道会分配缓冲区,缓冲区有限、因字节流形式读写两端需约定数据格式。
消息队列: 2次数据复制、 读写双方约定好数据类型和大小、
socket: 1个socket两个缓冲区、2次数据复制、
Binder: binder通信需要内核空间作支撑、linux的LKM机制可单编链接到内核运行,这样,Android 系统就可以通过动态添加一个 内核模块运行在内核空间,用户进程之间通过这个内核模块作为桥梁来实现通信,这个内核模块即Binder Driver binder驱动,通过内核缓冲区与内存映射关系实现数据读写,即跨进程间通信。 满足稳定性、传输性能、安全性的IPC机制。
Binder 是基于 C/S 架构的。由一系列的组件组成,包括 Client、Server、ServiceManager、Binder 驱动。其中Client、Server、Service Manager 运行在用户空间,Binder 驱动运行在内核空间。

2. Binder初始化
每一个应用进程启动的时候,都是通过zygote fork产生的,所以,当fork产生进程后app进程的代码就开始执行 { RuntimeInit.commonInit();//初始化运行环境 ZygoteInit.nativeZygoteInit();//启动Binder ,方法在 androidRuntime.cpp中注册 }
总的来说,Binder的初始化是在进程已创建就完成了。创建进程后会第一时间为这个进程打开一个binder驱动,并调用mmap接口向Binder驱动中申请内核空间的内存。
3. Binder通信模型
binder通信模型类似于网络通信模型: 个人PC --> 路由器-DNS服务器 <-- 应用服务器PC
4. Binder流程
1. 首先,一个进程使用 BINDERSETCONTEXT_MGR 命令通过 Binder 驱动将自己注册成为 ServiceManager;
2. Server 通过驱动向 ServiceManager 中注册 Binder(Server 中的 Binder 实体),表明可以对外提供服务。驱
动为这个 Binder 创建位于内核中的实体节点以及 ServiceManager 对实体的引用,将名字以及新建的引用打包
传给 ServiceManager,ServiceManger 将其填入查找表。
3. Client 通过名字,在 Binder 驱动的帮助下从 ServiceManager 中获取到对 Binder 实体的引用。
4. 通过这个Binder实体引用,Client实现和 Server 进程的通信。
二、AIDL
1.模型

2. Demo : MainActivity通过AIDL实现IPC来调用另一个进程中RemoteTestService的一个方法。 主要有以下几个文件
1. MainActivity.java自定义ServiceConnection,然后将ServiceConnection传入bindService,获取到IBinder后实现远程调用。2. RemoteTestService.java 在ITestServer.Stub中实现需要远程调用的方法testFunction(),在onBind中返回。3. IServer.aidl 定义一个aidl文件和需要远程调用的方法4. AndroidManifest.xml 在此设置RemoteTestService在remote进程。
MainActivity.java
public class MainActivity extends AppCompatActivity {
private IServer iServer;
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
iServer = IServer.Stub.asInterface(service);
System.out.println("onServiceConnected");
try {
int a = iServer.testFunction("test string");
Log.i("test", "after testFunction a:" + a);
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
Log.i("test", "onServiceDisconnected");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this, RemoteTestService.class);
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
}
RemoteTestService.java
public class RemoteTestService extends Service {
private IServer.Stub serverBinder = new IServer.Stub() {
@Override
public int testFunction(String s) throws RemoteException {
Log.i("test","testFunction s= " + s);
return 0;
}
};
@Nullable @Override
public IBinder onBind(Intent intent) {
return serverBinder;
}
}
IServer.aidl
interface IServer {
int testFunction(String s);
}
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=".RemoteTestService"
android:process=":remote" />
</application>
2. binder基于AIDL通信流程

aidl通信的基本步骤如下:1. Client通过ServiceConnection获取到Server的Binder,并且封装成一个Proxy。2. 通过Proxy来同步调用IPC方法(testFunction),同时通过Parcel将参数传给Binder,最终触发Binder的 transact方法。3. Binder的transact方法最终会触发到Server上Stub的onTransact方法。4. Server上Stub的onTransact方法中,会先从Parcel中解析中参数,然后将参数带入真正的方法中执行,然后将 结果写入Parcel后传回。5. 请注意:Client的Ipc方法中,执行Binder的transact时,是阻塞等待的,一直到Server逻辑执行结束后才会继 续执行。当然,如果IPC方法是oneWay的方式,那么就是非阻塞的等待。6. 当Server返回结果后,Client从Parcel中取出返回值,于是实现了一次IPC调用。所以,aidl 文件会生成一个java文件,这个java文件的意义在于将核心的binder驱动封装成为java层可以直接调用的 代码,同时也处理了将java层的数据格式转换为Parcel格式数据进行跨进程传递的一个功能。所以,aidl是一个使用 binder的标准方案,该方案的代码同样的可以通过用户自己编写的方式完成。
三 IPC bindService流程
1. 基于android API 30 代码的执行流程如下
ContextWrapper.bindService()
ContextImpl.bindService()
ContextImpl. bindServiceCommon()
ActivityManagerService.bindService()
ActiveServices.bindServiceLocked()
ActiveServices.requestServiceBindingLocked()
ActivityThread.ApplicationThread.scheduleBindService()
ActivityThread.sendMessage()
ActivityThread.handleBindService()
ActivityManagerService.publishService()
ActiveService.publishServiceLocked()
LoadedApk.ServiceDispatcher.InnerConnection.connected()
LoadedApk.ServiceDispatcher.connected()
ActivityThread.post()
LoadedApk.ServiceDispatcher. RunConnection.run()
LoadedApk.ServiceDispatcher.doConnected()
ServiceConnection.onServiceConnected()
上面会经历几个进程:1)App进程A,发起bindService(); 2)AMS所在的systemServer进程;3)Service所在的进 程B;4)还有一个隐藏在背后支撑binder通信的ServiceManager。

具体流程如下:1)Activity作为Client发起bindService,最终会调度到AMS 去执行bindService。在这个过程中,Client要去调用 AMS的代码,所以此时就会涉及到跨进程调度,基于第三章的Binder通信模型我们不难知道,Client会先和 ServiceManager通信,从ServiceManager中拿到AMS的IBinder。2)Activity拿到AMS的IBinder后,跨进程执行AMS的BindService函数;3)由于AMS管理所有的应用进程,因此AMS中持有了应用进程的Binder,所以此时AMS可以发起第4步也就是跨进 程调度scheduleBindService();4)Server端会在收到AMS的bindService的请求后,会将自己的IBinder发送给client,但是Server必须通过AMS才能 将Binder对象传过去,所以此时需要跨进程从ServiceManager中去拿到AMS的binder;5)Server端通过AMS的binder直接调用AMS的代码publishService(),将service的Binder发送给AMS;6)经过层层调用,最终AMS讲Server端的binder通过回调connect函数传递给了Client端的Activity;以上就是bindService的全流程,这个流程主要的目的是将Server端的Binder对象发送给Client端。从此以后,Client 端就可以通过Server端的binder与Server端像调用自己的代码一样完成跨进程通信了。
1. BpBinder
BpBinder和BBinder都是Android中与Binder通信相关的代表,他们都是从IBinder中继承而来的。其中BpBinder是 客户端用来与Server交互的代理类,BBinder则是和proxy相对的一端,它是proxy交互的目的端。如果说Proxy代表 客户端,那么BBinder就代表这服务端。这里BpBinder和BBinder是一一对应的,即某个BpBinder只能和对应的 BBinder交互。
四: ServiceManager解析
1. ServiceManager在init进程启动之后启动,用来管理系统中的service注册、查找、通讯等。
2. ServiceManager的启动
所有的系统服务都是需要在ServiceManager中进行注册的,而ServiceManager作为一个起始的服务,是通过init.rc 来启动的
3. servicemanager的入口函数在frameworks\native\cmds\servicemanager\main.cpp中
//通过Looper epoll机制处理binder事务
sp<Looper> looper = Looper::prepare(false /*allowNonCallbacks*/);
//通知驱动BC_ENTER_LOOPER,监听驱动fd,有消息时回调到handleEvent处理binder调用
BinderCallback::setupTo(looper);
//服务的注册监听相关
ClientCallbackCallback::setupTo(looper, manager);
//无限循环等消息
while(true) {
looper->pollAll(-1);
}
4. servicemanager 映射的虚拟内存有多大?现在的答案是和普通应用一样大

2757

被折叠的 条评论
为什么被折叠?



