从非UI线程更新视图 [英] Updating Views from non-UI threads

查看:158
本文介绍了从非UI线程更新视图的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我对Android系统的工作方式感到困惑,尤其是当它更新视图层次结构时. 我们都知道,除了UI(主)线程之外,我们不应从其他任何线程更新任何视图.当我们尝试这样做时,甚至 Android系统也会引发异常. 前几天,我试图在应用程序中实现显示视图的自定义进度.因此,我开始使用标准Java线程和处理程序组合.

I am confused about how the Android system works, specially when it updates the view hierarchy. We all know that we should not update any view from any thread other than UI (Main) thread. And even Android system throws exception when we try to do so. The other day I was trying to implement a custom progress showing view in my application. So I started with using standard Java threads and handler combo.

我发现我很惊讶,因为我能够从后台线程更新TextView.

What I found surprised me as I was able to update a TextView from background thread.

new Thread(new Runnable() {

        @Override
        public void run() {
            mTextView.setText("I am " + Thread.currentThread().getName());
        }
    }).start();

之后,我也尝试更新其他视图,效果很好.因此,我尝试在后台线程中进行睡眠调用.

After which I tried updating other views also, which worked pretty well. So I tried putting a sleep call in background Thread.

new Thread(new Runnable() {

        @Override
        public void run() {
            mTextView.setText("Thread : before sleep");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            mTextView.setText("Thread : after sleep");
        }
    }).start();

它如预期的那样崩溃

android.view.ViewRootImpl $ CalledFromWrongThreadException:仅 创建视图层次结构的原始线程可以触摸其视图.

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.

然后我尝试将setText()调用在sleep()调用之前和之后放在循环中100次,1000次.当然,该应用程序每次都会崩溃,但我能够在textview上看到睡眠前"文本.

Then I tried putting the setText() calls in loop for 100, 1000 times before and after the sleep() call. Of course the app crashes every single time but I was able to see the "Before Sleep" text on my textview.

所以我的问题是 系统何时检测到某些非UI线程正在尝试更新视图 .而且 为什么在非UI线程中没有sleep()调用时它不起作用 ?

So my question is when does system detect that some non-UI thread is trying to update the view. And why does not it work when there is no sleep() call in non-UI thread ?

推荐答案

我在Lollipop中使用sleep运行您的代码段,它崩溃了.堆栈跟踪为:

I run your code snippet with sleep in Lollipop and it crashes. The stack trace is:

android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
        at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6357)
        at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:909)
        at android.view.ViewGroup.invalidateChild(ViewGroup.java:4690)
        at android.view.View.invalidateInternal(View.java:11801)
        at android.view.View.invalidate(View.java:11765)
        at android.view.View.invalidate(View.java:11749)
        at android.widget.TextView.checkForRelayout(TextView.java:6850)
        at android.widget.TextView.setText(TextView.java:4057)
        at android.widget.TextView.setText(TextView.java:3915)
        at android.widget.TextView.setText(TextView.java:3890)
        at com.test.MainActivity$16.run(MainActivity.java:1126)
        at java.lang.Thread.run(Thread.java:818)

因此,该键隐藏在TextView.setText的4057行附近:

So the key hides around line 4057 of TextView.setText which is:

if (mLayout != null) {
    checkForRelayout();
}

我们可以看到TextViewmLayout是否为nullcheckForRelayout()不会被调用,因此该应用程序不会崩溃.并且mLayout将在TextViewonDraw中初始化.因此,应用程序不会在第一次调用setText时崩溃,因为mLayout为null.绘制后,mLayout会初始化,并导致应用程序在第二次调用setText时崩溃.

We can see if the mLayout of the TextView is null, checkForRelayout() won't be called and thus the app will not crash. And the mLayout will be initialized in onDraw of TextView. So the app doesn't crash the first time setText is called because mLayout is null. After drawing, mLayout is initialized and cause the app to crash the second time setText is called.

我想您是在绘制TextView之前先启动Thread的(例如在onCreateonResume中).对吧?

I guess you start the Thread before the TextView is drawn (e.g. in onCreate or onResume). Right?

应用程序是否崩溃取决于您调用setText的时间.如果在第一次绘制TextView之前调用setText,一切正常.否则,应用程序将崩溃.

Whether the app crashes or not depends on the TIME you call setText. If you call setText before the TextView is first drawn, everything is ok. Otherwise the app crashes.

这篇关于从非UI线程更新视图的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆