提供在工作线程同步执行主线程任务的能力,调用线程会阻塞直到任务完成。
import android.os.Handler;
import android.os.Looper;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
* UI线程执行代码工具类
* <p>
* 提供在工作线程同步执行主线程任务的能力,调用线程会阻塞直到任务完成。
* 适用于需要在工作线程中获取主线程操作结果的场景。
* </p>
*/
public final class UiThreadSync {
private static final Handler MAIN = new Handler(Looper.getMainLooper());
private static final long DEFAULT_TIMEOUT_MS = 5000; // 默认超时时间5秒
private UiThreadSync() {
// 工具类,防止实例化
}
/**
* 在工作线程同步执行主线程任务
* <p>
* 如果调用线程已经是主线程,则直接执行任务;否则会将任务投递到主线程并等待执行完成。
* 注意:任务中不应包含需要消息队列的其他异步操作,以免导致死锁。
* </p>
*
* @param r 要执行的任务
* @throws RuntimeException 如果任务执行抛出异常、等待时被中断或超时
*/
public static void runCode(Runnable r) {
runCode(r, DEFAULT_TIMEOUT_MS);
}
/**
* 在工作线程同步执行主线程任务(带超时控制)
* <p>
* 如果调用线程已经是主线程,则直接执行任务;否则会将任务投递到主线程并等待执行完成。
* </p>
*
* @param r 要执行的任务
* @param timeoutMs 超时时间(毫秒),0表示无限等待
* @throws RuntimeException 如果任务执行抛出异常、等待时被中断或超时
* @throws IllegalArgumentException 如果timeoutMs为负数
*/
public static void runCode(Runnable r, long timeoutMs) {
if (r == null) {
throw new NullPointerException("Runnable cannot be null");
}
if (timeoutMs < 0) {
throw new IllegalArgumentException("Timeout cannot be negative: " + timeoutMs);
}
// 如果已经在主线程,直接执行(不抛异常)
if (Looper.myLooper() == Looper.getMainLooper()) {
r.run();
return;
}
final CountDownLatch latch = new CountDownLatch(1);
final AtomicReference<Throwable> error = new AtomicReference<>();
// 提交任务到主线程
MAIN.post(() -> {
try {
r.run();
} catch (Throwable t) {
error.set(t);
} finally {
latch.countDown();
}
});
try {
boolean completed;
if (timeoutMs > 0) {
completed = latch.await(timeoutMs, TimeUnit.MILLISECONDS);
if (!completed) {
// 超时:清理未执行的任务
MAIN.removeCallbacksAndMessages(null);
throw new RuntimeException("Timeout waiting for UI thread after " + timeoutMs + "ms");
}
} else {
latch.await(); // 无限等待
completed = true;
}
if (completed) {
Throwable t = error.get();
if (t != null) {
rethrowThrowable(t);
}
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
// 中断:清理未执行的任务
MAIN.removeCallbacksAndMessages(null);
throw new RuntimeException("Thread interrupted while waiting for UI thread", e);
}
}
/**
* 重新抛出异常,保持原始异常类型
*/
private static void rethrowThrowable(Throwable t) {
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else if (t instanceof Error) {
throw (Error) t;
} else {
throw new RuntimeException("Exception in UI thread", t);
}
}
/**
* 检查当前是否在主线程
*/
public static boolean isMainThread() {
return Looper.myLooper() == Looper.getMainLooper();
}
}
支持 Ctrl+A 全选,Ctrl+C 复制