Android 为什么设置了HandlerThread循环器的处理程序可以与UI对象交互?

Android 为什么设置了HandlerThread循环器的处理程序可以与UI对象交互?,android,runnable,android-handlerthread,Android,Runnable,Android Handlerthread,为什么这个代码有效 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i("onCreate", Thread.currentThread().toString()); textView = (TextView) findView

为什么这个代码有效

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Log.i("onCreate", Thread.currentThread().toString());
    textView = (TextView) findViewById(R.id.textView);
    imageView = (ImageView) findViewById(R.id.imageView);

    HandlerThread thread = new HandlerThread("myHandlerThread");
    thread.start();
    mUiHandler = new Handler(thread.getLooper());
    mUiHandler.post(new Runnable() {
        @Override
        public void run() {
            Log.i("Thread: ", Thread.currentThread().toString());
            Toast.makeText(getApplicationContext(), "Hello Cats!", Toast.LENGTH_SHORT).show();
            textView.setText("Hello Cats");
            imageView.setImageResource(R.mipmap.ic_launcher);
        }
    });

}
我从某个地方读到,唯一可以与UI对象通信的线程是UI线程,或者我错过了一些东西

我有一些研究,但还没有找到答案,请帮忙,谢谢大家

这是我从日志中得到的

10-13 18:47:42.888 23841-23841/th.co.me.sampleapp I/onCreate:Thread[main,5,main]
10-13 18:47:42.891 23841-24041/th.co.me.sampleapp I/Thread::Thread[myHandlerThread,5,main]

更新1

我在@nshmura中尝试了这段代码,但出现了错误,现在我很困惑

   textView.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        HandlerThread thread = new HandlerThread("myHandlerThread");
        thread.start();
        mUiHandler = new Handler(thread.getLooper());
        mUiHandler.post(new Runnable() {
            @Override
            public void run() {
                Log.i("Thread: ", Thread.currentThread().toString());
                Toast.makeText(getApplicationContext(), "Hello Cats!", Toast.LENGTH_SHORT).show();
                textView.setText("Hello Cats");
                imageView.setImageResource(R.mipmap.ic_launcher);
            }
        });

    }
});
android.view.ViewRootImpl$CalledFromErrorThreadException:只有创建视图层次结构的原始线程才能接触其视图

试试这个代码

mHandler = new Handler();

new Thread(new Runnable() {
    @Override
    public void run () {
        // Perform long-running task here
        // (like audio buffering).
        // you may want to update some progress
        // bar every second, so use handler:
        mHandler.post(new Runnable() {
            @Override
            public void run () {
                // make operation on UI - on example
                // on progress bar.
            }
        });
    }
}).start();
检查如下:

    textView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            HandlerThread thread = new HandlerThread("myHandlerThread");
            thread.start();
            mUiHandler = new Handler(thread.getLooper());
            mUiHandler.post(new Runnable() {
                @Override
                public void run() {
                    Log.i("Thread: ", Thread.currentThread().toString());
                    Toast.makeText(getApplicationContext(), "Hello Cats!", Toast.LENGTH_SHORT).show();
                    textView.setText("Hello Cats");
                    imageView.setImageResource(R.mipmap.ic_launcher);
                }
            });

        }
    });
例外情况将发生:

  android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
      at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6556)
      at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:942)
      at android.view.ViewGroup.invalidateChild(ViewGroup.java:5081)
      at android.view.View.invalidateInternal(View.java:12719)
      at android.view.View.invalidate(View.java:12683)
      at android.view.View.invalidate(View.java:12667)
      at android.widget.TextView.checkForRelayout(TextView.java:7156)
      at android.widget.TextView.setText(TextView.java:4347)
      at android.widget.TextView.setText(TextView.java:4204)
      at android.widget.TextView.setText(TextView.java:4179)

可能在
活动.onCreate()
中未调用
ViewRootImpl.checkThread()
异常
android.view.ViewRootImpl$calledFromErrorThreadException:只有创建视图层次结构的原始线程才能接触其视图。
只有在以下情况下才会抛出:

  • 从非ui线程调用
  • 调用需要更改布局。例如,如果
    TextView
    的大小(宽度和高度)由于字符串太长而需要扩展

  • 因此,如果您的ui操作没有导致布局更改(创建或重新创建),则可能不会引发异常。

    处理程序对象向创建它的线程注册自身。它提供了一个向该线程发送数据的通道。例如,如果在活动的onCreate()方法中创建一个新的处理程序实例,则可以使用它将数据发布到主线程。可以通过Handler类发布的数据可以是消息的实例或Runnable类

    因此,根据下面的行,您的处理程序应该属于HandlerThread

    mUiHandler = new Handler(thread.getLooper());
    
    据我所知,OnCreate()是主线程,但OnClick Listener是单独的类。这意味着存在上下文差异

    例如:

    // onCreate() we are writing like this
     Toast.makeText(this, "Hello Cats!", Toast.LENGTH_SHORT).show();
    // but in Onclick method we are writing like this
     Toast.makeText(MainActivity.this, "Hello Cats!", Toast.LENGTH_SHORT).show();
    

    我没有收到您的que。请使用无参数构造函数或处理程序(Looper.getMainLooper())初始化您的处理程序。。。这样,处理程序将在主线程上运行,您可以从itI更新视图。运行与OP相同的示例,将TextView的宽度和高度设置为
    wrap\u content
    。这个例子改变了文本,难道它最终不应该要求改变版面吗?我想你没有领会我的意思……我用大小
    wrap\u content
    来触发版面改变。如果您建议的答案是正确的,那么应用程序将由于调用的
    错误线程异常而崩溃,但事实并非如此。哦,顺便说一句,我刚刚尝试了另一次使用固定大小的
    TextView
    运行,但应用程序仍然没有崩溃。也许您在创建
    之前发生了更改。在
    onCreate
    之后,将创建布局。查看
    TextView#setText
    的源代码,尝试在Resume上更改文本大小,但没有出现错误:(我尝试了这段代码,但得到了相同的错误,现在非常困惑如果这是真的,为什么在使用onClick方法上的代码时仍然会发生错误我将HandlerThread和Handler部分的创建实例移动到onClick方法中,但错误仍然发生:(顺便说一句,如果onClick运行在另一个线程中,它如何接触UI视图?我的错误您没有说onClick运行在单独的线程中,我很抱歉