Android开发UI之在子线程中更新UI组件

问题一:Android能否在子线程中更新UI?

Android在子线程中更新UI的方法汇总(共七种)
Android可不可以在子线程中更新UI?

  1. 在某些情况下是可以在子线程中更新UI的! 如:在一个activity的xml文件中中随便写一个TextView文本控件,然后在Activity的onCreate方法中开启一个子线程并在该子线程的run方法中更新TextView文本控件,你会发现根本没有任何问题。
package com.example.uithread;
import android.os.Bundle;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
    private TextView mContent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
    private void initView() {
        mContent = (TextView) findViewById(R.id.content);
        //在主线程里开启子线程准备用于更新UI组件
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
//                    Thread.sleep(2000);//让子线程休眠200毫秒;
                    mContent.setText("Hello World!!!!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

Android开发UI之在子线程中更新UI组件_第1张图片

  1. 但如果你让子线程休眠2秒就会报错了如下代码:
package com.example.uithread;
import android.os.Bundle;
import android.widget.TextView;

import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
    private TextView mContent;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
    }
    private void initView() {
        mContent = (TextView) findViewById(R.id.content);
        //在主线程里开启子线程准备用于更新UI组件
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);//让子线程休眠200毫秒;
                    //更新文本内容
                    mContent.setText("Hello World!!!!");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

Android开发UI之在子线程中更新UI组件_第2张图片

  1. 源码问题所在:throw new CalledFromWrongThreadException异常里面的内容跟上面爆红一模一样
public void invalidateChild(View child, Rect dirty) {
  //关键的地方就是这个方法
    checkThread();
  //后边的全部省略
}
 void checkThread() {
    //在这里mThread表示的是主线程,程序作了判断,检查当前线程是不是主线程,如果不是就会抛出异常
    if (mThread != Thread.currentThread()) {
        throw new CalledFromWrongThreadException(
                "Only the original thread that created a view hierarchy can touch its views.");
    }
}

because:为什么我们在不让子线程休眠的情况下去更新TextView文本可以,而让线程休眠两秒后就出抛异常呢?根本原因就是ViewRootImpl到底是在哪里被初始化的!ViewRootImpl是在onResume中初始化的,而我们开启的子线程是在onCreat方法中,这个时候程序没有去检测当前线程是不是主线程,所以没有抛异常!!

Android子线程更新UI的方法总结:

消息机制,对于Android开发者来说是常用的,对于处理有着大量交互的场景,采用消息机制是再好不过的,有些特殊的场景,eg:在Android开发中,子线程不能更新UI,而主线程又不能进行耗时操作,一种常见的处理方法就是,在子线程进行耗时操作,完成后发送信息通知主线程更新UI,或使用异步任务,异步任务的实质也是对消息机制的封装.

如上述报错问题我们可以利用消息机制(Handler机制)来解决

package com.example.uithread;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
public class MainActivity extends AppCompatActivity {
    private TextView mContent;
    private Handler handler;//Handler机制
    private int message=0x11;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
         handler=new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                if (msg.what==message){
                    try {
                        Thread.sleep(200);
                        mContent.setText("Hello World!!!");
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }
    private void initView() {
        mContent = (TextView) findViewById(R.id.content);
        //在主线程里开启子线程准备用于更新UI组件
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
          //handler发送消息告诉thread知道更新组件的任务交给我handler就可以了
                    handler.sendEmptyMessage(message);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
}

Android开发UI之在子线程中更新UI组件_第3张图片

你可能感兴趣的:(Android技术栏)