import android.os.Handler; import android.os.Looper; // 异步 // 整个类只做一件事:把任意代码块 Runnable 抛到主线程执行,并且“内部消化”所有异常, // 让调用方既不用关心线程切换,也不用写 try-catch。 public class UiThread { // 1. 预先拿到主线程的 Handler,后续所有 post 都靠它。 // Looper.getMainLooper() 在应用启动时就已经存在,可以放心使用。 private static final Handler MAIN = new Handler(Looper.getMainLooper()); // 2. 内部专用包装器,唯一目的:在 run() 里包一层 try-catch, // 这样即使外部 Runnable 抛了 RuntimeException,也能被当场吃掉,不会崩溃。 private static class BaseRunnable implements Runnable { // 3. 持有真正要执行的“用户代码”。 Runnable runnable; // 4. 构造时把外部传进来的 Runnable 存起来。 public BaseRunnable(Runnable runnable) { this.runnable = runnable; } // 5. 当 Handler 在主线程回调这个方法时,先 try 再 run, // 任何 Exception(包括 NullPointer、IllegalState 等)都被捕获后直接忽略。 @Override public void run() { try { runnable.run(); // 真正执行业务代码 } catch (Exception ignore) { // 空 catch 块:故意不吃也不 log,达到“静默防崩”效果。 } } } // 6. 对外唯一 API:不管当前身处哪个线程,只要把 Runnable 丢进来, // 它最终都会跑到主线程执行,且异常全部被吞掉。 public static void run(Runnable r) { // 7. 如果调用者本身就站在主线程,就直接同步执行,省去一次 post 调度。 if (Looper.myLooper() == Looper.getMainLooper()) { try { r.run(); // 同步跑 } catch (Exception ignore) { // 同样吃掉异常,保持行为一致。 } } else { // 8. 当前是子线程,把“带防弹衣”的 BaseRunnable post 到主线程队列, // 等主线程空闲时再去执行。 MAIN.post(new BaseRunnable(r)); } } }