熟悉开发的朋友,对多线程编程一定不会陌生。当我们需要执行一些耗时操作时,比如说发起一条网络请求,考虑到网速以及一些其他原因,服务器不一定会立刻响应我们的请求。如果不将这类操作放到子线程里去运行,就可能会导致主线程阻塞,影响用户的正常使用。但是,并不是所有操作都能够在子线程中进行的,接下来我们就通过一个实例来探究一下。
首先我们新建一个AndroidThreadTest项目,编辑activity_main.xml中的代码,如下:
<?xml version="1.0" encoding="utf-8"?>
<
RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<
Button
android:id="@+id/change_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Change Text"
android:textAllCaps="false" />
<
TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Hello Thread"
android:textSize="20sp"
android:textAllCaps="false" />
</
RelativeLayout>
界面布局如下:
布局文件中就是简单的定义了两个控件,TextView用于在屏幕中央显示一个Hello World字符串。Button用于改变TextView中显示的内容。我们希望在点击Button后可以把屏幕中央的内容变成:Kevin Durant
接下来修改MainActivity中的代码:
public class MainActivity
extends AppCompatActivity
implements View.OnClickListener{
private TextView
text;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.
activity_main);
Button changeText = (Button) findViewById(R.id.
change_text);
text = (TextView) findViewById(R.id.
text);
changeText.setOnClickListener(
this);
}
@Override
public void onClick(View v){
switch (v.getId()){
case R.id.
change_text:
new Thread(
new Runnable() {
@Override
public void run() {
text.setText(
"Kevin Durant");
}
}).start();
break;
default:
break;
}
}
首先获取到Button和TextView控件的实例,然后在Button按钮的点击事件里面开启了一个子线程,在子线程中调用text的setText()方法将字符串的内容变成 Kevin Durant,也就是更新了子线程中的UI元素,接下来我们运行程序,然后点击Button按钮,会发现程序出现了异常,异常信息如下:
很明显,大概的意思就是,由于在子线程中更新UI导致了程序出现崩溃。也由此证明了,Android确实是不允许在子线程中进行UI操作的。但是有些时候,我们必须在子线程里去执行一些耗时任务,然后根据执行结果来更新响应的UI控件,难道我们就无法解决了吗? 当然NO!
对于这种情况,Android提供了一套异步消息处理机制,很好的解决了在子线程中进行UI操作的问题,接下来我们就来学习一下异步消息时如何解决这个问题的。
首先还是修改MainActivity中的代码:
public class MainActivity
extends AppCompatActivity
implements View.OnClickListener{
public static final int UPDATE_TEXT =
1;
private TextView
text;
private Handler
handler =
new Handler(){
public void handleMessage(Message msg){
switch (msg.
what){
case UPDATE_TEXT:
//在这里进行UI操作
text.setText(
"Kevin Durant");
break;
default:
break;
}
}
};
我们首先定义了一个整型常量UPDATE_TEST,用于表示更新TextView这个动作。之后新增了一个Handler对象,并重写了父类的handleMessage()方法。在这里对Message对象进行具体的操作。如果发现Message的what字段值为UPDATE_TEXT。我们就来更新TextView的UI显示内容。
接着再看一下Button按钮点击事件里面子线程的代码逻辑。
@Override
public void onClick(View v){
switch (v.getId()){
case R.id.
change_text:
new Thread(
new Runnable() {
@Override
public void run() {
Message message =
new Message();
message.
what =
UPDATE_TEXT;
handler.sendMessage(message);
//将Message对象发送出去
}
}).start();
break;
default:
break;
}
}
其实,重点就是在子线程的run()方法里。先是创建了一个Message对象,并将它的字段what指定为UPDATE_TEXT,也就是可以更新UI了。然后调用Handler的sendMessage()方法将这条Message发送出去。很快,Handler就会收到这条Message,并在handleMessage()方法中对它进行处理。
而此时我们应该已经注意到了,handleMessage()方法已经是在主线程中运行了,所以我们可以放心的进行UI操作了。接下来运行程序,点击按钮,我们会发现,程序正常运行,TextView显示的内容变成了: Kevin Durant
这样,我们就通过Android异步消息处理的方法,实现了即在子线程中执行了任务,也解决了子线程中更新UI的问题。后续的章节中,我们继续来深入探讨关于Android异步消息处理机制到底是如何工作的。