iOS で、現在の実行スレッドを待機 (ブロック) し、メイン ループを実行して、メイン キューの次の実行スレッドを実行できるようにする場合は、次のように呼び出します。
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate date]];
Androidで同等のことを行うにはどうすればよいですか?
これは実際にAndroidで行うことができます。Shachar の答えは正しい方向に進んでいます。問題は、メインループがブロックされることではありません (コードがメインスレッドで実行された場合を除きますが、それは質問が提案しているものではありません)。問題は、他のスレッドがブロックされず、while ループで単純にループして CPU サイクルを消費していることです。アプリで使用する main メソッドでのブロッキング実行を次に示します。
/**
* Runs the runnable on the main UI thread. If called from a thread other than the UI thread,
* this method will block the calling thread and return only after the runnable has completed
* execution on the main UI thread.
* @param runnable Runnable to run on the main UI thread
*/
public static void blockingRunOnMain(Runnable runnable) {
if (Looper.myLooper() == Looper.getMainLooper()) { // Already on UI thread run immediately
runnable.run();
}
else { // Queue to run on UI thread
final MainRunMonitor lock = new MainRunMonitor();
Handler mainHandler = new Handler(Looper.getMainLooper());
mainHandler.post(runnable);
// Task to notify calling thread when runnable complete
mainHandler.post(new Runnable() {
@Override
public void run() {
synchronized (lock) {
lock.mRunComplete = true;
lock.notify();
}
}
});
// Block calling thread until runnable completed on UI thread
boolean interrupted = false;
try {
synchronized (lock) {
while (!lock.mRunComplete) {
try {
lock.wait();
} catch (InterruptedException e) {
// Received interrupt signal, but still haven't been notified, continue waiting
interrupted = true;
}
}
}
} finally {
if (interrupted) {
Thread.currentThread().interrupt(); // Restore interrupt to be used higher on call stack (we're not using it to interrupt this task)
}
}
}
}
MainRunMonitor
単純なクラスです。私の場合は、実装するクラスのプライベート内部クラスですblockingRunOnMain()
。
/**
* Monitor to lock calling thread while code is executed on UI thread.
*/
private static class MainRunMonitor {
private boolean mRunComplete = false;
}
blockingRunOnMain()
Runnable
メインスレッドで実行するために a を渡すことによって使用されます。
blockingRunOnMain(new Runnable() {
@Override
public void run() {
workToDoSynchronouslyOnMain();
}
});
メソッドの最初の部分では、blockingRunOnMain()
メソッドがメイン スレッドから呼び出されているかどうかを確認し、そうであれば、コードをただちに実行します。の関数は、メソッドが戻る前にコードblockingRunOnMain()
を同期的に実行することRunnable
であるため、メイン スレッド自体から呼び出された場合でも、これは同じ結果になります。
メソッドがメイン スレッド以外のスレッドから呼び出された場合、メイン スレッドの にバインドされている に をRunnable
ポストします。パラメーターを投稿した後、実行がs とs を順番に投稿したため、パラメーターの実行が完了した後に実行される別のパラメーターを投稿します。この 2 番目は、メイン スレッドで作業が完了したことをブロックされたスレッドに通知する役割を果たします。Handler
Looper
Runnable
Runnable
Runnable
Handler
Message
Runnable
Runnable
2 番目を投稿した後Runnable
、バックグラウンド スレッドをブロックし、通知されるまで待ちます。lock
操作が各スレッドでアトミックになるように、実行される操作を同期することが重要です。
バックグラウンド スレッドはwait()
モニターで呼び出し、 まで待機しmRunComplete == true
ます。を取得した場合はInterruptedException
、タスクをキャンセルするために割り込みメカニズム自体を使用していないため、待機を継続し、完了後にスレッドの中断された状態を復元することが重要です。それを復元すると、呼び出しスタックの上位にある別のメソッドが割り込みを処理します。「InterruptedException の処理」を参照してください。
パラメータのRunnable
実行が完了し、2 番目にポストされたRunnable
実行が実行されると、単純mRunComplete
に true に設定され、ブロックされたスレッドに実行を継続するよう通知します。この結果は、メイン UI スレッドでパラメータを同期的に実行したmRunComplete == true
から返されるようになりました。blockingRunOnMain()
Runnable
One short workaround is to have a boolean that is changed by the next main thread loop.
running on main thread can be done with runOnUIthread
(or getting the main looper yourself)
moving to the next loop can b easely done with handler.postDelayed(Runnable run, long delayMills)
, and a no-time delay.
so you could do this:
nextMainLoopDone = false;//This should be changed to a thread safe boolean, could use AtomicBoolean
new Handler(Looper.getMainLooper()).postDelayed(new Runnable() {
@Override
public void run() {
nextMainLoopDone = true;
}
}, 1/* delay for no time, just to next loop*/);
while(!nextMainLoopDone) {
;
}