Android アプリでクラス オブジェクトのリストを一元的に更新してアクセスするためのスレッド セーフな方法を実装する際に問題が発生しています。これは公開タイプの質問であり、多くのソース コードを提供することはできません。
基本的に、アクティビティとサービスを備えたアプリがあります。サービスは、UDP ブロードキャスト タスクと UDP ブロードキャスト リスニング タスクを実装します。ブロードキャストされた情報は、基本的に、デバイスを表すクラスの JSON シリアル化されたコピーです。例: UUID、IP、ユーザー セット名、説明などの管理情報。JSON は UDP パケットに格納されて送信され、UDP パケットは受信され、デシリアライズされて処理されます。その逆シリアル化されたクラスは Hashtable に格納され、現在のインスタンスは Service に属します。たとえば、データへのアクセスを希望するすべての人は、サービスを通過する必要があります。全体が非常に非同期です。
Activity は (Binder の拡張を介して) サービスにバインドされるため、UDP タスクの開始/停止などの Service メソッドを呼び出すことができます。アクティビティは、更新または新しいデバイス データが受信されたときにサービスが発行する Android インテントをリッスンし、受信した UDP パケット データに関連する情報を UI に表示します。パケット データは、Service に属するコンテナ クラスに格納されていることに注意してください。
問題は、受信したデータのハッシュテーブルをスレッドセーフにする適切な方法を見つけられないことです。Service メソッドからデータを取得して処理すると、java.util.ConcurrentModificationException エラーが発生します。ループ (反復子または for) でデータを処理している間にデータが更新されると、ConcurrentModificationException が発生します。どこで、いつ、なぜなのかはわかっていますが、lock() または ReentrantLock() の使用は、通常、そのコンテナー クラスの外部で処理するために返される単一ポイント データではなく、データをロックするクラスのメソッド呼び出し内で利用されます。次のようなもの: (ReentrantLock() ではなく syncronized を使用している場合、これは単なる例です)
public class sampleLockClass {
private Hashtable<String, String> sampleData = new Hashtable<String, String>();
public sampleLockClass() {}
public synchronized put(String s1, String s2) {
this.sampleData().put(s1, s2);
}
public synchronized Hashtable<String, String> getAll() {
return this.sampleData; // This is returned for the processing outside the class
}
}
この場合、 getAll() メソッドは、クラス自体の外部での処理に必要な sampleData Hashtable を返しています。この理由は、私が利用している他の API にデータが渡され、それらの API がこのアプローチと互換性がないためです。たとえば、その使用のために単一のスレッドセーフなコピーがあることを期待しています。
これはばかげているか問題ではないかもしれませんが、返された sampleData を必要な期間だけスレッドセーフにするにはどうすればよいでしょうか? 現時点では、Service だけが sampleData に書き込むことに注意してください。それ以外はすべて読み取り専用であり、Intent を介して CommService を介して Activity から sampleData への更新をコミットしようとする可能性があります。
get() メソッドごとに Hashtable sampleData のコピーを作成する方が安全でしょうか?