在安卓中,如果我们直接新建主线程意外的线程处理UI,是会报错的。因为安卓不能在子线程中修改UI,必须要在主线程中修改。
示例:
public void onClick(View v) {
if(v == button) {
new Thread(new Runnable() {
@Override
public void run() {
textView.setText("修改之后");
}
}).start();
}
}
报错的内容是:android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
很多时候,我们需要在子线程中,做耗时的操作,做完后才会有修改UI。安卓有一套异步消息处理机制,能很好的解决这个问题。
首先,新建一个activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.aiden.example.MainActivity">
<Button
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="修改UI" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="修改之前" />
</LinearLayout>
很简单,就是定义了一个Button和TextView。测试的方式是:通过点击Button,改变TextView的内容。
新建MainActivity.java:
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends Activity implements View.OnClickListener {
private Button button;
private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setContentView(R.layout.activity_main);
button = (Button) this.findViewById(R.id.button);
textView = (TextView) this.findViewById(R.id.textView);
button.setOnClickListener(this);
}
private Handler handler = new Handler() {
// 重写处理消息的方法
public void handleMessage(Message message) {
// 如果消息的what为1,则修改textView的内容
if (message.what == 1) {
textView.setText("修改之后");
}
}
};
@Override
public void onClick(View v) {
if (v == button) {
new Thread(new Runnable() {
@Override
public void run() {
// 子线程sleep 3s 用于模拟耗时的操作
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 新建消息对象
Message message = new Message();
message.what = 1;
// 发送消息
handler.sendMessage(message);
}
}).start();
}
}
}
分析一下整个过程:
这里用到了:
1、Message:是子线程与主线程之间传递的消息。子线程通过改变what属性的值、主线程通过识别what属性的值用来识别消息。
2、Handler:通过代码可以看出,该类可以发送消息【调用sendMessage()方法】和处理消息【调用handleMessage()方法】。其中handleMessage()方法需要重写。
3、MessageQueue:看意思就知道是消息队列的意思。该队列存放所有通过Handler发送的消息。这部分消息会一直存在于消息队列中,等待被处理。这里需要注意:每个线程只有一个消息队列
4、Looper:【MessageQueue消息队列】的管家。【新建的子线程默认是不会调用loop()方法的,主线程默认会调用loop()方法】进入无线循环。若MessageQueue消息队列中存在消息,则将消息取出。交给Handler的handleMessage()方法处理该消息。这里需要注意:每个线程只有一个消息队列 该类通过调用loop()方法。
其他用法:使用Handler类实现启动页:
活动A为启动页,活动A经过3s后跳转至活动B
Handler handler = new Handler(); // 对handler进行初始化
handler.postDelayed(new Runnable() { // 实现runnable接口,将runnable对象发送给UI线程,在3s后执行该线程
@Override
public void run() { // 重写run方法
Intent intent = new Intent(活动A.this, 活动B.class);
活动A.this.startActivity(intent);
活动A.this.finish();
}
}, 3000);