Looperのスレッドを作成し、すぐにメッセージを送信するにはどうすればよいですか?
質問
私は、バックグラウンドでメッセージを処理するワーカスレッドを持っています。このようなものです。
class Worker extends Thread {
public volatile Handler handler; // actually private, of course
public void run() {
Looper.prepare();
mHandler = new Handler() { // the Handler hooks up to the current Thread
public boolean handleMessage(Message msg) {
// ...
}
};
Looper.loop();
}
}
メインスレッド(UIスレッド、関係ないですが)から、以下のようなことをしたいと思います。
Worker worker = new Worker();
worker.start();
worker.handler.sendMessage(...);
問題は、これによって美しいレースコンディションが発生することです。
worker.handler
が読み込まれた時点で、ワーカスレッドがこのフィールドに既に割り当てていることを確認する方法がありません!
を単純に作成することはできません。
Handler
から
Worker
のコンストラクタはメインスレッドで実行されるからです。
Handler
はそれ自体を間違ったスレッドに関連付けることになります。
これはほとんど珍しいシナリオではないようです。私はいくつかの回避策を思いつきますが、どれも醜いものばかりです。
-
このようなものです。
class Worker extends Thread { public volatile Handler handler; // actually private, of course public void run() { Looper.prepare(); mHandler = new Handler() { // the Handler hooks up to the current Thread public boolean handleMessage(Message msg) { // ... } }; notifyAll(); // <- ADDED Looper.loop(); } }
そしてメインスレッドから
Worker worker = new Worker(); worker.start(); worker.wait(); // <- ADDED worker.handler.sendMessage(...);
しかし、これも信頼できるものではありません。
notifyAll()
が起こる前にwait()
の前に起きたら、絶対に起きない! -
初期値を渡す
Message
をWorker
のコンストラクタにrun()
メソッドにポストさせます。アドホックな解決策ですが、複数のメッセージや、すぐに送信せずにすぐに送信したい場合にはうまくいきません。 -
が送信されるまで忙しく待ちます。
handler
フィールドがnull
. うん、最後の手段だ...。
を作成したいと思います。
Handler
と
MessageQueue
を代表して
Worker
のスレッドに代わって、しかしこれは可能ではないようです。これを解決する最もエレガントな方法は何でしょうか?
どのように解決するのですか?
最終的な解決方法(エラーチェックを除く)は、CommonsWareのおかげです。
class Worker extends HandlerThread {
// ...
public synchronized void waitUntilReady() {
d_handler = new Handler(getLooper(), d_messageHandler);
}
}
そしてメインスレッドから
Worker worker = new Worker();
worker.start();
worker.waitUntilReady(); // <- ADDED
worker.handler.sendMessage(...);
のセマンティクスのおかげで、これは動作します。
HandlerThread.getLooper()
のセマンティクスのおかげで、ルーパーが初期化されるまでブロックされます。
ちなみに、これは上記の私の解決策その1に似ています。
HandlerThread
はおおよそ次のように実装されています (オープンソースを愛さなければなりません)。
public void run() {
Looper.prepare();
synchronized (this) {
mLooper = Looper.myLooper();
notifyAll();
}
Looper.loop();
}
public Looper getLooper() {
synchronized (this) {
while (mLooper == null) {
try {
wait();
} catch (InterruptedException e) {
}
}
}
return mLooper;
}
重要な違いは、ワーカスレッドが実行されているかどうかではなく、実際にルーパーを作成したかどうかをチェックしていることです。そしてその方法は、ルーパーをプライベートフィールドに保存することです。ナイス!
関連
-
[解決済み】Looper.prepare()を呼び出していないスレッド内でハンドラを作成できない。)
-
[解決済み】クロススレッド操作が有効でない。作成されたスレッド以外のスレッドからアクセスされたコントロール
-
[解決済み] 他のスレッドからGUIを更新するにはどうすればよいですか?
-
[解決済み] グリッドレイアウトにおけるフリングジェスチャーの検出
-
[解決済み] Looperの目的、使い方を教えてください。
-
[解決済み] RecyclerView.Stateを使って、RecyclerViewのスクロール位置を保存するには?
-
[解決済み] Android Webview - キャッシュを完全に削除する
-
[解決済み] アプリ内課金テスト:android.test.purchased already owned
-
[解決済み] アンドロイドでシェイクを検出するには?
-
[解決済み] アダプタからActivityメソッドを呼び出す
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] Nexus 4でUSBデバッグモードを見つける方法とオンにする方法
-
[解決済み] 深くネストされたスタックから離れるとき、Fragmentのバックスタックをクリーンアップする方法はこれで良いのでしょうか?
-
[解決済み] HttpPostによる画像送信
-
[解決済み] getApplication()、getApplicationContext()、getBaseContext()、someClass.thisの違いと使い分け。
-
[解決済み] Androidのソースコードにある@hideの意味とは?
-
[解決済み] Android Navigation Architecture Component - 現在表示されているフラグメントを取得する
-
[解決済み] XMLで矩形を描画できますか?
-
[解決済み] Android端末がHDPI画面かMDPI画面かを確認する方法は?
-
[解決済み] 非推奨のandroid.support.v4.app.ActionBarDrawerToggleの置き換え方法
-
[解決済み] アンドロイドボタンセレクター