在android开发中,随处可见的handler.sendMessage()与handler.handleMessage()是怎样实现的?今天就写一篇文章通过查看android源码理解Looper,Handler,Message这三者之间的关系。
android中UI线程(主线程)之外的线程是不能操作UI的改变或刷新的。我们先来了解一个从UI外的线程控制UI的刷新的方法,就是通过Handler来控制UI的刷新。先看下事例代码(通过开启一个线程监听消息来控制UI线程的刷新):
布局文件:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" > <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" android:id="@+id/tv_Message" /> <Button android:id="@+id/btn_click" android:layout_width="match_parent" android:layout_height="30dp" > </Button> </LinearLayout>
代码:
public class MainActivity extends Activity { private TextView tv; private Button btn; /** * UI线程的Handler */ private Handler uiHandler = new Handler(Looper.getMainLooper()){ @Override public void handleMessage(Message msg) { if(msg.what==0x02){ tv.setText("点击按钮触发!"); } } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_activity); findViewById(); } /** * 查找控件并为控件写入事件监听器 */ private void findViewById() { tv=(TextView) findViewById(R.id.tv_Message); btn=(Button) findViewById(R.id.btn_click); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { Message msg = new Message(); msg.what=0x02; test.threadHandler.sendMessage(msg); } }); test.start(); } private TestThread test = new TestThread(); private class TestThread extends Thread { private Handler threadHandler; @Override public void run() { Looper.prepare(); threadHandler = new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what==0x02){ Message msg2 = new Message(); msg2.what=0x02; uiHandler.sendMessage(msg2); } } }; Looper.loop(); } } } 在布局文件中加入一个TextView用来显示内容,一个Button按钮,代码中为Button按钮写入点击事件,当Button按钮被点击时调用test线程的threadHanler对象来发送一条what=0x02的消息。当初始化控件完成后启动一个线程来创建一个属于该线程的消息队列。当test线程的threadHanler收到消息时再通过主主线程的Handler对象将消息内容转发,再在主线程中实现为TextView写入文本。
看一下运行结果:
我们先来理解一下这一行代码
private Handler uiHandler = new Handler(Looper.getMainLooper()) 这行代码是给主线程创建一个Handler对象,Looper.getMainLooper()是获取主线程的Looper对象,用主线程的Looper对象创建一个Handler对象。其实这句代码与 private Handler uiHandler = new Handler()是等效的。我们来看一下Handler类的构造方法:public class Handler { ........................ public Handler() { this(null, false); } public Handler(Callback callback, boolean async) { ............... mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; } ................................ }使用Handler的默认构造方法最终会调用public Handler(Callback callback, boolean async) 方法,在该方法中又调用了Looper.myLooper()方法来获取当前线程的Looper,而当前线程正是主线程。下面是Looper.myLooper方法的源码: public static Looper myLooper() { return sThreadLocal.get(); } public T get() { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); }
sThreadLocal在Looper类中定义为常量对象static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
也就是所有的Looper对象都保存在这一个sThreadLocal 对象中。
简单的说就是myLooper方法通过调用ThreadLocal<T>类的get方法由当前线程(Thread.currentThread())获取Looper返回。mQueue 是保存Message消息的队列,而由mQueue = mLooper.mQueue可以看出,每个 Looper对象都只有一个消息队列mQueue ,在创建handler对象时会在handler对象中保存Looper的消息队列,用于handler发送消息,这样通过handler.sendMessage(msg)发送的msg消息对象都会保存在创建handler该对象的线程的Looper对象的mQueue消息队列中。
下面看一下在test线程中执行的两行代码:
Looper.prepare();
threadHandler = new Handler(){ @Override public void handleMessage(Message msg) { if(msg.what==0x02){ Message msg2 = new Message(); msg2.what=0x02; uiHandler.sendMessage(msg2); } } };Looper.loop();
下面是源码prepare方法的源码:
public static void prepare() { prepare(true); } private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }调用Looper.prepare()方法会通过sThreadLocal.get()的get方法来为当前线程(Thread.currentThread())获取一个Looper对象(每个线程只能有一个Looper对象)返回。
再将获取到的对象保存到sThreadLocal常量对象中。
在线程中创建了一个threadHandler 对象,该对象是属于test线程的。所有通过threadHandler 对象发送的消息都会发送到test线程的消息队列中去。
我们看一下handler.sendMessage()相关的源码:
public class Handler { .................................. public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } 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); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); } ........................................... }delayMillis表示多长时间后发送,单位为毫秒,public boolean sendMessageAtTime(Message msg, long uptimeMillis)是在指定的时间将消息发送出去。可以看出当我们调用handler.sendMessage(msg)发送消息时,最终调用的方法是private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis),而queue则是在sendMessageAtTime方法中获取的,也就是handler对象自身保存的Looper对象的消息队列。 msg.target 是msg消息对象中的一个Handler对象, msg.target = this;是将handler对象赋值给msg,这样做的目标后面再说。queue.enqueueMessage(msg, uptimeMillis);就是将消息放入Looper对象的消息队列中出了,到此发送消息就结束了。
下面来说一下handler是如何接收到消息来处理消息的。
Looper.loop();这句代码还没说。线程的消息获取并分配消息执行就是通过这个方法来执行的。以下是源码
public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg); if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } // Make sure that during the course of dispatching the // identity of the thread wasn't corrupted. final long newIdent = Binder.clearCallingIdentity(); 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(); } }通过myLooper()获取当前线程的Looper对象,再获取该对象的消息队列,当队列中又消息时会循环获取消息,再通过消息的target对象发送给指定的handler来处理该消息。msg.target.dispatchMessage(msg);知道msg为什么要保存发送自己的Handler对象了吧。
我们再看一下handler类的dispatchMessage(msg)方法的源码;
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }当msg中不存在回调函数的时候会去调用handler类对象的handleMessage(msg)方法。到此,一个消息的循环就结束了。
======================为什么在testHandler的handleMessage方法中为什么不能直接将接收到的msg转发=============================
@Override public void handleMessage(Message msg) { if(msg.what==0x02){ Message msg2 = new Message(); msg2.what=0x02; uiHandler.sendMessage(msg2); } }解释一下为什么在testHandler的handleMessage方法中为什么不能直接将接收到的msg转发。
Handler类: public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); } 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; ............................... return enqueueMessage(queue, msg, uptimeMillis); } private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }handler类对象再sendMessage(msg)中最终调用handler对象的消息队列对象的enqueueMessage方法,而MessageQueue类对象中的enqueueMessage在将对象放入队列之前会调用消息msg的isInUse()判断该msg是否被使用过,如果被使用过则会抛出异常。
MessageQueue类: boolean enqueueMessage(Message msg, long when) { if (msg.isInUse()) { throw new AndroidRuntimeException(msg + " This message is already in use."); } if (msg.target == null) { throw new AndroidRuntimeException("Message must have a target."); } ...................................................... ....................................................... } return true; }我们看一下Message的isInUse()源码:
boolean isInUse() { return ((flags & FLAG_IN_USE) == FLAG_IN_USE); } 可见当msg对象的flags为真时则说明该msg对象已经被使用过,那么该msg对象的flags是何时被置为真的呢?
我们来看一下Looper类的loop()方法:
Looper类: public static void loop() { final Looper me = myLooper(); if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); ......................................................................... msg.target.dispatchMessage(msg); .............................................................. msg.recycle(); } }原来Looper类的loop()方法的调用了msg对象的recycle()方法。那我们再去看一下msg对象的recycle()方法到底做了些什么: Message类: public void recycle() { clearForRecycle(); synchronized (sPoolSync) { if (sPoolSize < MAX_POOL_SIZE) { next = sPool; sPool = this; sPoolSize++; } } } void clearForRecycle() { flags = 0; what = 0; arg1 = 0; arg2 = 0; obj = null; replyTo = null; when = 0; target = null; callback = null; data = null; }可见,当loop从消息队列中取出msg对象并将msg对象根据msg对象的target对象分配msg出去时,就会调用msg的 recycle()方法最终调用clearForRecycle()方法将msg的flags 设置为真。
结束。。。。。。。。。。。。。。。。。。。