问题一:Android能否在子线程中更新UI?
Android在子线程中更新UI的方法汇总(共七种)
Android可不可以在子线程中更新UI?
- 在某些情况下是可以在子线程中更新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();
}
}
- 但如果你让子线程休眠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();
}
}
- 源码问题所在: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();
}
}