在 Android 中,RemoteCallbackList是专门用于跨进程回调管理 的工具类,能解决多进程场景下回调的注册、解注册、批量通知及无效回调自动清理问题。以下是完整的使用步骤 + 代码示例,涵盖 AIDL 定义、服务端实现、客户端调用全流程:
一、核心前置知识
RemoteCallbackList的核心作用是管理实现IInterface的回调接口(通常是 AIDL 定义的接口),需配合AIDL 跨进程通信使用。其关键特性:
- 自动监听 Binder 死亡,清理崩溃客户端的回调;
- 线程安全的注册 / 解注册 / 遍历操作;
- 批量通知所有有效回调。
二、使用步骤(完整示例)
步骤 1:定义 AIDL 接口
需创建两个 AIDL 文件:服务接口 (供客户端调用注册 / 解注册)和回调接口(服务端向客户端推送事件)。
1.1 定义回调接口(IStatusCallback.aidl)
// IStatusCallback.aidl
package com.example.remotecallbackdemo;
// 跨进程回调接口:服务端通过此接口向客户端推送状态
interface IStatusCallback {
/**
* 状态变化回调方法
* @param status 状态值(如0=正常,1=低电量,2=故障)
* @param msg 状态描述
*/
void onStatusChanged(int status, String msg);
}
1.2 定义服务接口(IMyRemoteService.aidl)
// IMyRemoteService.aidl
package com.example.remotecallbackdemo;
// 导入回调接口
import com.example.remotecallbackdemo.IStatusCallback;
// 服务端暴露的接口:供客户端注册/解注册回调
interface IMyRemoteService {
/**
* 注册回调
* @param callback 客户端传入的回调实例
*/
void registerCallback(IStatusCallback callback);
/**
* 解注册回调
* @param callback 客户端传入的回调实例
*/
void unregisterCallback(IStatusCallback callback);
/**
* 模拟服务端主动触发状态更新(测试用)
*/
void triggerStatusUpdate(int status, String msg);
}
步骤 2:服务端实现(RemoteService.java)
在 Service 中初始化RemoteCallbackList,实现 AIDL 接口的方法,并管理回调通知:
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;
public class RemoteService extends Service {
private static final String TAG = "RemoteService";
// 核心:初始化RemoteCallbackList,泛型为回调接口类型
private final RemoteCallbackList<IStatusCallback> mCallbackList = new RemoteCallbackList<>();
// 实现AIDL服务接口的Stub类(服务端核心逻辑)
private final IMyRemoteService.Stub mBinder = new IMyRemoteService.Stub() {
@Override
public void registerCallback(IStatusCallback callback) throws RemoteException {
if (callback != null) {
mCallbackList.register(callback);
Log.d(TAG, "回调已注册,当前回调数:" + mCallbackList.getRegisteredCallbackCount());
}
}
@Override
public void unregisterCallback(IStatusCallback callback) throws RemoteException {
if (callback != null) {
boolean unregistered = mCallbackList.unregister(callback);
if (unregistered) {
Log.d(TAG, "回调已解注册,当前回调数:" + mCallbackList.getRegisteredCallbackCount());
}
}
}
@Override
public void triggerStatusUpdate(int status, String msg) throws RemoteException {
// 触发状态更新:通知所有注册的回调
notifyAllCallbacks(status, msg);
}
};
/**
* 批量通知所有有效回调
*/
private void notifyAllCallbacks(int status, String msg) {
// 1. 开始遍历回调(必须调用beginBroadcast())
int callbackCount = mCallbackList.beginBroadcast();
Log.d(TAG, "开始通知回调,有效回调数:" + callbackCount);
// 2. 遍历所有回调并调用方法
for (int i = 0; i < callbackCount; i++) {
IStatusCallback callback = mCallbackList.getBroadcastItem(i);
if (callback != null) {
try {
// 调用客户端的回调方法(跨进程通信)
callback.onStatusChanged(status, msg);
} catch (RemoteException e) {
// 客户端进程崩溃,无需处理:finishBroadcast()会自动清理
Log.e(TAG, "回调调用失败,客户端已断开:" + e.getMessage());
}
}
}
// 3. 结束遍历(必须调用finishBroadcast(),自动清理无效回调)
mCallbackList.finishBroadcast();
}
@Override
public IBinder onBind(Intent intent) {
// 返回服务Binder对象,供客户端绑定
return mBinder;
}
@Override
public void onDestroy() {
super.onDestroy();
// 服务销毁时清空回调(可选,RemoteCallbackList会自动释放)
mCallbackList.kill();
}
}
步骤 3:客户端实现(Activity/Fragment)
客户端通过绑定 Service 获取服务接口,注册回调并处理服务端的事件推送:
import android.content.ComponentName;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AppCompatActivity;
import com.example.remotecallbackdemo.IMyRemoteService;
import com.example.remotecallbackdemo.IStatusCallback;
public class ClientActivity extends AppCompatActivity {
private static final String TAG = "ClientActivity";
private IMyRemoteService mRemoteService; // 服务端接口实例
private boolean isServiceBound = false;
// 客户端回调实例(必须是Stub子类,用于跨进程通信)
private final IStatusCallback mStatusCallback = new IStatusCallback.Stub() {
@Override
public void onStatusChanged(int status, String msg) throws RemoteException {
// 服务端回调触发(运行在Binder线程池,需切换到主线程更新UI)
runOnUiThread(() -> {
Log.d(TAG, "收到服务端回调:status=" + status + ", msg=" + msg);
// TODO: 更新UI(如显示状态提示)
});
}
};
// 服务连接回调
private final ServiceConnection mServiceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
// 绑定成功:获取服务端接口实例
mRemoteService = IMyRemoteService.Stub.asInterface(service);
isServiceBound = true;
Log.d(TAG, "服务绑定成功");
// 注册回调到服务端
try {
mRemoteService.registerCallback(mStatusCallback);
} catch (RemoteException e) {
Log.e(TAG, "注册回调失败:" + e.getMessage());
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
// 服务断开(如服务崩溃)
mRemoteService = null;
isServiceBound = false;
Log.d(TAG, "服务断开连接");
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_client);
// 绑定服务按钮
Button btnBindService = findViewById(R.id.btn_bind_service);
btnBindService.setOnClickListener(v -> bindRemoteService());
// 触发服务端更新状态按钮(测试用)
Button btnTriggerUpdate = findViewById(R.id.btn_trigger_update);
btnTriggerUpdate.setOnClickListener(v -> triggerStatusUpdate());
}
/**
* 绑定远程服务
*/
private void bindRemoteService() {
Intent intent = new Intent(this, RemoteService.class);
// 注意:Android 5.0+ 隐式Intent绑定Service需设置包名,或用显式Intent
intent.setPackage("com.example.remotecallbackdemo");
bindService(intent, mServiceConnection, BIND_AUTO_CREATE);
}
/**
* 测试:触发服务端更新状态,通知所有客户端
*/
private void triggerStatusUpdate() {
if (!isServiceBound || mRemoteService == null) {
Log.w(TAG, "服务未绑定,无法触发更新");
return;
}
try {
// 调用服务端方法,触发回调通知
mRemoteService.triggerStatusUpdate(1, "低电量警告:剩余续航不足50km");
} catch (RemoteException e) {
Log.e(TAG, "触发状态更新失败:" + e.getMessage());
}
}
@Override
protected void onDestroy() {
super.onDestroy();
// 解注册回调 + 解绑服务,避免内存泄漏
if (isServiceBound && mRemoteService != null) {
try {
mRemoteService.unregisterCallback(mStatusCallback);
} catch (RemoteException e) {
Log.e(TAG, "解注册回调失败:" + e.getMessage());
}
unbindService(mServiceConnection);
isServiceBound = false;
}
}
}
步骤 4:配置 Service(AndroidManifest.xml)
确保 Service 在 Manifest 中注册(若为跨应用通信,需设置android:exported="true"):
<service
android:name=".RemoteService"
android:enabled="true"
android:exported="true"
android:process=":remote"> <!-- 可选:让Service运行在独立进程,模拟跨进程场景 -->
</service>
三、关键注意事项
-
回调遍历规范 必须通过
beginBroadcast()开始遍历,finishBroadcast()结束遍历,二者必须配对使用 。finishBroadcast()会自动移除已死亡的回调(如客户端崩溃)。 -
注册 / 解注册匹配 客户端注册和解注册必须使用同一个回调实例 (如上例的
mStatusCallback),否则RemoteCallbackList无法识别(内部通过 Binder 对象的唯一标识匹配)。 -
线程处理 服务端调用回调方法时,客户端的
onStatusChanged()运行在Binder 线程池 ,需通过runOnUiThread()切换到主线程更新 UI。 -
内存泄漏防护 客户端退出时必须解注册回调 + 解绑服务 ;服务端销毁时可调用
mCallbackList.kill()清空所有回调。 -
跨应用通信 若服务端和客户端属于不同应用,需确保 AIDL 文件的包名、接口名完全一致,且 Service 的
android:exported="true"。
四、与普通集合(如 ArrayList)的对比
| 特性 | RemoteCallbackList | ArrayList<IStatusCallback> |
|---|---|---|
| 跨进程回调管理 | 原生支持,自动处理 Binder 死亡 | 需手动检测,易出现空指针 |
| 线程安全 | 所有操作线程安全 | 需手动加锁(如 synchronized) |
| 无效回调清理 | 自动清理(finishBroadcast) | 需遍历检测 RemoteException |
| 批量通知效率 | 高效(内部优化遍历逻辑) | 低效(需逐个处理异常) |
五、适用场景
- 多进程通信中的事件推送(如车载系统中服务向多个应用通知车辆状态);
- 需要管理大量跨进程回调的场景(如推送服务向多个客户端推送消息);
- 需自动清理无效回调的场景(避免因客户端崩溃导致内存泄漏)。
通过RemoteCallbackList,可安全、高效地实现 Android 跨进程回调管理,是多进程通信的最佳实践之一。