android其他线程更新ui,Android 关于子线程更新UI的那些事

相信大家都有听过,子线程更新UI的操作。但这种说法,不是很明确。有些人说子线程更新UI会挂,而有些人说子线程可以更新UI。接下来分析下这两种情况。

先来说说子线程更新UI会挂的问题吧。

在Activity中onCreate完后,会生成一个ViewRootImpl,View的绘制都是同个它来实现的,而ViewRootImpl调用到requestLayout()来完成View的绘制操作。看下源码:

//ViewRootImpl.java

@Override

public void requestLayout() {

if (!mHandlingLayoutInLayoutRequest) {

checkThread();

mLayoutRequested = true;

scheduleTraversals();

}

}

但布局绘制或者发生变化时,会调用requestLayout(),而里面有checkThread(),来看下它的源码:

//ViewRootImpl.java

void checkThread() {

if (mThread != Thread.currentThread()) {

throw new CalledFromWrongThreadException(

"Only the original thread that created a view hierarchy can touch its views.");

}

}

其中mThread是在ViewRootImpl创建时初始化的,把创建时的线程和mThread绑定,而ViewRootImpl又是在主线程初始化的,所以mThread表示主线程。假如更新UI,会调用requestLayout() -> checkThread() -> mThread != Thread.currentThread(),判断是否在主线程,如果在子线程中,会抛异常,所以子线程更新UI才会挂。

子线程更新UI会挂的思路明确了,再来看看子线程更新UI为什么不会挂吧。

刚才讲到了,在Activity中onCreate完后,会生成一个ViewRootImpl,那么之后它就会去检查你更新UI时在哪个线程。那假如我在onCreate时去开一个子线程更新UI,此时ViewRootImpl还没创建,就不会去检测UI变化,所以在onCreate中子线程是可以更新UI的。

那又有些人说,我不在onCreate里面更新,我就在onCreate后在子线程里面去更新,它还是不会挂,这又是为什么呢?主要还是看下更新UI的方法吧

text1.setOnClickListener {

thread {

it.post {

text1.text = "change"

}

}

}

这里用一个点击事件,开子线程更新UI,运行后发现没挂,这是为什么?看下post的源码:

//View.java

public boolean post(Runnable action) {

final AttachInfo attachInfo = mAttachInfo;

if (attachInfo != null) {

return attachInfo.mHandler.post(action);

}

通过源码分析,你就知道这个mHandler是在ViewRootImpl里面赋值的,mHandler是主线程的Handler,而又掉了handler.post(),所以只要Handler在主线程,那么它post的所有的UI操作都是主线程。看起来像在子线程,实际是回到主线程更新UI。

继续看这种

class MyTestActivity:AppCompatActivity() {

private val handler = Handler()

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.ab)

}

override fun onResume() {

super.onResume()

getHandler()

}

private fun getHandler(){

thread {

handler.post {

text0.text = "change"

}

}

}

}

由于创建handler时是在主线程,所以这个handler是属于主线程的,所以其他的步骤就更上面的一样了。另一种handler的发消息更新的方式我就不写出来了,只要通过handler更新UI的,只要handler是主线程的,必定不会挂。之前有写过Handler使用的文章,有兴趣的同学可以看看~

还有一种就是调用runOnUiThread{}

class MyTestActivity:AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.ab)

}

override fun onResume() {

super.onResume()

thread()

}

private fun thread(){

thread {

runOnUiThread {

text.text = "change"

}

}

}

}

顾名思义,在主线程运行,只要调了这个方法,所有操作都在主线程里面,所有只要的操作不会挂。

总结一点,并不是说子线程可以更新,仔细点说:子线程中可以在调用主线程的Handler去更新UI,或者子线程可以调用runOnUIThread{}切换到主线程更新UI。

之前还遇到一种特殊情况,在onResume中更新UI不会挂,要是在最后一行加上 view.requestLayout()

@Override

protected void onResume() {

super.onResume();

new Thread() {

@Override

public void run() {

super.run();

try {

Thread.sleep(3000);

} catch (InterruptedException e) {

e.printStackTrace();

}

TextView view = findView(R.id.tv_content);

view.setText("我在子线程更新");

view.setBackgroundColor(Color.RED);

view.requestLayout();

}

}.start();

}

再试一下,就崩了

总结一点:实际上,就是只要你改view,不触发checkThread()就没事,而TextView的宽高不改变,也不会去触发requestLayout(),修改背景也同样。不会触发view的位置大小改变。当然,这种情况,不是每个版本的android都有用,还是要规范的去主线程更新UI。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值