UI线程执行代码工具类

2026年01月23日 06:28

描述

提供在工作线程同步执行主线程任务的能力,调用线程会阻塞直到任务完成。

标签

Android Java
UI线程执行代码工具类.txt 3,559 字符 | 123 行
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 复制