只要参与过安卓项目开发一两年的朋友们应该清楚,为了避免UI渲染出现异常安卓框架限制UI操作只能在主线程中进行,如果贸然在子线程做了UI操作结果会怎样?我们随便写下了如下测试代码。
不出意外的话,代码执行报错抛出了名为CalledFromWrongThreadException异常,这件事告诉我们UI操作必须在主线程中进行。
那么是不是在所有情况下,子线程都不能做UI操作呢?换句话说是不是只要在子线程中操作UI就一定会抛出上述异常呢?今天我们不妨针对这个问题做下研究。
还是以上述代码为例,我们只在布局中把TextView的layout_width改成match_parent的话,业务逻辑什么都不改,再运行一下代码竟然发现一切运行正常。
怎么样?这种情况你们想到了吗?看到这里有没有颠覆自己的世界观?也就是说主线程里不能操作UI这句话并不是完全正确,那么什么情况下不正确呢?通过上面的例子来看,至少当TextView组件足够大以至于能容纳其内容时上面这句是不正确的,除非你不承认修改TextView文本是UI操作。
下面我们试图从源码(Api 28)角度来分析为什么会出现上面这种打破了安卓程序员固有思维的现象,我们先讨论出异常这种情况。从TextView设置文本的方法setText的方法调用栈可知,这个方法会调用View的requestLayout方法,而这个方法最终又会调用ViewRootImpl的checkThread方法,正是在这个方法里抛出了上述异常。
那么我们将TextView的宽度改成足够大的时候,方法的调用栈又会发生什么变化呢?我们再回到TextView的checkForRelayout方法中,在这个方法的8862行是判断TextView是否需要根据文本的内容变化来改变控件的大小。如果控件足够大的话,requestLayout方法将不会调用,所以也不至于去校验布局操作是在哪个线程处理了。
综上,子线程是可以处理UI操作的,只要没涉及到控件布局变化就行,其实也不仅限于TextView,大家有时间完全可以尝试其他控件如ImageView。
不过虽然在某些情况下可以在子线做UI操作,但是还是不建议那样做,因为在开发过程中很难去判断也没必要去判断文本内容会不会超出控件,作为开发者还是要遵守安卓开发规范尽量保证在主线程里操作UI。不过如果有一天面试的时候有人问你主线程是否可以做UI操作的话,你应该能举出上面这种情况,这样面试官一定会对你刮目相看。
评论0