多进程Binder

进程间通信(ipc)

[[7-Webview多进程方案]]

IPC的主要目的是调用其他进程的函数

  • 使用多进程显而易见的好处就是分担主进程的内存压力。我们的应用越做越大,内存越来越多,将一些独立的组件放到不同的进程,它就不占用主进程的内存空间了。当然还有其他好处,有些应用后台是有多个进程的,启动一个不可见的轻量级私有进程,在后台收发消息,或者做一些耗时的事情,或者开机启动这个进程,然后做监听等。还有就是防止主进程被杀守护进程,守护进程和主进程之间相互监视,有一方被杀就重新启动它。因为它们要常驻后台,特别是即时通讯或者社交应用
  • 推送、融云

什么时候用服务
不需要和用户交互而且还要长期运行的任务。服务的运行不依赖于任何用户界面,即使程序被切换到后台,或者用户打开了另一个应用程序,服务仍然能够保持独立运行。音乐、播放器

如果一个 app 两个进程同时操作 sharedpreferences,如何保证数据安全。不能,使用ContentProvider、mmkv

Binder

https://blog.csdn.net/freekiteyu/article/details/70082302

Android Binder 驱动框架设计与分析

  • Binder 是 Android 系统中进程间通讯(IPC)的一种方式。不同进程、不同 APP 间就是通过 binder 通讯的。Binder 通信采用 C/S 架构,从组件视角来说,包含 Client、 Server、 ServiceManager 以及 Binder 驱动。
  • 服务端提供服务,客户端获取数据。ServiceManager 的作用查询服务和注册服务。Binder 驱动是运行在内核空间 (liux),进程间通讯的最底层就是 Binder 驱动。它使用的是操作系统中一种内存映射的方法。
  • ServiceManager 将用户空间的参数等请求数据复制到内核空间,并向服务端插入一条执行方法的事务(事务(Transaction),一般是指要做的或所做的事情)。事务执行完通知 ServiceManager 将执行结果从内核空间复制到用户空间,并唤醒等待的线程 (binder_send_reply 发送 reply、binder_write 客户端写),响应结果,通讯就结束了。
  • 在 Android 开发中,可以使用 AIDL,并通过系统生成的代码来实现客户端和服务端的通信。AIDL 可以处理跨进程的方法调用、数据传输、回调等功能,简化了开发者在进行跨进程通信时的代码编写工作。

Binder 驱动:运行在内核空间 kernel,负责各个用户进程通过 Binder 实现通信的内核模块(Binder Dirver)底层就是 Binder 驱动

  • 内存映射就是将用户空间的一块内存区域映射到内核空间。映射关系建立后不同的进程的内核地址空间是共享的。能减少数据拷贝次数,实现进程间通讯的高效互动。
  • 2次拷贝,内核空间与用户空间共享内存通过 copy_from_user (), copy_to_user () 内核方法来完成用户空间与内核空间内存的数据传输。
  • 不同进程之间的内核地址空间映射到相同的物理地址,即不同的进程的内核地址空间是共享的。不同进程之间的用户地址空间映射到不同的物理地址,相互之间是隔离的,无法访问的。

内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。
完成映射后,进程只需调用一次 copy_from_user,A 进程的用户空间就可以访问到 int a。这里优化到了一次拷贝

unknown_filename.12|600

流程图

unknown_filename.3

MMAP

Binder 只需要一次数据拷贝,性能更好
内核空间与用户空间共享内存通过 copy_from_user (), copy_to_user () 内核方法来完成用户空间与内核空间内存的数据传输

Binder IPC 机制中涉及到的内存映射通过 mmap () 来实现,mmap () 是操作系统中一种内存映射的方法。内存映射简单的讲就是将用户空间的一块内存区域映射到内核空间。映射关系建立后,用户对这块内存区域的修改可以直接反应到内核空间;反之内核空间对这段区域的修改也能直接反应到用户空间。

内存映射能减少数据拷贝次数,实现用户空间和内核空间的高效互动。两个空间各自的修改能直接反映在映射的内存区域,从而被对方空间及时感知。也正因为如此,内存映射能够提供对进程间通信的支持。
完成映射后,进程只需调用一次 copy_from_user,A 进程的用户空间就可以访问到 int o。这里优化到了一次拷贝

unknown_filename.12|600
unknown_filename|600

ServiceManager

[[1-Android的启动流程#ServiceManager]]

  • ServiceManager 分为 framework 层和 native 层,framework 层只是对 native 层进行了封装方便调用。
  • ServiceManager 的作用很简单就是提供了查询服务和注册服务的功能。
  • 负责管理系统中的 Service 组件,并且向 Client 组件提供获取 Service 代理对象的服务。

ServiceManager 运行在一个独立的进程中,因此,Service 组件和 Client 组件也需要通过进程间通信机制来和它交互,而采用的进程间通信机制正好也是 Binder 进程间通信机制。

ServiceManager 是由 init 进程负责启动的,而 init 进程是在系统启动时启动的,因此,ServiceManager 也是在系统启动时启动。

最后开启 loop 不断的处理共享内存中的数据,无限循环来等待和处理 Service 组件和 Client 组件的进程间通信请求

驱动

Binder是一种虚拟的物理设备驱动

  • 虚拟设备驱动程序:虚拟设备驱动程序是模拟硬件设备的驱动程序。它模拟了一块硬件,因此软件可能会有访问真实硬件的错觉。一些虚拟设备驱动程序示例是虚拟网络适配器,虚拟DVD / CD驱动器,虚拟磁盘(内存)等
  • 驱动是干什么的?仅从功能的角度来说,驱动程序使得应用程序可以访问硬件。
  • 那应用是如何访问硬件的?linux 中一切皆文件,访问硬件就是对文件的读写操作。比如 led 灯对应的文件是 /dev/led, 读写这个文件就能操作 led 灯。而binder驱动就是读写硬件内存区域

虚拟内存

在 Linux 中,每个进程都有自己的虚拟内存地址空间。虚拟内存地址空间又分为了用户地址空间和内核地址空间。

虚拟内存 通过创建虚拟地址空间来隔离不同进程的内存访问,解决了多进程运行时的内存冲突问题。每个进程都有自己的虚拟地址空间,并通过 MMU(内存管理单元) 将虚拟地址映射到物理内存地址上,从而实现对物理内存的访问和管理。

Binder 基本原理 | Android Framework

客户端怎么执行服务端的方法

  • 我们们在使用 Binder 时基本都是调用 framework 层封装好的方法,AIDL 就是 framework 层提供的傻瓜式是使用方式。假设服务已经注册完,我们来看看客户端怎么执行服务端的方法。
  • 首先我们通过 ServiceManager 获取到服务端的 BinderProxy 代理对象,通过调用 BinderProxy 将参数,方法标识(例如:TRANSACTION_test,AIDL中自动生成)传给 ServiceManager,同时客户端线程进入等待状态。
  • ServiceManager 将用户空间的参数等请求数据复制到内核空间,并向服务端插入一条执行执行方法的事务。事务执行完通知 ServiceManager 将执行结果从内核空间复制到用户空间,并唤醒等待的线程,响应结果,通讯结束。

Binder 通信中的代理模式

  • 我们已经解释清楚 Client、Server 借助 Binder 驱动完成跨进程通信的实现机制了,但是还有个问题会让我们困惑。A 进程想要 B 进程中某个对象(object)是如何实现的呢?毕竟它们分属不同的进程,A 进程 没法直接使用 B 进程中的 object。
  • 在数据流经 Binder 驱动的时候驱动会对数据做一层转换。当 A 进程想要获取 B 进程中的 object 时,驱动并不会真的把 object 返回给 A,而是返回了一个跟 object 看起来一模一样的代理对象 ProxyObject,这个 ProxyObject 具有和 object 一摸一样的方法,但是这些方法并没有 B 进程中 object 对象那些方法的能力,这些方法只需要把把请求参数交给驱动即可。对于 A 进程来说和直接调用 object 中的方法是一样的。
  • 当 Binder 驱动接收到 A 进程的消息后,发现这是个 objectProxy 就去查询自己维护的表单,一查发现这是 B 进程 object 的代理对象。于是就会去通知 B 进程调用 object 的方法,并要求 B 进程把返回结果发给自己。当驱动拿到 B 进程的返回结果后就会转发给 A 进程,一次通信就完成了

unknown_filename.1|600

常规通讯

进程间通信就是为了实现数据共享。一个程序不同组件在不同进程也叫多进程,和俩个应用没有本质区别。使用 process 属性可以实现多进程,但是会带来很多麻烦,主要原因是共享数据会失败,弊端有: 静态和单利失效,同步失效,sharedprefer 也变的不可靠等问题

只有允许不同应用的客户端用 IPC 方式访问服务,并且想要在服务中处理多线程(多任务)时,才有必要使用 AIDL。 如果您不需要执行跨越不同应用的并发 IPC,就应该通过实现一个 Binder 创建接口;或者,如果您想执行 IPC,但根本不需要处理多线程,则使用 Messenger 类来实现接口。无论如何,在实现 AIDL 之前,请您务必理解绑定服务。

aidl文档

AIDL

Android interface definition language (android接口定义语言) , 用来跨进程的访问方法。

AIDL 是什么,怎么实现的,为什么要创建个文件 aidl
1)、定义AIDL文件;——类似于后台给了一个接口文档,里面描述了各种各样的接口,而在AIDL文件中描述的是类似于JAVA接口的方法定义
2)、实现AIDL文件里面定义的接口;——类似于后台实现接口文档的接口控制器,里面定义了接口的具体实现
3)、暴露接口;——这个就像后台开发人员把接口文档给App开发人员,然后App开发人员就知道有哪些接口可以调用来实现业务了
4)、调用;——这个就是App开发人员调用后台的接口来获取过程数据了。
从上面的描述可以看出,这个AIDL实际上就是一个C/S模型,一边是客户端,一边是服务器。

aidl 操作步骤

  1. 在两个项目中新建aidl文件,客户端和服务端中这个文件所在的包名要保持一致,内容也要一样,在接口文件AIDLFunctions.aidl中,我们定义一个方法 show
1
2
3
interface AidlFunctions{
void show();
}
  1. 编译之后, 会在build目录下,自动产生同名的,后缀为 java 的文件。里面有我们要用到的 Stub内部类。 com.example.aidl.AidlFunctions是刚定义。
    里面的东西
1
public static abstract class Stub extends android.os.Binder implements  com.example.aidl.AidlFunctions

unknown_filename.2

  1. AIDL的使用,需要一个Service配合,所以我们在服务端还要声明一个Service,来接收消息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class AIDLService extends Service {
//stub就是系统自动产生的
AidlFunctions.Stub binder;

@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
}

@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
binder = new AidlFunctions.Stub() {

@Override
//这里是我们在接口中声明的方法的实现
public void show() throws RemoteException {
System.out.println("--------------------收到----------------------");
}
};
return binder;
}
}

https://www.jianshu.com/p/29999c1a93cd
需要把服务端的AIDL文件以及Book类复制过来,将 aidl 文件夹整个复制到和Java文件夹同个层级下,不需要改动任何代码

远端服务通过action绑定
unknown_filename.8

  1. 客户端:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//绑定服务,要用到ServiceConnection 
private ServiceConnection serviceConnection;
//自定义的接口,和服务端一样
private AidlFunctions aidlFunctions;

serviceConnection = new ServiceConnection() {

@Override
public void onServiceDisconnected(ComponentName name) {
System.out.println("--------------------ServiceDisconnected----------------------");
}

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
System.out.println("--------------------ServiceConnected----------------------");
aidlFunctions = AidlFunctions.Stub.asInterface(service);
}
};
Intent intent = new Intent("com.example.androidaidl.AIDLService");
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
//调用show方法
try {
aidlFunctions.show();
} catch (RemoteException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

AIDL 生成的文件

stub 具体实现类、proxy 代理类

unknown_filename.7

  1. 基本角色
  • Client 端(左边 Activity 部分)

    • 调用 bindService() 绑定服务。
    • 绑定成功后,系统回调 onServiceConnected(),得到一个 IBinder 对象(mRemoteService)。
    • 通过 Stub.asInterface()IBinder 转换为 Ixxx 接口对象(其实是 Stub.Proxy)。
  • Service 端(右边 RemoteService 部分)

    • onBind() 方法里返回一个 Binder 对象(mRemoteBinder)。
    • 这个 Binder 实际上是 Ixxx.Stub 的实例。
  • Binder 驱动层(中间的 AMS / Binder Driver)

    • 属于 内核空间,负责进程间通信(IPC)的数据传输。
    • Client 和 Service 不直接通信,而是通过 Binder 驱动。
  1. AIDL 工具生成的代码
  • **Ixxx.aidl**:定义接口方法(如 doXXX())。

  • Ixxx.java(AIDL 工具自动生成)

    • **Ixxx.Stub**(抽象类,继承自 Binder)

      • 在 Service 端使用,负责接收调用并分发到具体实现。
      • 核心方法:
        • onTransact():接收来自 Client 的请求(IPC)。
        • doXXX():实际逻辑实现。
    • **Ixxx.Stub.Proxy**(内部类)

      • 在 Client 端使用,代表 Service 的代理对象。
      • 调用 doXXX() 时,最终通过 Binder 驱动发到远程 Service。
  1. 通信流程(按图走一遍)

  2. 客户端发起绑定

    • Activity.bindService() → AMS → 远程 Service.onBind()
    • Service 返回 Ixxx.Stub 的实例(mRemoteBinder)。
  3. 客户端获得接口对象

    • 系统回调 onServiceConnected()
    • 得到 IBinder 对象 → Stub.asInterface(mRemoteBinder) → 生成 Ixxx.Stub.Proxy
  4. 调用远程方法

    • 客户端调用 mRemoteService.doXXX()
    • 实际上是调用 Proxy.doXXX(),数据通过 Binder 驱动发到 Service 进程。
  5. 服务端接收请求

    • Stub.onTransact() 被触发。
    • 根据事务 code 找到对应方法 → 调用真正的实现 doXXX()
  6. 结果返回

    • 执行结果通过 Binder 驱动回传给客户端。
    • Proxy 接收结果并返回给调用的 Activity。

总结

  • Proxy:客户端的远程代理,假装自己就是服务。
  • Stub:服务端的 Binder,负责接收并转发调用。
  • Binder Driver:系统内核,负责 IPC。
  • AIDL 工具:自动生成 StubProxy,开发者只需要实现接口。

一句话总结:
👉 Activity 调用 Proxy → Binder 驱动转发 → Service 的 Stub 接收并执行 → 返回结果。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
interface IMyAidlInterface {
int sendData(String data);
String getData();
}


//生成
public interface IMyAidlInterface extends android.os.IInterface {

public static abstract class Stub extends android.os.Binder implements com.zero.aidleservicedemo.IMyAidlInterface {
private static final java.lang.String DESCRIPTOR = "com.zero.aidleservicedemo.IMyAidlInterface";

public Stub() {
this.attachInterface(this, DESCRIPTOR);
}

/**
* Cast an IBinder object into an com.zero.aidleservicedemo.IMyAidlInterface interface,
* generating a proxy if needed. 生成代理需要
*/
public static com.zero.aidleservicedemo.IMyAidlInterface asInterface(android.os.IBinder obj) {
if ((obj == null)) {
return null;
}
android.os.IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (((iin != null) && (iin instanceof com.zero.aidleservicedemo.IMyAidlInterface))) {
return ((com.zero.aidleservicedemo.IMyAidlInterface) iin);
}
return new com.zero.aidleservicedemo.IMyAidlInterface.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;
switch (code) {
//自己的方法
case TRANSACTION_sendData: {

return true;
}
case TRANSACTION_getData: {
data.enforceInterface(descriptor);
java.lang.String _result = this.getData();
reply.writeNoException();
reply.writeString(_result);
return true;
}
default: {
return super.onTransact(code, data, reply, flags);
}
}
}

//代理类
private static class Proxy implements com.zero.aidleservicedemo.IMyAidlInterface {
private android.os.IBinder mRemote;

@Override
public android.os.IBinder asBinder() {
return mRemote;
}


@Override
public int sendData(java.lang.String data) throws android.os.RemoteException {
return _result;
}

@Override
public java.lang.String getData() 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);
mRemote.transact(Stub.TRANSACTION_getData, _data, _reply, 0);
_reply.readException();
_result = _reply.readString();
} finally {
_reply.recycle();
_data.recycle();
}
return _result;
}
}
}

public int sendData(java.lang.String data) throws android.os.RemoteException;

public java.lang.String getData() throws android.os.RemoteException;
}

onTransact, 典型的代理模式

  • Server 则是通过 onTransact 方法接收 Client 进程传过来的数据,包括函数名称、函数参数,找到对应的函数(这里是 sum ),把参数喂进去,得到结果,返回。
  • 所以 onTransact 函数经历了读数据→执行要调用的函数→把执行结果再写数据的过程
  • 注意,在 AIDL 的另一端,是通过 Stub 类的 onTransact 把值从 data 中取出来,计算完得到结果,再把结果通过 reply 返回。
    unknown_filename.4|700|600

其他通讯方法

socket、binder、信号链、共享内存
Intent、 Messenger、AIDL,sharedpre、广播、scheme

  1. 使用intent的附加信息extras来传递,通过bundle,传递的是bundle支持的类型,比如基本数据类型、实现pracellable或serializeable的对象
1
2
3
4
5
6
7
/**指定包名和带包名的Activity的名字*/
ComponentName componentName = new ComponentName("com.example.androidaidl", "com.example.androidaidl.MainActivity");
Intent intent = new Intent();
intent.putExtra("id", 1001);
intent.setComponent(componentName);
startActivity(intent);

  1. 使用文件共享,序列化或是sharedpre,不过不适用于读写并发的操作
  2. 广播:Android的广播是系统级的,只要传递的Action一样,就可以接收到其他进程广播的消息,广播中可以通过Intent传递数据。
  3. scheme协议是android中的一种页面内跳转协议,通过定义自己的scheme协议,可以非常方便跳转app中的各个页面,并且传递数据,还是可以通过H5页面跳转指定页面等。
  4. soket,长链接读网络数据

ElegantBus

这是一个基于 Binder 的多进程事件总线方案
核心思路是引入一个中间 Service 作为通信枢纽,所有业务进程通过 bindService 连接到它。

各进程在 Service 中通过 AIDL Callback 注册自己,Service 使用 RemoteCallbackList 统一管理这些跨进程回调,自动处理进程死亡。

当某个进程 post 事件时,只需要通过 Binder 把 EventWrapper 发给 Service,
Service 再 遍历 callback 列表,将事件 fan-out 分发到其他进程,由各进程本地处理。

这种设计避免了进程之间直接互调,降低耦合度,同时对 WebView 多进程、插件化场景比较友好。

Q:为什么不让主进程直接调 web 进程?

会形成 N×N 的 Binder 依赖,生命周期和异常处理都很复杂,用中心 Service 可以统一管理。

Q:为什么用 RemoteCallbackList?

它能自动感知进程死亡并清理 callback,避免内存泄漏,这是 Binder 回调的最佳实践。

总结流程

  1. 客户端通过 AIDL 注册回调接口给服务端:
    service.registerCallback(myCallback);
  2. 服务端将回调保存到 RemoteCallbackList
  3. 服务端需要通知客户端时:
    • 遍历 RemoteCallbackList → 调用 callback.call()
  4. RemoteCallbackList 自动处理死掉的客户端,避免内存泄漏。

call 方法是 IProcessCallback 的方法,IProcessCallback 自己定义的,调用 call 其实就是客户端调用代理类的方法,然后真正的服务端方法执行
aidl 发送的是 JSON 字符串,然后转成对应的对象

unknown_filename.9|900

RemoteCallbackList

  • 在 Android 多进程环境下,客户端可能注册多个回调接口,而且这些回调对象可能跨进程。
  • RemoteCallbackList 是专门用来管理远程回调的线程安全列表。
  • 作用
    • 自动处理死掉的进程(当客户端进程退出,RemoteCallbackList 会自动移除回调)。
    • 提供 beginBroadcast()finishBroadcast() 来安全遍历回调列表。

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
RemoteCallbackList<IProcessCallback> mCallbacks = new RemoteCallbackList<>();

// 注册回调
public void registerCallback(IProcessCallback callback) {
mCallbacks.register(callback);
}

// 取消注册
public void unregisterCallback(IProcessCallback callback) {
mCallbacks.unregister(callback);
}

// 通知所有客户端
public void notifyClients(String message) {
final int n = mCallbacks.beginBroadcast();
for (int i = 0; i < n; i++) {
try {
mCallbacks.getBroadcastItem(i).call(message);
} catch (RemoteException e) {
e.printStackTrace();
}
}
mCallbacks.finishBroadcast();
}

callback.call()

  • 这是实际触发回调的方法。
  • 关键点:
    • **可能抛出 RemoteException**,因为跨进程通信可能失败。
    • 每个 callback 对象都是 远程代理,调用它会触发 IPC 通信。
    • 通常在 RemoteCallbackList 遍历时使用。

ContentProvider

(进程间数据共享)和message一样,底层也是binder,除了oncreate方法其他方法(crud)都是运行在bindler线程里。所以在oncerate里不能做耗时操作。Android本身就提供了不少的ContentProvider访问,比如联系人、相册等。 访问ContentProvider,需要通过Uri,需要以“content://”开头。在其他应用访问通过uri(主机名):

缺点:互相传递数据麻烦,2个provider

ContentProvider

1
2
3
4
5
6
7
contentResolver?.call(
MainProcessContentProvider.MAINCONTENTPROVIDER_URI,
"getAge",
null,
bundleOf("student" to "小米")
)

一般的跨进程通信就是基于 aidl,这种方式存在的缺点就是每次都需要定义一个Service,再通过BindService来进行进程间通信。也能用ContentProvider.call()来实现便捷的跨进程通信

1
2
3
4
5
6
7
8
9
10
11
12
@Override
public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
if (!TextUtils.isEmpty(method) && TextUtils.equals(method, "setData")) {
mBinderParcel.mProxy = extras.getBinder("binder");
} else if (!TextUtils.isEmpty(method) && TextUtils.equals(method, "getData")) {
Bundle bundle = new Bundle();
bundle.putBinder("binder", mBinderParcel.mProxy);
return bundle;
}
return null;
}

method可以用来区分调用的逻辑,arg貌似没什么用,extras是一个Bundle类型用来存放数据。
调用的时候

1
2
3
4
5
6
7
getContentResolver().call(Uri.parse("content://com.example.mycontentprovider"), "setData", null, bundle); //设置数据

Bundle bundle = getContentResolver().call(Uri.parse("content://com.example.mycontentprovider"), "getData", null, null);

mBinder = IMyAidlInterface.Stub.asInterface(bundle.getBinder("binder"));

Log.i("kingiyu", "RemoteActivity-->-->onCreate-->" + mBinder.getData());

Socket

[[2-Activity进程启动流程#ams-Zygote 通信为什么用 Socket]]

Messenger

  1. 通过 Messenger进行传递(handler),在远程服务里创建handler(接收客户端发送的消息)、 Messenger对像,在onbind里返回( Messenger.getbinder)。在客户端绑定服务,拿着 Messenger对象发消息(可以用bundle)。在远程服务的handlermessage方法就会收到。他是一个个处理的,如果大量并发请求用aidl, Messenger底层就是aidl

在客户端中创建一个Messenger。然后,当客户端收到 onServiceConnected() 回调时,会向服务发送一条 Message,并在其 send() 方法的 replyTo 参数中包含客户端的 Messenger。
注意:Messenger和Message是俩个东西

1
2
3
4
5
6
7
8
9
public void sayHello(View v) {
if (!mBound) return;
Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
try {
mService.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
  1. 直接使用Binder对象:缺点是这种方式不能进行跨进程,跨应用程序的函数调用。只能实现在同一个进程之中,同一个应用程序之中的不同的组件之间通讯。

用法:继承Binder,然后在service里return
继承Binder用它的对象返回,客户端将bind对象强转成自定义Bind

内存泄露

Binder 代理对象泄露问题

  1. 跨进程回调:当一个进程通过Binder将自己的代理对象传递给另一个进程,并且在另一个进程中注册回调接口时,如果没有适时地取消注册或解除引用,就会导致泄露。例如,一个Activity通过Binder传递自己的代理对象给Service,并在Service中注册回调接口,如果Activity销毁时没有取消注册,就会导致Activity的代理对象无法释放
  2. 跨进程单例模式:当一个进程中的组件(如Service或ContentProvider)通过Binder提供单例对象给其他进程时,如果其他进程持有该对象的引用,并且没有适时地释放引用,就会导致泄露。这种情况下,其他进程可能会长时间持有对象的引用,使对象无法被垃圾回收。
  3. 跨进程数据缓存:当一个进程通过Binder将数据提供给其他进程,并且其他进程持有数据的引用时,如果没有适时地释放引用,就会导致泄露。这种情况下,数据对象可能会占用大量内存,并且无法被回收,从而导致内存泄漏。

为避免Binder代理对象泄露问题,可以采取以下措施:

  1. 确保在不再需要Binder代理对象时及时解除引用或取消注册回调接口,避免长时间持有代理对象的引用。
  2. 在使用Binder进行跨进程通信时,遵循良好的生命周期管理原则,确保在合适的时机释放资源。
  3. 对于跨进程传递的数据对象,尽量避免长时间持有引用,或者使用弱引用等机制来管理引用。

多进程Binder
http://peiniwan.github.io/2025/12/d842c8b35664.html
作者
六月的雨
发布于
2025年12月16日
许可协议