一般而言,android中相关的view和控件不是线程安全的,Android会禁止在非UI线程更新UI,对于显式的非法操作,比如说直接在Activity里创建子线程,然后直接在子线程中操作UI等,Android会直接异常退出,并提示should run on UIThread之类的错误日志信息。而对于隐式的非法操作,App不会直接简单粗暴地异常退出,只是出现奇怪的结果,Only the original thread that created a view hierarchy can touch its views便是一个例子,字面意思是只有创建视图层次结构的原始线程才能操作它的View。
但我们有时候会遇到:在非UI线程中运行UI操作而不报错
比如:
public class MainActivity extends AppCompatActivity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView =(TextView)findViewById(R.id.textview);
change_text();
}
public void change_text(){
new Thread(){
@Override
public void run(){
super.run();
try {
textView.setText("改变文字信息");
}
catch (Exception E){
E.printStackTrace();
}
}
}.start();
}
}
这段代码把更新UI操作放在一个非UI线程中,按道理说会出错,接下来看看效果:
excuse me?居然成功了?
不要急,我们在try语句里面添加sleep(3000),让这个线程sleep3秒试试:
try {
sleep(3000);
textView.setText("改变文字信息");
}
看看效果:
这次UI没有更新成功了,还是系统初始化的“Hello World”。
看看Android Monitor里面:
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
现在大家应该知道为什么刚刚能更新成功了吧,因为Main Thread(也就是UI Thread)一开始并没有初始化完成,等到3秒后,这时候Main Thread肯定已经初始化完成了,这个时候我们在其他线程中更新UI,是不行的,就会像上面一样
Only the original thread that created a view hierarchy can touch its views.
正确的方法:(1)直接在Main Thread(也就是UI Thread)里面进行UI更新操作,这个不多说。
(2)或者利用handler进程间通信,把UI操作放进Main Thread,如下:
①.在主线程中创建handler,因为handler与创建handler所在的线程绑定
②.handleMessage(msg)方法里再处理UI更新,这个时候UI更新操作就会被放到UI线程里面去了;
public class MainActivity extends AppCompatActivity {
TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView =(TextView)findViewById(R.id.textview);
change_text();
}
public void change_text(){
new Thread(){
@Override
public void run(){
super.run();
try {
sleep(3000);
}
catch (Exception E){
E.printStackTrace();
}
Message mg=Message.obtain();
mg.arg1=1;
myHandler.sendMessage(mg);
}
}.start();
}
Handler myHandler = new Handler(){
/**
* handleMessage接收消息后进行相应的处理
*/
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if(msg.arg1==1){
//改变文字信息
textView.setText("改变文字信息");
}
}
};
}
反正就是UI 操作一定要放在UI进程里面就对了。