本篇正式介绍安卓UI线程与其他子线程交互的正确方法。
相比于PC客户端,安卓客户端的UI线程对耗时操作更加敏感,因此像上文提到的ArrayBlockingQueue,因为需要用到阻塞方法,所以不建议在安卓中使用。本篇将介绍 android提供的轻量级的线程交互方式——Handler,并将用Handler实现子线程通知UI线程更新、UI线程通知子线程给服务器发消息的模拟例子。
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的时间不能太长,否则会影响交互!
以上是本篇全部内容,欢迎交流指正!