尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

LoadingAndRetryManager 线程安全设计解析:子线程调用 showXxx 不崩溃的秘密

LoadingAndRetryManager 线程安全设计解析:子线程调用 showXxx 不崩溃的秘密 LoadingAndRetryManager 线程安全设计解析子线程调用 showXxx 不崩溃的秘密【免费下载链接】LoadingAndRetryManager无缝为Activity、Fragment、任何View设置加载loading、重试(retry)和无数据empty页面。项目地址: https://gitcode.com/gh_mirrors/lo/LoadingAndRetryManagerLoadingAndRetryManager 是一款开源 Android 加载状态管理框架可以无缝为 Activity、Fragment 以及任意 View 设置 loading加载中、retry重试和 empty无数据页面。很多开发者在使用时都会好奇一个问题为什么在网络请求等子线程里直接调用showLoading()、showContent()这些方法App 却完全不崩溃按说 Android 不是禁止在子线程操作 UI 吗本文将深入 LoadingAndRetryLayout.java 源码拆解它的线程安全设计揭开这个秘密。先看效果子线程调用 showXxx 真的安全吗在官方示例 MainActivity.java 中数据加载是放在一个new Thread()里模拟的加载完成后直接在子线程调用showContent()、showRetry()、showEmpty()new Thread() { Override public void run() { Thread.sleep(2000); double v Math.random(); if (v 0.8) mLoadingAndRetryManager.showContent(); else if (v 0.4) mLoadingAndRetryManager.showRetry(); else mLoadingAndRetryManager.showEmpty(); } }.start();实测结果完全不崩溃页面状态切换正常。它究竟是怎么做到的答案藏在LoadingAndRetryLayout里的两个秘密武器中。先搞懂前提为什么子线程改 UI 会崩溃Android 采用的是单线程 UI 模型所有界面的绘制、触摸事件、布局操作都必须发生在主线程UI 线程。如果子线程直接去修改 View 的可见性系统会抛出著名的CalledFromWrongThreadException异常直接导致 App 崩溃。所以子线程安全的核心问题只有一个如何把子线程里的 UI 操作安全地转移到主线程执行。LoadingAndRetryManager 的答案非常巧妙——它没有用 Handler、没有用锁而是靠一次线程判断加上一次投递。秘密武器一isMainThread() 一行代码判断当前线程先看 LoadingAndRetryLayout.java 中的线程判断方法private boolean isMainThread() { return Looper.myLooper() Looper.getMainLooper(); }Looper.myLooper()返回当前线程绑定的 LooperLooper.getMainLooper()是主线程的 Looper。两者相等说明当前就是主线程不等说明正处于子线程。这短短两行代码就是整个线程安全设计的判断中枢为后面每次调用做了分诊。秘密武器二View.post() 把操作投递到主线程队列再看showLoading()的完整实现这是整个设计的精华所在源码位置public void showLoading() { if (isMainThread()) { showView(mLoadingView); } else { post(new Runnable() { Override public void run() { showView(mLoadingView); } }); } }逻辑一目了然像不像一个红绿灯主线程调用直接执行showView()零延迟、同步切换。子线程调用把真正的 UI 操作包装成Runnable通过View.post()投递到主线程的消息队列里由主线程 Looper 取出执行。View.post()本身就绑定了主线程的 Handler所以投递出去的 Runnable 必然在主线程运行。而且它还有一个隐藏福利即使 View 尚未 attach 到 Windowpost 的 Runnable 也会先进入内部 RunQueue等 View 挂载后再执行因此在任意生命周期阶段调用都是安全的。四个 showXxx完全一致的统一套路showLoading()、showRetry()、showContent()、showEmpty()四个方法全部采用上面这套判断 投递的模板唯一区别只是切换的目标 View 不同完整源码。而 LoadingAndRetryManager.java 中的showXxx系列方法只是对LoadingAndRetryLayout对应方法的简单转发不重复实现任何线程逻辑。正是这种只在最底层做一次线程处理的设计让上层 API 保持极致的简洁——调用方完全感知不到线程的存在。为什么不用锁聊聊这套设计的精妙之处很多开发者第一反应是多线程访问为什么不加synchronized或使用原子变量其实完全没有必要因为这套设计已经把并发问题消解于无形真正的 UI 操作全部串行在主线程无论多少子线程调用showXxx最终都会转成主线程消息队列里的一个个 Runnable按 FIFO 顺序逐个执行天然串行不存在竞态条件。无锁就不会死锁避免了锁的引入也就彻底规避了死锁、锁竞争带来的性能损耗。调用方零负担业务代码不用记住必须切回主线程这条规则写起来更不容易出错。当然它也有两个需要注意的小特性切换是异步的子线程调用后页面切换发生在主线程下一次消息循环存在极短的延迟但对用户体验几乎无感。连续调用保持顺序例如子线程先showLoading()再showContent()两个 Runnable 按投递顺序执行最终一定停在showContent()状态不会错乱。小结三行代码带来的线程安全回看整个设计LoadingAndRetryManager 的线程安全秘密可以浓缩成一句话一次主线程判断 一次 post 投递。它没有使用任何复杂的并发原语仅依靠Looper对比和View.post()两个 Android 原生机制就把子线程调用 showXxx 不崩溃这件看似魔法的事情变得顺理成章。如果你也想为自己的 View 状态切换组件加上子线程安全能力不妨直接参考这套模板项目源码 中的实现就是一个非常好的范本简单、可靠、又易于理解。【免费下载链接】LoadingAndRetryManager无缝为Activity、Fragment、任何View设置加载loading、重试(retry)和无数据empty页面。项目地址: https://gitcode.com/gh_mirrors/lo/LoadingAndRetryManager创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表