プロジェクト概要
この記事では、Androidアプリケーション間でカスタムデータ構造を含むプロセス間通信(IPC)を行う方法を説明します。Androidが提供するBinderフレームワークとAIDL(Android Interface Definition Language)を活用して、複数アプリケーション間でデータを双方向にやり取りする実装例を紹介します。
実装要件
- 2つのアプリケーション間でのデータ交換
- 基本型に加えて、カスタムParcelableオブジェクトを使用
- 片方向および双方向通信のサポート
- サービスベースの通信アーキテクチャ
プロジェクト構成
2つのモジュールで構成されます:
- サービス提供側(サーバー):データを提供するバックグラウンドサービスをホスト
- サービス利用側(クライアント):サービスに接続してデータを取得/送信
サービス側実装
1. Parcelableデータモデルの作成
public class DeviceStatus implements Parcelable {
private int deviceId;
private String status;
private long timestamp;
public DeviceStatus(int deviceId, String status, long timestamp) {
this.deviceId = deviceId;
this.status = status;
this.timestamp = timestamp;
}
protected DeviceStatus(Parcel in) {
deviceId = in.readInt();
status = in.readString();
timestamp = in.readLong();
}
public static final Creator<DeviceStatus> CREATOR = new Creator<DeviceStatus>() {
@Override
public DeviceStatus createFromParcel(Parcel in) {
return new DeviceStatus(in);
}
@Override
public DeviceStatus[] newArray(int size) {
return new DeviceStatus[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(deviceId);
dest.writeString(status);
dest.writeLong(timestamp);
}
// Getters and setters
public int getDeviceId() { return deviceId; }
public void setDeviceId(int deviceId) { this.deviceId = deviceId; }
public String getStatus() { return status; }
public void setStatus(String status) { this.status = status; }
public long getTimestamp() { return timestamp; }
public void setTimestamp(long timestamp) { this.timestamp = timestamp; }
@Override
public String toString() {
return "DeviceStatus{" +
"deviceId=" + deviceId +
", status='" + status + '\'' +
", timestamp=" + timestamp +
'}';
}
}
2. AIDLインターフェースの定義
aidl/com/example/ipcdemo/IDeviceMonitor.aidl
package com.example.ipcdemo;
import com.example.ipcdemo.bean.DeviceStatus;
interface IDeviceMonitor {
DeviceStatus getCurrentStatus();
void updateStatus(in DeviceStatus status);
oneway void registerCallback(ICallback callback);
oneway void unregisterCallback(ICallback callback);
}
aidl/com/example/ipcdemo/ICallback.aidl
package com.example.ipcdemo;
interface ICallback {
void onStatusUpdated(in DeviceStatus status);
void onEvent(int eventType, String message);
}
3. サービス実装
public class DeviceMonitorService extends Service {
private static final String TAG = "DeviceMonitorService";
private final RemoteCallbackList<ICallback> callbackList = new RemoteCallbackList<>();
private DeviceStatus currentStatus;
private final IDeviceMonitor.Stub binder = new IDeviceMonitor.Stub() {
@Override
public DeviceStatus getCurrentStatus() {
return currentStatus;
}
@Override
public void updateStatus(DeviceStatus status) {
currentStatus = status;
notifyStatusUpdate(status);
}
@Override
public void registerCallback(ICallback callback) {
callbackList.register(callback);
}
@Override
public void unregisterCallback(ICallback callback) {
callbackList.unregister(callback);
}
};
private void notifyStatusUpdate(DeviceStatus status) {
int count = callbackList.beginBroadcast();
for (int i = 0; i < count; i++) {
try {
callbackList.getBroadcastItem(i).onStatusUpdated(status);
} catch (Exception e) {
Log.e(TAG, "Callback error: " + e.getMessage());
}
}
callbackList.finishBroadcast();
}
@Override
public void onCreate() {
super.onCreate();
currentStatus = new DeviceStatus(0, "initial", System.currentTimeMillis());
}
@Override
public IBinder onBind(Intent intent) {
return binder;
}
}
クライアント側実装
1. サービス接続処理
public class MainActivity extends AppCompatActivity {
private IDeviceMonitor deviceMonitor;
private ICallback callback = new ICallback.Stub() {
@Override
public void onStatusUpdated(DeviceStatus status) {
runOnUiThread(() -> {
// UI更新処理
});
}
@Override
public void onEvent(int eventType, String message) {
// イベント処理
}
};
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
deviceMonitor = IDeviceMonitor.Stub.asInterface(service);
try {
deviceMonitor.registerCallback(callback);
DeviceStatus status = deviceMonitor.getCurrentStatus();
// 初期状態を処理
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
deviceMonitor = null;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Intent intent = new Intent();
intent.setComponent(new ComponentName(
"com.example.serverapp",
"com.example.serverapp.DeviceMonitorService"
));
bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE);
}
@Override
protected void onDestroy() {
if (deviceMonitor != null) {
try {
deviceMonitor.unregisterCallback(callback);
} catch (Exception e) {
e.printStackTrace();
}
}
unbindService(serviceConnection);
super.onDestroy();
}
}
2. データ送信例
if (deviceMonitor != null) {
try {
DeviceStatus newStatus = new DeviceStatus(1, "active", System.currentTimeMillis());
deviceMonitor.updateStatus(newStatus);
} catch (Exception e) {
e.printStackTrace();
}
}