AndroidにおけるカスタムParcelableオブジェクトとAIDLを用いたプロセス間通信の実装

プロジェクト概要

この記事では、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();
    }
}

タグ: aidl Binder parcelable IPC AndroidService

5月20日 04:39 投稿