我们异步处理可以通过:
- 使用Handler + Looper + MessageQueue (本文"使用"前半部分)
- 使用AsyncTask异步更新UI界面 (下一篇博文)
- 使用Thread + Handler实现非UI线程更新UI界面 (本文"使用"后半部分)
1.名词解释
Message 消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理。是MessageQueue里存放的对象,可以调用removeMessages()时,将Message从Message Queue中删除和通过Handler对象的obtainMessage()获取一个Message实例,可以在线程中使用Handler对象的sendEmptyMessage()或者sendMessage()来传递Bundle对象到Handler,对Handler类提供handlerMessage(Message msg)判断,通过msg.what来区分每一条信息
MessageQueue 消息队列,用来存放Handler发送过来的消息,并按照FIFO规则执行。当然,存放Message并非实际意义的保存,而是将Message以链表的方式串联起来的,等待Looper的抽取。
Looper 消息泵,不断地从MessageQueue中抽取Message执行。因此,一个MessageQueue需要一个Looper。负责管理线程的MessageQueue.除了主线程外,创建的线程默认是没有Looper和MessageQueue,创建一个Looper会同时创建一个MessageQueue,可以使用Looper.prepare()创建MessageQueue,Looper.loop()进入消息循环,Looper.release()释放资源.如需要接受,自己定义一个Looper对象(通过prepare函数),这样该线程就有了自己的Looper对象和MessageQueue数据结构了。
Looper从MessageQueue中取出Message然后,交由Handler的handleMessage进行处理。处理完成后,调用Message.recycle()回收。Handler 处理者,负责Message的发送及处理。使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等。一定要在主线程中使用handler。否则会出现: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()。原因很简单就是Looper的名词解释,在主线程中才会存在Looper。
Thread 线程,负责调度整个消息循环,即消息循环的执行场所。
他们之间的关系是:
handler负责将需要传递的信息封装成Message,通过调用handler对象的obtainMessage()来实现; 将消息传递给Looper,这是通过handler对象的sendMessage()来实现的。继而由Looper将Message放入MessageQueue中。 当Looper对象看到MessageQueue中含有Message,就将其广播出去。该handler对象收到该消息后,调用相应的handler对象的handleMessage()方法 对其进行处理。
2.API
① Message:
主要方法及构造:public final class Message implements Parcelable{...}
what:用于标记message的id,便于handler针对处理
arg1,arg2:可以在消息中传递一些简单的int数据
obj:message传递对象数据,比如json数据
replyTo: 消息到达返回message
我们可以通过new Message()的方式创建Message实例,但是并不推荐采用这种方式,强烈推荐使用Message.obtain(...)的方式创建Message实例,因为后者在MessagePool消息缓冲池中会首先看看有没有现成的的Message,如果没有才会创建,提高了效率和降低了消耗。通过message.sendToTarget()代替handle.sendMessage()发送消息,其实实际还是调用的这个方法。peekData()方法同getData方法,前者不会延迟创建Bundle,当Bundle没创建时返回null。
② MessageQueue
很明显MessageQueue用来管理Message列表的,IdleHandler是队列id的一个接口。
③ Looper
前面说过如果我们要创建一个Looper要通过prepare()加入到队列才有效,通过myQueue()方法可以获取消息队列,循环调用loop();方法。
④ Handler
Handler要在主线程中创建,不管是继承还是创建对象都要复写
mHandler = new Handler() { public void handleMessage(android.os.Message msg) { // process incoming messages here } };
来抓取message,这是一个回调。发送消息用sendMessage相关方法,延迟操作我们经常使用post方法,其实是把参数中的runnable作为回调传给Message的CallBack.即Post的各种方法是把一个Runnable发送给消息队列,它将在到达时进行处理。
相关参考:Android 中Message,MessageQueue,Looper,Handler详解+实例<转> - livesoft - 博客园
3.使用
相关参考:android的Handler - DotDot - 博客园
这是一个倒计时程序。
xml
<RelativeLayout 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"> <TextView android:id="@+id/tv" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerHorizontal="true" android:layout_marginTop="20dp" android:textSize="18sp" android:text="20"/> <LinearLayout android:id="@+id/ll" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/tv" android:padding="10dp" android:orientation="horizontal"> <Button android:id="@+id/start" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:padding="10dp" android:text="start"/> <Button android:id="@+id/stop" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:padding="10dp" android:text="stop"/> </LinearLayout> </RelativeLayout>
MainActivity这里面其实有个MainLooper在不断地循环从MessageQueue中取出Message,由handler处理message从输出来看,主线程创建和接收铺的线程ID都是同一个,但是发送message的线程不一样,见效果图:package com.lzy.exploremessagedemo; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Message; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; public class MainActivity extends Activity implements OnClickListener, Handler.Callback, ITaskCallBack{ private TextView textView; private Button startButton, stopButton; private Handler mHandler; private Timer timer; private int total = 20; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler = new Handler(this);//放在主线程中,因为这里采用的是实现Handler.Callback接口,所以要重写handleMessage方法,如果采用继承的话,要也要复写此方法 initView(); } private void initView() { textView = (TextView) findViewById(R.id.tv); startButton = (Button) findViewById(R.id.start); stopButton = (Button) findViewById(R.id.stop); startButton.setOnClickListener(this); stopButton.setOnClickListener(this); Log.d("ThreadId", "onCreate:" + String.valueOf(Thread.currentThread().getId())); } @Override public void onClick(View v) { int id = v.getId(); if (id == R.id.start) { startTime(); } else if (id == R.id.stop) { stopTime(); } } private void stopTime() { timer.cancel(); } private synchronized void startTime() { timer = new Timer(); Task mTask = new Task(this); timer.schedule(mTask, 1000, 1000); } @Override public boolean handleMessage(Message msg) { // 这是在主线程UI线程中更新界面 switch (msg.what) { case 0x01: startButton.setClickable(false); textView.setText(String.valueOf(msg.arg1));//如果是复杂数据自然是采用msg.getData()/msg.obj方式获取了 Log.d("ThreadId", "HandlerMessage:" + String.valueOf(Thread.currentThread().getId())); break; case 0x02: startButton.setClickable(true); stopTime(); textView.setText("计时结束"); total = 20; break; default: break; } return false; } private class Task extends TimerTask{ private ITaskCallBack callBack; public Task(ITaskCallBack callBack) { this.callBack = callBack; } @Override public void run() { callBack.taskRun(); } } @Override public void taskRun() { updateTimerValues(); } // 执行耗时的倒计时任务。 private void updateTimerValues() { total --; Log.d("ThreadId", "send:" + String.valueOf(Thread.currentThread().getId())); /** * 方式一 * */ Message message = new Message(); /** * 如果是复杂的数据采用obj 或者 Bundle * 少量数据而且是整形的推荐使用arg1 arg2 * */ /*Bundle date = new Bundle();// 存放数据 date.putInt("time", total); msg.setData(date);*/ if (total > 0) { message.arg1 = total; message.what = 0x01; }else { message.what = 0x02; } mHandler.sendMessage(message); /** * 方式二 * */ /*Message msg = Message.obtain(); msg.arg1 = total; msg.what = 0x01; msg.sendToTarget();//推荐采用这个方法 */ } @Override protected void onDestroy() { timer.cancel(); mHandler.removeMessages(0x01); super.onDestroy(); } } interface ITaskCallBack{ void taskRun(); }
总结一下message和 handler的使用
①.在主线程中创建Handler,并复写handlerMessage方法处理返回message.what更新UI。
②.在后台或者是需要耗时的操作中创建Message对象,有两种方式一个使用构造,另外一个使用静态的Message.obtaion()创建。
③.在message中传入message的唯一ID根据需要传入要发送的数据。(根据数据的复杂性和需求灵活使用Message中的字段)
④.借助主线程的mHandler将该消息发送出去。(也是有两种方式,使用案例见上demo)
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
《以下来自》http://blog.csdn.net/jiangwei0910410003/article/details/17021809
Handler发送消息
有了handler之后,我们就可以使用
post(Runnable)
,postAtTime(Runnable, long)
,postDelayed(Runnable, long)
,sendEmptyMessage(int)
,sendMessage(Message)
,sendMessageAtTime(Message, long)
和sendMessageDelayed(Message, long)
这些方法向MQ上发送消息了。光看这些API你可能会觉得handler能发两种消息,一种是Runnable对象,一种是message对象,这是直观的理解,但其实post发出的Runnable对象最后都被封装成message对象了,总之通过handler发出的message有如下特点:1.message.target为该handler对象,这确保了looper执行到该message时能找到处理它的handler,即loop()方法中的关键代码
msg.target.dispatchMessage(msg);2.post发出的message,其callback为Runnable对象
Handler处理消息
说完了消息的发送,再来看下handler如何处理消息。消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的
handler源码
<span style="font-size:12px;">public class handler { final MessageQueue mQueue; // 关联的MQ final Looper mLooper; // 关联的looper final Callback mCallback; //回调函数,他的执行是在handlerMessage方法之前执行的,具体可以查看dispatchMessage(msg)方法 // 其他属性 public Handler() { if (FIND_POTENTIAL_LEAKS) { final Class<?extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) ==0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: "+ klass.getCanonicalName()); } } // 默认将关联当前线程的looper mLooper = Looper.myLooper(); // looper不能为空,即该默认的构造方法只能在looper线程中使用 if (mLooper ==null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } // 重要!!!直接把关联looper的MQ作为自己的MQ,因此它的消息将发送到关联looper的MQ上 mQueue = mLooper.mQueue; mCallback =null; } // 其他方法 } </span>
dispatchMessage方法源码:----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------// 处理消息,该方法由looper调用 publicvoid dispatchMessage(Message msg) { if (msg.callback !=null) { // 如果message设置了callback,即runnable消息,处理callback! handleCallback(msg); } else { // 如果handler本身设置了callback,则执行callback if (mCallback !=null) { /* 这种方法允许让activity等来实现Handler.Callback接口,避免了自己编写handler重写handleMessage方法。见http://alex-yang-xiansoftware-com.iteye.com/blog/850865 */ if (mCallback.handleMessage(msg)) { return; } } // 如果message没有callback,则调用handler的钩子方法handleMessage handleMessage(msg); } } // 处理runnable消息 privatefinalvoid handleCallback(Message message) { message.callback.run(); //直接调用run方法! } // 由子类实现的钩子方法 publicvoid handleMessage(Message msg) { }
接下来我们来看看Looper的使用
前面说过,自己的mLooper要通过Looper的两个方法处理才会有效,比如
public class LooperThread extends Thread { @Override publicvoid run() { // 将当前线程初始化为Looper线程 Looper.prepare(); // ...其他处理,如实例化handler // 开始循环处理消息队列 Looper.loop(); } }
这样,我们自己的LooperThread就升级为Looper进程了。我们在上面的基础上加以实现吧:
在xml添加了一个发送消息的按钮:
<Button android:id="@+id/send" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/ll" android:padding="10dp" android:text="send message" />
MainActivity改变如下/******************************looper**************************************/ 作了标记
package com.lzy.exploremessagedemo; import java.util.Timer; import java.util.TimerTask; import android.app.Activity; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; import android.widget.Toast; public class MainActivity extends Activity implements OnClickListener, Handler.Callback, ITaskCallBack{ private TextView textView; private Button startButton, stopButton, sendButton; private Handler mHandler, mHandler2; private Timer timer; private int total = 20; /******************************looper**************************************/ private LooperThread mLooperThread; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mHandler = new Handler(this);//放在主线程中,因为这里采用的是实现Handler.Callback接口,所以要重写handleMessage方法,如果采用继承的话,要也要复写此方法 /******************************looper**************************************/ mLooperThread = new LooperThread();//开启线程 mLooperThread.start(); /******************************looper**************************************/ initView(); } private void initView() { textView = (TextView) findViewById(R.id.tv); startButton = (Button) findViewById(R.id.start); stopButton = (Button) findViewById(R.id.stop); /******************************looper**************************************/ sendButton = (Button) findViewById(R.id.send); startButton.setOnClickListener(this); stopButton.setOnClickListener(this); /******************************looper**************************************/ sendButton.setOnClickListener(this); Log.d("ThreadId", "onCreate:" + String.valueOf(Thread.currentThread().getId())); } @Override public void onClick(View v) { int id = v.getId(); if (id == R.id.start) { startTime(); } else if (id == R.id.stop) { stopTime(); /******************************looper**************************************/ } else { sendMessage(); } /******************************looper**************************************/ } /******************************looper**************************************/ private void sendMessage() { Message message =new Message(); message.obj = "hello"; message.what = 0x3; mHandler2.sendMessageDelayed(message, 3000); } /******************************looper**************************************/ private void stopTime() { timer.cancel(); } private synchronized void startTime() { timer = new Timer(); Task mTask = new Task(this); timer.schedule(mTask, 1000, 1000); } @Override public boolean handleMessage(Message msg) { // 这是在主线程UI线程中更新界面 switch (msg.what) { case 0x01: startButton.setClickable(false); textView.setText(String.valueOf(msg.arg1));//如果是复杂数据自然是采用msg.getData()/msg.obj方式获取了 Log.d("ThreadId", "HandlerMessage:" + String.valueOf(Thread.currentThread().getId())); break; case 0x02: startButton.setClickable(true); stopTime(); textView.setText("计时结束"); total = 20; break; default: break; } return false; } private class Task extends TimerTask{ private ITaskCallBack callBack; public Task(ITaskCallBack callBack) { this.callBack = callBack; } @Override public void run() { callBack.taskRun(); } } @Override public void taskRun() { updateTimerValues(); } // 执行耗时的倒计时任务。 private void updateTimerValues() { total --; Log.d("ThreadId", "send:" + String.valueOf(Thread.currentThread().getId())); /** * 方式一 * */ Message message = new Message(); /** * 如果是复杂的数据采用obj 或者 Bundle * 少量数据而且是整形的推荐使用arg1 arg2 * */ /*Bundle date = new Bundle();// 存放数据 date.putInt("time", total); msg.setData(date);*/ if (total > 0) { message.arg1 = total; message.what = 0x01; }else { message.what = 0x02; } mHandler.sendMessage(message); /** * 方式二 * */ /*Message msg = Message.obtain(); msg.arg1 = total; msg.what = 0x01; msg.sendToTarget();//推荐采用这个方法 */ } @Override protected void onDestroy() { timer.cancel(); mHandler.removeMessages(0x01); /******************************looper**************************************/ mHandler2.removeMessages(0x03); mLooperThread.destroy(); /******************************looper**************************************/ super.onDestroy(); } /******************************looper**************************************/ class LooperThread extends Thread{ @Override public void run() { // 进行进程升级 Looper.prepare(); //线程中有一个Looper对象,它的内部维护了一个消息队列MQ。注意,一个Thread只能有一个Looper对象,可以看到,一个线程可以有多个Handler,但是只能有一个Looper! mHandler = new Handler(); mHandler2 = new Handler(){ @Override public void handleMessage(Message msg) { if (msg.what == 0x3) { Toast.makeText(getApplicationContext(), "这是三秒后发过来的 "+ msg.obj + "消息", Toast.LENGTH_LONG).show(); } } }; Looper.loop(); } } /******************************looper**************************************/ } interface ITaskCallBack{ void taskRun(); }
OK,基本就是这样,遇到问题欢迎回复指正