Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/android/224.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/multithreading/4.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Android 从非UI线程更新视图_Android_Multithreading_Textview_Android View_Background Thread - Fatal编程技术网

Android 从非UI线程更新视图

Android 从非UI线程更新视图,android,multithreading,textview,android-view,background-thread,Android,Multithreading,Textview,Android View,Background Thread,我对Android系统的工作原理感到困惑,特别是当它更新视图层次结构时我们都知道,我们不应该从UI(主)线程以外的任何线程更新任何视图。当我们尝试这样做时,甚至安卓系统也会抛出异常。 前几天,我试图在我的应用程序中实现一个自定义进度显示视图。因此,我开始使用标准Java线程和处理程序组合 我的发现让我惊讶,因为我能够从后台线程更新TextView new Thread(new Runnable() { @Override public void run() {

我对Android系统的工作原理感到困惑,特别是当它更新视图层次结构时我们都知道,我们不应该从UI(主)线程以外的任何线程更新任何视图。当我们尝试这样做时,甚至安卓系统也会抛出异常。 前几天,我试图在我的应用程序中实现一个自定义进度显示视图。因此,我开始使用标准Java线程和处理程序组合

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

new Thread(new Runnable() {

        @Override
        public void run() {
            mTextView.setText("I am " + Thread.currentThread().getName());
        }
    }).start();
之后,我还尝试更新其他视图,效果非常好。所以我试着在后台线程中加入一个睡眠呼叫

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$CalledFromErrorThreadException:只有 创建视图层次结构的原始线程可以接触其视图

然后,我尝试在sleep()调用前后将setText()调用放入循环100、1000次。当然,该应用程序每次都会崩溃,但我可以在我的textview上看到“睡觉前”文本


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

线程是UI线程的并行进程。当您尝试将sleep函数放入线程中时,线程的执行将停止。你问题的答案就在问题本身里面。它说-只有创建视图层次结构的原始线程才能接触其视图。另外两个线程运行一个ui线程和您创建的另一个ui线程。当你调用sleep方法时。线程停止与ui线程不同步的执行。当您的线程尝试更改textview的文本时,两个线程都不同步。在睡眠之前,线程是同步的。睡觉后,它们不同步

我用棒棒糖中的
sleep
运行你的代码片段,它崩溃了。堆栈跟踪是:

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行周围,即:

if (mLayout != null) {
    checkForRelayout();
}
我们可以查看
TextView
mLayout
是否为
null
checkForRelayout()
不会被调用,因此应用程序不会崩溃。而
mLayout
将在
TextView
onDraw
中初始化。因此,应用程序不会在第一次调用
setText
时崩溃,因为
mLayout
为空。绘制后,
mLayout
被初始化,并导致应用程序在第二次调用
setText
时崩溃

我想您应该在绘制
TextView
之前(例如在
onCreate
onResume
中)启动
线程。对吧?


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

只需使用android.os.Handler-它将始终在UI-thread上运行其可运行程序。什么时候?请参见
android/view/ViewRootImpl.java
方法名称是
checkThread
@Dogcat:我知道Handler。但我的问题是,为什么在第一种情况下系统不会崩溃?我的问题是,为什么系统的行为会像这样,只是有时抛出异常,为什么不是每次都抛出异常。Thread.sleep()是如何改变系统行为的?@pskink:事实上,我从不从非UI线程中接触UI元素,事实上我很少使用线程。我的一位同事(大学新生)做了类似的事情,所以我产生了怀疑。顺便说一句,我对“你不允许那样做”这一点产生了怀疑,因为我能做到。系统运行不一致。所以您的意思是说Thread.sleep()以某种方式初始化TextView中的mLayout。因为即使在1000次循环中调用第一个setText(),它的行为也是一样的。您可以尝试在第一次调用
setText
之前添加
sleep
。应用程序也会崩溃。在
Thread.sleep()
过程中,
TextView
已经被绘制,这也意味着
mLayout
已经初始化。他可能在做什么。我只是试着睡两秒钟而不是100毫秒,现在我的应用程序也崩溃了。@Dogcat我试着像你的代码一样睡线程100或200毫秒,但我的应用程序还是崩溃了。首先,线程不是进程。先别管sleep()调用,在没有睡眠的情况下尝试代码。代码运行良好。这就是我的疑问所在,为什么android系统会允许任何非UI线程更新视图**它不会抛出异常**。顺便说一下,UI线程和任何其他线程之间都没有同步,不管你是否让它进入睡眠状态。你在开玩笑吗??我已经告诉过你(在你最后的回答中),我知道在为Android编写代码时要使用的所有多线程关注点和相关模式。请重新阅读我的问题。