以下是一份基于AIDL观察者模式实现服务端数据自动变化并回调客户端的技术文档,采用Java实现,结合最佳实践和线程安全设计。


AIDL观察者模式技术文档:服务端数据变化回调机制

适用场景:跨进程数据实时同步(如股票报价、传感器数据推送)


一、核心架构设计

1. 定义AIDL接口
2. 定时数据生成
3. 管理客户端回调
4. 注册回调
5. 数据变更通知
服务端
回调接口
数据模拟器
RemoteCallbackList
客户端

二、实现步骤与代码

1. 定义数据模型(Parcelable)
// Stock.java
public class Stock implements Parcelable {
    private String symbol;
    private float price;

    // Parcelable实现
    protected Stock(Parcel in) {
        symbol = in.readString();
        price = in.readFloat();
    }
    public static final Creator<Stock> CREATOR = new Creator<Stock>() {
        @Override public Stock createFromParcel(Parcel in) { return new Stock(in); }
        @Override public Stock[] newArray(int size) { return new Stock[size]; }
    };
    @Override public void writeToParcel(Parcel dest, int flags) {
        dest.writeString(symbol);
        dest.writeFloat(price);
    }
    // Getters & Setters
}
2. 定义AIDL接口
  • 回调接口 (IStockCallback.aidl)
    package com.example.stockservice;
    import com.example.stockservice.Stock;
    oneway interface IStockCallback {
        void onStockChanged(in Stock stock); // 单向非阻塞
    }
    
  • 服务接口 (IStockService.aidl)
    package com.example.stockservice;
    import com.example.stockservice.IStockCallback;
    interface IStockService {
        oneway void registerCallback(IStockCallback callback); // 注册
        oneway void unregisterCallback(IStockCallback callback); // 注销
    }
    
3. 服务端实现
// StockService.java
public class StockService extends Service {
    private final RemoteCallbackList<IStockCallback> callbacks = new RemoteCallbackList<>();
    private Stock currentStock = new Stock("AAPL", 150.0f);
    private final Handler handler = new Handler(Looper.getMainLooper());

    // 定时更新数据
    private final Runnable updateTask = new Runnable() {
        @Override public void run() {
            currentStock.setPrice(140 + new Random().nextInt(20)); // 模拟价格波动
            notifyClients(); // 通知所有客户端
            handler.postDelayed(this, 10000); // 10秒间隔
        }
    };

    private void notifyClients() {
        int count = callbacks.beginBroadcast();
        for (int i = 0; i < count; i++) {
            try {
                callbacks.getBroadcastItem(i).onStockChanged(currentStock);
            } catch (RemoteException e) {
                // 客户端进程终止时自动清理
            }
        }
        callbacks.finishBroadcast();
    }

    private final IStockService.Stub binder = new IStockService.Stub() {
        @Override
        public void registerCallback(IStockCallback callback) {
            callbacks.register(callback);
        }
        @Override
        public void unregisterCallback(IStockCallback callback) {
            callbacks.unregister(callback);
        }
    };

    @Override
    public void onCreate() {
        super.onCreate();
        handler.post(updateTask); // 启动定时任务
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    @Override
    public void onDestroy() {
        handler.removeCallbacks(updateTask);
        callbacks.kill(); // 释放资源
        super.onDestroy();
    }
}
4. 客户端实现
// ClientActivity.java
public class ClientActivity extends AppCompatActivity implements IStockCallback {
    private IStockService stockService;

    private final ServiceConnection conn = new ServiceConnection() {
        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            stockService = IStockService.Stub.asInterface(service);
            try {
                stockService.registerCallback(ClientActivity.this); // 注册回调
            } catch (RemoteException e) { e.printStackTrace(); }
        }
        @Override
        public void onServiceDisconnected(ComponentName name) {
            stockService = null;
        }
    };

    @Override
    public void onStockChanged(Stock stock) {
        runOnUiThread(() -> {
            // 更新UI
            textView.setText(String.format("%s: %.2f", stock.getSymbol(), stock.getPrice()));
        });
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 绑定服务
        Intent intent = new Intent();
        intent.setComponent(new ComponentName(
            "com.example.stockservice", 
            "com.example.stockservice.StockService"
        ));
        bindService(intent, conn, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onDestroy() {
        if (stockService != null) {
            try {
                stockService.unregisterCallback(this);
            } catch (RemoteException e) { 
           		e.printStackTrace();
            }
            unbindService(conn);
        }
        super.onDestroy();
    }
}

三、关键特性与优化

特性 实现方案 优势
线程安全回调管理 使用 RemoteCallbackList 自动处理客户端进程死亡导致的泄漏
非阻塞通知 AIDL接口声明为 oneway 避免服务端线程阻塞
跨进程数据传递 数据类实现 Parcelable 高效序列化/反序列化
后台限制适配 服务端使用前台服务(需声明权限) 兼容 Android 12+ 后台限制

四、常见问题排查

  1. 回调不触发

    • 检查服务端是否用 RemoteCallbackList 而非 ArrayList(否则无法感知客户端死亡)
    • 确认客户端 onServiceConnected 中正确调用 registerCallback()
  2. 跨进程传递失败

    • 确保所有自定义数据类型实现 Parcelable
    • 验证 AIDL 文件中的包名和导入路径是否一致
  3. 服务绑定失败

    • AndroidManifest 中声明服务并设置 exported="true"
    <service android:name=".StockService" 
             android:exported="true"
             android:permission="android.permission.FOREGROUND_SERVICE"/>
    

五、完整项目结构

├── app (客户端)
│   ├── src/main/java/com/example/client
│   │   ├── ClientActivity.java
│   └── AndroidManifest.xml
│
├── service (服务端)
│   ├── src/main/aidl/com/example/stockservice
│   │   ├── IStockCallback.aidl
│   │   └── IStockService.aidl
│   ├── src/main/java/com/example/stockservice
│   │   ├── Stock.java
│   │   └── StockService.java
│   └── AndroidManifest.xml

源码参考AIDL回调机制详解 | RemoteCallbackList使用指南

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐