整理自模拟面试,覆盖
Binder / AIDL / Messenger / Parcelable / TransactionTooLargeException / 共享内存
一句话:IPC(Inter-Process Communication)就是不同进程之间交换数据、传递消息的机制。
Binder:Android 最核心的 IPC 机制,系统服务通信基本都基于它AIDL:Binder 的上层接口定义方式,适合跨进程接口调用Messenger:Binder + Handler 封装,适合轻量消息通信ContentProvider:适合跨进程共享结构化数据Socket:更通用,适合跨设备或跨语言通信一句话:Binder 是 Android 提供的基于 Client-Server 模型的 IPC 机制,让跨进程调用看起来像本地方法调用。
Client:调用方Server:服务提供方Binder 驱动:内核中的通信中转ServiceManager:服务注册和查询中心ProxyProxy 把方法参数写入 ParcelStub 在 onTransact() 中解包数据Parcel 返回给 Clientuid/pid一句话:AIDL 是 Android 定义跨进程接口的一种方式,适合复杂接口调用,但存在类型、线程、性能和传输大小等限制。
String、CharSequenceList、Map 的受限类型ParcelableParcel 序列化/反序列化TransactionTooLargeExceptionRemoteExceptiononServiceDisconnected()DeathRecipientin:客户端传给服务端,最常用out:服务端写回客户端inout:双向读写ParcelableStub / Proxy / asInterface() 做了什么IMathService.aidl
package com.example.ipc;
interface IMathService {
int add(int a, int b);
}
服务端:
public class MathService extends Service {
private final IMathService.Stub mBinder = new IMathService.Stub() {
@Override
public int add(int a, int b) {
return a + b;
}
};
@Override
public IBinder onBind(Intent intent) {
return mBinder;
}
}
客户端:
public class MainActivity extends AppCompatActivity {
private IMathService mathService;
private final ServiceConnection connection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
mathService = IMathService.Stub.asInterface(service);
try {
int result = mathService.add(3, 5);
} catch (RemoteException e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
mathService = null;
}
};
}
Stub 做了什么Binder 并实现 AIDL 接口onTransact() 中读取 Parcelreply ParcelProxy 做了什么Parceltransact() 发送给远程进程Parcel 中读取结果Stub.asInterface(binder) 做了什么一句话:把一个通用的 IBinder 转换成业务接口对象,并自动屏蔽本地调用和远程调用的差异。
典型逻辑:
public static IMathService asInterface(IBinder obj) {
if (obj == null) {
return null;
}
IInterface iin = obj.queryLocalInterface(DESCRIPTOR);
if (iin != null && iin instanceof IMathService) {
return (IMathService) iin;
}
return new Proxy(obj);
}
Proxy,后续调用走 BinderParcelable一句话:因为跨进程不能共享对象内存,只能传序列化后的数据;Parcelable 是 Android 为 Parcel 和 IPC 专门优化的方案。
Parcel,对端再反序列化重建对象ParcelParcelable 的特点Intent、Bundle、AIDLSerializable 的特点ParcelableGson 的特点Parcelable 做高频 IPCSerializable:Java 通用序列化,简单但慢Parcelable:Android 高性能序列化,适合组件通信和 IPCGson:对象和 JSON 字符串互转,适合网络和跨语言交换一句话:Messenger 适合简单、低并发、消息驱动的跨进程通信;AIDL 适合复杂接口、复杂数据和高并发场景。
Handler + MessageHandler.handleMessage() 处理请求.aidlMessageMessenger 更像“发消息”AIDL 更像“调接口”服务端:
public class MessengerService extends Service {
public static final int MSG_ADD = 1;
public static final int MSG_RESULT = 2;
private final Handler handler = new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == MSG_ADD) {
int a = msg.getData().getInt("a");
int b = msg.getData().getInt("b");
int result = a + b;
Messenger client = msg.replyTo;
if (client != null) {
Message reply = Message.obtain(null, MSG_RESULT);
Bundle bundle = new Bundle();
bundle.putInt("result", result);
reply.setData(bundle);
try {
client.send(reply);
} catch (RemoteException e) {
e.printStackTrace();
}
}
} else {
super.handleMessage(msg);
}
}
};
private final Messenger messenger = new Messenger(handler);
@Override
public IBinder onBind(Intent intent) {
return messenger.getBinder();
}
}
客户端:
public class MainActivity extends AppCompatActivity {
private static final int MSG_ADD = 1;
private static final int MSG_RESULT = 2;
private Messenger serviceMessenger;
private final Messenger clientMessenger = new Messenger(
new Handler(Looper.getMainLooper()) {
@Override
public void handleMessage(@NonNull Message msg) {
if (msg.what == MSG_RESULT) {
int result = msg.getData().getInt("result");
} else {
super.handleMessage(msg);
}
}
}
);
}
Handler 创建 MessengerIBindernew Messenger(service) 构造代理send(Message) 发消息handleMessage() 中处理replyTo 回传结果一句话:Binder 擅长控制型、小中等数据量的 IPC,不适合直接传大对象,因为事务缓冲区有限,而且伴随明显的序列化、拷贝和线程切换成本。
Parcel 成本高Bundlebyte[]UriTransactionTooLargeException 的本质是什么一句话:它的本质是一次 Binder 事务写入的数据整体过大,超出了 Binder 事务缓冲区可承受范围,导致事务失败。
Intent、Bundle、页面状态保存也会触发Intent.putExtra() 传大对象Bundle 中保存太多字段onSaveInstanceState() 塞入过多页面状态onSaveInstanceState() 为什么容易触发 TransactionTooLargeException一句话:因为 onSaveInstanceState() 中保存的 Bundle 最终也要通过 Binder 传给系统进程,所以它并不是一个可以无限存数据的本地缓存容器。
BundleBundle 会被序列化成 Parcelsystem_serverFragment 状态、参数、View 层级状态会一起叠加ViewModel 更适合保存页面大对象一句话:ViewModel 保存的是当前进程内存中的运行期数据,不需要经过 Bundle 和 Binder;而 onSaveInstanceState() 只适合保存轻量恢复状态。
ViewModelBundleTransactionTooLargeExceptiononSaveInstanceState() 和持久化ViewModel:保存运行期大对象onSaveInstanceState():保存轻量恢复标记一句话:大数据不要直接放进 Binder 事务里,而是通过 Binder 传“引用、句柄或控制信息”,真实数据放到更合适的承载介质中。
Uri 或元数据ContentProviderParcelFileDescriptorContentProviderParcelFileDescriptor一句话:共享内存的典型做法是用 Binder 传递共享内存句柄,用共享内存本身承载真正的大数据。
package com.example.ipc;
import android.os.ParcelFileDescriptor;
interface ISharedMemoryService {
ParcelFileDescriptor getSharedMemoryFd();
}
@TargetApi(Build.VERSION_CODES.O_MR1)
public class SharedMemoryService extends Service {
private final ISharedMemoryService.Stub binder = new ISharedMemoryService.Stub() {
@Override
public ParcelFileDescriptor getSharedMemoryFd() throws RemoteException {
try {
SharedMemory sharedMemory = SharedMemory.create("demo_region", 1024);
ByteBuffer buffer = sharedMemory.mapReadWrite();
buffer.put("hello from server process".getBytes(StandardCharsets.UTF_8));
SharedMemory.unmap(buffer);
return ParcelFileDescriptor.dup(sharedMemory.getFileDescriptor());
} catch (ErrnoException | IOException e) {
throw new RemoteException(e.getMessage());
}
}
};
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
@TargetApi(Build.VERSION_CODES.O_MR1)
private void readSharedMemory() {
try {
ParcelFileDescriptor pfd = service.getSharedMemoryFd();
SharedMemory sharedMemory = SharedMemory.fromFileDescriptor(
pfd.getFileDescriptor()
);
ByteBuffer buffer = sharedMemory.mapReadOnly();
byte[] bytes = new byte[1024];
buffer.get(bytes);
String result = new String(bytes, StandardCharsets.UTF_8).trim();
SharedMemory.unmap(buffer);
pfd.close();
} catch (Exception e) {
e.printStackTrace();
}
}
FileDescriptor| 知识点 | 速记 |
|---|---|
| Binder 原理 | Proxy 打包 → Binder 驱动转发 → Stub 解包执行 |
| AIDL 限制 | 类型受限、线程池并发、传输有开销、大小有限 |
Stub / Proxy |
Stub 负责服务端接收分发,Proxy 负责客户端打包发送 |
asInterface() |
本地返回实现,远程返回 Proxy |
Parcelable |
Android 为 Parcel 优化的高性能序列化方案 |
| Messenger | 适合简单、低并发、消息驱动 IPC |
| 大数据传输 | 不直接塞 Binder,改传文件、句柄、共享内存、分片 |
TransactionTooLargeException |
单次 Binder 事务数据过大导致失败 |
onSaveInstanceState() |
最终也走 Binder,只能存轻量恢复状态 |
ViewModel |
适合保存运行期大对象,不适合替代持久化 |