本篇正式介绍安卓UI线程与其他子线程交互的正确方法。
相比于PC客户端,安卓客户端的UI线程对耗时操作更加敏感,因此像上文提到的ArrayBlockingQueue,因为需要用到阻塞方法,所以不建议在安卓中使用。本篇将介绍 android提供的轻量级的线程交互方式——Handler,并将用Handler实现子线程通知UI线程更新、UI线程通知子线程给服务器发消息的模拟例子。
Handler的基础知识
Handler、MessageQueue、Looper,很多文章中,这些词几乎都是同时出现的。
接下来我尝试尽量用形象的语言来表述他们之间的关系。
如果把要一个线程比喻成一个生产队(这个比喻可能有些不恰当,因为一个线程只能干一个工人的活,硬要理解的话,可以理解为只有一个工人的生产队),那么Handler就是车间主任, Looper是车间主任的助理,以消息队列(MessageQueue)形式记录生产队的工作任务,而Message,自然就是这个施工队所要干的工作。
以上表述实际上还隐含了一个对应关系——一个Handler对应一个Looper,而一个Looper中会有多个Message。
那么也就意味着,我们要新建一个Handler的时候,必然还要给Handler准备一个Looper(主线程除外),好让他们得以配合。
好了,让我们看一下整体的流程:让我们调用车间主任,分配给这个生产队一些任务(Message)。车间主任收到任务,因为自己还在处理手头上的其他任务,所以让助理先把这个分配的任务记录下来(MessageQueue)。每当车间主任手头上的任务干完,助理就会提示他下一条任务(Looper.loop())。好了,车间主任读到了我们给的那条任务,根据自己头脑中的固定行事方式(handleMessage)分析这条任务,然后就根据这样的理解,把自己分析完的更细化的任务分配给生产队去做。
具体实现
同样的 为了实现发送和接收的异步,我们需要一个发送线程和一个接收线程:
接收线程
import android.os.Message;
import android.util.Log;
public class ClientRecvThread extends Thread{
@Override
public void run() {
try {
Thread.sleep(2000); //等待服务器消息的阻塞语句
} catch (InterruptedException e) {
e.printStackTrace();
}
Log.d("RECV", "接收到了来自服务器的消息 ");
Message msg= new Message();
msg.what=1;
msg.obj="服务器传来的数据";
MainActivity.mHandler.sendMessage(msg);//通知UI更新
}
}
发送线程
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;
public class ClientSendThread extends Thread{
Looper looper;
Handler handler;
@Override
public void run() {
Looper.prepare();
looper=Looper.myLooper();
handler=new Handler(looper){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 2:
Log.d("SEND", "已向服务器发送消息:"+msg.obj);;//收到了UI线程的消息,在这里发送消息给服务器
break;
default:break;
}
}
};
/*
*在这里完善run方法
* */
Looper.loop();//loop()方法一定要放在最后
}
}
这里一定注意:因为Looper.loop()方法是阻塞的,所以一定要放在最后,保证前面的语句都能得到执行。
UI线程
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
static Handler mHandler;
TextView textView;
Button button;
ClientRecvThread crt;
ClientSendThread cst;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textView = findViewById(R.id.textView);
button = findViewById(R.id.button);
crt=new ClientRecvThread();
cst=new ClientSendThread();
crt.start();
cst.start();
//处理UI信息
mHandler = new Handler(){
@Override
public void handleMessage(Message msg) {
switch (msg.what){
case 1:
textView.setText((String)msg.obj);//UI收到了接收子线程的消息,在这
里更新
break;
default:break;
}
}
};
try {
Thread.sleep(20);//在这里进行短暂sleep,防止发送子进程中的looper没有准备好
} catch (InterruptedException e) {
e.printStackTrace();
}
//注册向服务器发送数据的方法
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//向服务器发送数据
Message msg = new Message();
msg.what =2 ;
msg.obj ="要向服务器发送的消息" ;
cst.handler.sendMessage(msg);
}
});
}
}
其中sleep的时间不能太长,否则会影响交互!
结果
以上是本篇全部内容,欢迎交流指正!