标签:
演示代码
public class MainActivity extends ListActivity {private TextView tv_info;private CalThread calThread;//一个子线程private boolean ifLooperPrepare = false;//是否在子线程中的创建Handler【前】初始化Looperprivate boolean ifLooperLoop = false;//是否在子线程中创建Handler【后】执行loop方法private boolean sendMessageToUi = false;//是否将子线程中的消息转发到主线程中private boolean sendMessageToZi = false;//是否将主线程中的消息转发到子线程中public static final int MSG_WHAT_1 = 1;@SuppressLint("HandlerLeak")private Handler uiHandler = new Handler() {//在主线程中,系统已经初始化了一个Looper对象,所以我们直接创建Handler对象就可以进行信息的发送与处理了public void handleMessage(Message msg) {Toast.makeText(MainActivity.this, "主线程收到消息:" + msg.obj, Toast.LENGTH_SHORT).show();tv_info.setText("主线程收到消息:" + msg.obj);if (sendMessageToZi) {Message newMsg = Message.obtain(null, msg.what, new SimpleDateFormat(" HH:mm:ss", Locale.getDefault()).format(new Date()));calThread.getThreadHandler().sendMessageAtTime(newMsg, SystemClock.uptimeMillis() + 4500);// 从开机到现在的毫秒数(不包括手机睡眠的时间)}}};protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);String[] array = { "在子线程中的创建Handler前,必须先通过Looper.prepare()初始化Looper",//"若要子线程能收到消息,在子线程中的创建Handler后,必须调用Looper.loop()",//"开启子线程,并在子线程中创建Handler \n注意:此线程会一直阻塞在Looper.loop()", //"在主线程中,通过子线程的Handler向子线程发消息", //"将子线程中的收到的消息(不)转发到主线程",//"将主线程中的收到的消息(不)转发到子线程",//"演示Handler的post方法" };for (int i = 0; i < array.length; i++) {array[i] = i + "、" + array[i];}tv_info = new TextView(this);tv_info.setTextColor(Color.BLUE);tv_info.setTextSize(TypedValue.COMPLEX_UNIT_SP, 16);tv_info.setPadding(20, 10, 20, 10);getListView().addFooterView(tv_info);setListAdapter(new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, new ArrayList<String>(Arrays.asList(array))));calThread = new CalThread();}@Overrideprotected void onListItemClick(ListView l, View v, int position, long id) {switch (position) {case 0:ifLooperPrepare = true;break;case 1:ifLooperLoop = true;break;case 2:calThread.start();break;case 3:String obj = new SimpleDateFormat("yyyy.MM.dd HH:mm:ss", Locale.getDefault()).format(new Date());calThread.getThreadHandler().sendMessage(Message.obtain(null, MSG_WHAT_1, obj));//这种方式时,Message.obtain的第一个参数可直接设为null//注意,并不是说Message.obtain的第一个参数Handler没有用(其作用是指定msg.target),而是后面调用sendMessage方法时重新指定了当前Handler为msg.targetbreak;case 4:sendMessageToUi = !sendMessageToUi;break;case 5:sendMessageToZi = !sendMessageToZi;break;case 6:uiHandler.post(new Runnable() {//其实这个Runnable并没有创建什么线程,而是发送了一条消息,当Handler收到此消息后回调run()方法@Overridepublic void run() {tv_info.setText("演示Handler的post方法");}});break;}}/** 定义一个线程,用于执行耗时的操作 */class CalThread extends Thread {private Handler ziHandler;public Handler getThreadHandler() {return ziHandler;}@SuppressLint("HandlerLeak")public void run() {if (ifLooperPrepare) {Looper.prepare();//①为当前线程创建唯一的Looper对象 ②在它的构造方法中会创建一个的MessageQueue对象//prepare()方法只能被调用一次,否则抛异常;这保证了在一个线程中只有一个Looper实例,同时一个Looper实例也只有一个MessageQueue实例}ziHandler = new Handler() {//任何线程都可通过此Handler发送信息!//在初始化Handler之前必须先调用Looper.prepare(),否则报RuntimeException: Can‘t create handler inside thread that has not called Looper.prepare()@Overridepublic void handleMessage(Message msg) {Toast.makeText(MainActivity.this, "子线程收到消息:" + msg.obj, Toast.LENGTH_SHORT).show();//在子线程中是可以弹土司的,因为土司机制比较特殊!//在子线程中创建Handler的目的完全是为了和其他线程(包括UI线程)通讯,绝对不是(也不能)更新UI,若要更新UI,还必须通过UI线程才可以!if (sendMessageToUi) {uiHandler.sendMessageDelayed(Message.obtain(msg), 2500);//注意,不能直接把消息转发出去,否则IllegalStateException: This message is already in use}}};if (ifLooperLoop) {Looper.loop();//启动一个死循环,不断从MessageQueue中取消息,没有消息则阻塞等待,有则通过将消息传给当前Handler去处理。此方法必须在prepar之后运行}}}}
简介
Handler 、Looper 、Message 这三者都与Android异步消息处理线程相关的概念。那么什么叫异步消息处理线程呢?异步消息处理线程启动后会进入一个无限的循环体之中,每循环一次,从其内部的消息队列中取出一个消息,然后回调相应的消息处理函数,执行完成一个消息后则继续循环。若消息队列为空,线程则会阻塞等待。其实Looper负责的就是创建一个MessageQueue,然后在一个无限循环体中不断从该MessageQueue中读取消息,而消息是由Handler(一个或多个)发送过来的。基本使用过程:
- ①在主线程中创建Handler并重写handleMessage()方法
- ②在子线程利用此Handler发送消息,消息会被发送到主线程的MessageQueue中
- ③一旦MessageQueue中有新消息,主线程中的 Looper 就会发现此消息,然后就会调用Handler的handleMessage()方法处理此消息
相关名词
- UI线程:就是我们的主线程,系统在创建UI线程的时候会初始化一个Looper对象,同时也会创建一个与其关联的MessageQueue;
- Handler:作用就是发送与处理信息,如果希望Handler正常工作,在当前线程中要有一个Looper对象
- Message:Handler接收与处理的对象
- MessageQueue:消息队列,先进先出管理Message,在初始化Looper对象时会创建一个与之关联的MessageQueue;
- Looper:每个线程只能够有一个Looper,管理MessageQueue,不断地从中取出Message分发给对应的Handler处理!
Looper详解
首先看其prepare()方法public static final void prepare() {if (sThreadLocal.get() != null) throw new RuntimeException("Only one Looper may be created per thread");sThreadLocal.set(new Looper(true));}ThreadLocal可以在一个线程中存储变量,可以看到,我们将一个Looper的实例放入了ThreadLocal,并且判断了sThreadLocal是否存储过Looper对象,存储过(即.get()不为null)则抛出异常。这也就说明了Looper.prepare()方法不能被调用两次,同时也保证了一个线程中只有一个Looper实例。下面看其构造方法:private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mRun = true;mThread = Thread.currentThread();}在构造方法中,创建了一个消息队列MessageQueue。然后我们看loop()方法:public static void loop() {final Looper me = myLooper();//方法体为 return sThreadLocal.get(); 即此方法直接返回了sThreadLocal存储的Looper实例if (me == null) throw new RuntimeException("No Looper; Looper.prepare() wasn‘t called on this thread.");//如果me为null则抛出异常,也就是说looper方法必须在prepar()之后运行。final MessageQueue queue = me.mQueue;//拿到该looper实例中的消息队列Binder.clearCallingIdentity(); // Make sure the identity of this thread is that of the local process, and keep track of what that identity token actually is.final long ident = Binder.clearCallingIdentity();//无限循环for (;;) {Message msg = queue.next(); // might block,取出一条消息,如果没有消息则阻塞等待if (msg == null) return;Printer logging = me.mLogging; // This must be in a local variable, in case a UI event sets the loggerif (logging != null) logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what);msg.target.dispatchMessage(msg);//Msg的target其实就是handler对象if (logging != null) logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);final long newIdent = Binder.clearCallingIdentity();// Make sure that during the course of dispatching the identity of the thread wasn‘t corrupted.if (ident != newIdent) {Log.wtf(TAG, "Thread identity changed from 0x" + Long.toHexString(ident) + " to 0x" + Long.toHexString(newIdent)+ " while dispatching to " + msg.target.getClass().getName() + " " + msg.callback + " what=" + msg.what);}msg.recycle();//释放消息占据的资源}}Looper主要作用:
- 1、与当前线程绑定,保证一个线程只会有一个Looper实例,同时一个Looper实例也只有一个MessageQueue。
- 2、执行loop()方法,不断从MessageQueue中取消息,交给消息的target属性的dispatchMessage方法去处理。
大家可能还会问,在Activity中,我们并没有显示的调用Looper.prepare()和Looper.loop()方法,为啥Handler可以成功创建呢?这是因为在Activity的启动代码中,已经默默的在当前UI线程中调用了Looper.prepare()和Looper.loop()方法。而如果我们要想在子线程中创建Handler,则必须先调用Looper.prepare()方法,Handler创建后再调用Looper.loop()方法。
Handler详解
使用Handler之前,我们都是初始化一个实例,我们可以在声明的时候直接初始化,或者在onCreate中初始化。我们首先看Handler的构造方法,看其如何与MessageQueue联系上的,它在子线程中发送的消息(当然可以在任何线程)是怎么发送到MessageQueue中的。public Handler() {//我们一般都是使用此无参的构造方法this(null, false);}public Handler(Callback callback, boolean async) {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());}}mLooper = Looper.myLooper();//获取当前线程保存的Looper实例if (mLooper == null) throw new RuntimeException("Can‘t create handler inside thread that has not called Looper.prepare()");//在初始化Handler之前必须先通过Looper.prepare()方法创建Looper的实例mQueue = mLooper.mQueue;//获取这个Looper实例中保存的MessageQueue(消息队列)mCallback = callback;mAsynchronous = async;}然后看我们最常用的发送消息相关的几个方法public final boolean sendMessage(Message msg) {return sendMessageDelayed(msg, 0);}public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {Message msg = Message.obtain();msg.what = what;return sendMessageDelayed(msg, delayMillis);}public final boolean sendMessageDelayed(Message msg, long delayMillis) {if (delayMillis < 0) delayMillis = 0;return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);}public boolean sendMessageAtTime(Message msg, long uptimeMillis) {MessageQueue queue = mQueue;if (queue == null) {RuntimeException e = new RuntimeException(this + " sendMessageAtTime() called with no mQueue");Log.w("Looper", e.getMessage(), e);return false;}return enqueueMessage(queue, msg, uptimeMillis);}辗转反侧最后都是调用了sendMessageAtTime方法,在此方法内部又直接获取MessageQueue,然后调用了enqueueMessage方法private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) msg.setAsynchronous(true);return queue.enqueueMessage(msg, uptimeMillis);}enqueueMessage中首先为meg.target赋值为this,最后调用queue的enqueueMessage的方法,也就是说handler发出的消息,最终会保存到消息队列中去。【上面我们说了,Looper的loop方法会取出每个msg然后交给msg.arget.dispatchMessage(msg)去处理消息】,也就是说Looper的loop方法会把取出的每个msg通过当前的handler的dispatchMessage回调处理。下面我们去看一看handler中的这个dispathMessage方法public void dispatchMessage(Message msg) {if (msg.callback != null) handleCallback(msg);else {if (mCallback != null) {if (mCallback.handleMessage(msg)) return;}handleMessage(msg);}}可以看到,最后调用了handleMessage方法,可实际上这是一个空方法,为什么呢?因为消息的最终回调是由我们控制的,我们在创建handler的时候都是复写handleMessage方法,然后根据msg.what进行消息处理。
Handler的全部方法
构造方法其他方法获取消息移除消息和回调发送消息post方法有时候为了方便,我们会直接写如下代码:
mHandler.post(new Runnable() {@Overridepublic void run() {mTxt.setText("yoxi");//在run方法中可以更新UI}});其实这个Runnable并没有创建什么线程,而是发送了一条消息,下面看源码:public final boolean post(Runnable r) {return sendMessageDelayed(getPostMessage(r), 0);}private static Message getPostMessage(Runnable r) {Message m = Message.obtain();//得到了一个Message对象m.callback = r;//将我们创建的Runable对象作为callback属性,赋值给了此Messagereturn m;}后面的步骤就和handler.sendMessage一样了可以看到,这里msg的callback和target都有值,那么会执行哪个呢?其实上面已经贴过代码,就是dispatchMessage方法:如果msg.callback不为null,则执行callback回调,也就是我们定义的Runnable。
总结
- 1、首先Looper.prepare()在本线程中保存一个Looper实例,然后在该实例中保存一个MessageQueue对象;因为Looper.prepare()在一个线程中只能调用一次,所以MessageQueue在一个线程中只会存在一个。
- 2、Looper.loop()方法会让当前线程进入一个无限循环,不断从MessageQueue中读取消息,然后回调msg.target.dispatchMessage(msg)方法。
- 3、Handler的构造方法,会首先得到当前线程中保存的Looper实例,进而与Looper实例中的MessageQueue相关联。
- 4、Handler的sendMessage方法,会给msg.target赋值为handler自身,然后加入MessageQueue中。
- 5、在构造Handler实例时,我们会重写handleMessage方法,也就是msg.target.dispatchMessage(msg)最终调用的方法。
标签:
原文地址:http://www.cnblogs.com/baiqiantao/p/5583908.html