外观
setSelected、setMargins、findViewById 要在主线程吗?
标准回答
Android 的 View UI 通常采用单线程模型。对已接入界面的 View,setSelected、更新布局参数、附加或移除孩子等操作,应在拥有这棵 View 树的 UI 线程执行,普通 Activity 中通常就是主线程。
setMargins 只是修改 LayoutParams 中的字段,本身未必马上触发线程检查。但如果这些参数已经被界面使用,在后台修改仍可能与布局过程竞争;修改后也要通过合适的 setLayoutParams / requestLayout 路径通知界面。没有抛异常不等于线程安全。
findViewById 主要是查找 View 引用,并不属于应当后台化的重活。既然查找的是正在被 UI 线程维护的树,后续通常又要更新 View,直接在 UI 线程完成最清楚。可以把数据读取和计算放后台,再将最终结果投递回主线程。
AsyncLayoutInflater 是受限制的布局创建机制,不能推广成任意 View 都能在后台随意操作。postInvalidate 也只负责投递更新请求,不会保护你对共享字段的所有并发写入。
关键机制
- 后台处理文件、网络和计算;主线程应用最终 UI 状态,并检查页面或会话是否仍有效。
- 独立的新数据对象可在后台准备,再安全交接;不要让后台持续修改已发布给 UI 的共享对象。
- 主线程是执行规则,生命周期是有效性规则;切回主线程后仍要防止更新已经结束的页面。
通俗例子
后台读取设备名称,完成后通过主线程 Handler 或页面状态机制更新 TextView;快速退出时丢弃不再属于当前页面的结果。
追问与回答
用 view.post 包起来就万事大吉吗?
它可以安排代码在对应 UI 链路执行,但不自动解决页面已销毁、结果过期或任务积压。还要检查当前页面和任务身份,并避免投递大量细碎更新。线程正确只是必要条件,状态与生命周期仍要正确。
易混淆点
某次后台调用没有异常,只能说明未触发检查,不能证明这种用法受到框架保证。
参考资料
内容来源
- Android 技术札记 · 原条目
view-thread