学习android到今天也将近3个月了,虽然各种界面各种组件一直在欢快的用着,但有一天忽然发现,一些简单的东西去不是很理解:比如Bundle和Handler。于是就抽空好好阅读了下adk文档。现在也算是理解一二了,技术需要分享,于是乎花了半天的时间整理了这篇文章。
这应该是任何一个学习android的人创建的第一个hello world程序都会遇到的一个类,它是activity的oncreate的入参,但由于做东西的时候很少会遇到它,也就一直没在意。
这个类很简单,先听听官方的解释:“A mapping from String values to various Parcelable types.”
Soga,原来就是一个String - Object的映射表,同时又是支持序列化的。
再看代码:“final class Bundle implements Parcelable, Cloneable ”。实现了两个接口,前者是序列化接口,后置是浅拷贝和深拷贝的接口,具体实现就不再说了,这个不是重点。
总之一句话,Bundle就是为了实现数据传输而生的,比如activity之间。通常Bundle会附在intent里面intent.putExtra()。
好了,打住,进入下一个议题:消息循环!
做过客户端的肯定都会接触到一个概念:UI线程。说白了,UI线程和普通工作线程的区别就在于消息循环。消息循环就是用来处理分发相关线程消息队列中的消息,Android和Windows都是如此。作为一名曾经的windows开发人员,研究的第一个点也是系统对UI的消息处理流程,不然干活都没底气。
int _WinMain(args ...) { //WND definition ... ... while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch(message) { ... ... } }
上面就是经过简化后的一个最基本的windows的创建窗口的过程。其中main函数里面的while就是一个消息循环,来不断从消息队列里面读取消息并dispatch消息。dispatch的消息均会进入到消息处理回调函数WndProc中完成消息的处理。这个程序的main线程也就是通常说的UI线程。假如我们另起一个线程包含了上面的while循环,则那个线程也照样可以创建窗口什么的,那么它也是一个UI线程。
如果对win32的ui感兴趣,可以参考http://blog.csdn.net/cheneywong/article/details/8847810
上面说了这么些的windows相关技术,其实就是想描述下窗口处理的基本原理,因为有些概念在windows上更易懂写,这样再看android的时候容易理解些。
官方解释:Class used to run a message loop for a thread. Threads by default do not have a message loop associated with them; to create one, call prepare()
in the thread that is to run the loop, and then loop()
to have it process messages until the loop is stopped.
很明白,looper就是一个用来处理消息的消息循环。默认的Thread也是不具有处理消息的能力,需要添加Looper来实现。说白了,android中的UI线程也就是带有一个Looper的Thread,用户所有的操作产生的消息均通过Looper来分发处理(这句没有看代码验证,但应该也是八九不离十了)。
和上一节比较我们发现好像少了一点东西 -- 消息处理回调函数WndProc。
“Most interaction with a message loop is through the Handler
class.”
这句话也引出了Looper和Hanlder的关系了。一个消息循环分发消息,一个消息处理。如下一段实例代码,演示了线程中添加消息循环并处理的过程:
class LooperThread extends Thread { public Handler mHandler; public void run() { Looper.prepare(); mHandler = new Handler() { public void handleMessage(Message msg) { // process incoming messages here } }; Looper.loop(); } }
看了这段代码,我就立刻产生了一个疑问,windows中的wndproc是需要注册才能进入处理的,那上段代码handler没有注册却能接收并处理消息!!
官方解释:A Handler allows you to send and process Message
and Runnable objects associated with a thread's MessageQueue
. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
简单翻译:Handler允许你向线程的MessageQueue发送消息,同时还能够处理该消息队列里的Message和Runnable。每个handler只能关联一个线程,并在创建的时候和线程绑定。
原来创建Hanlder的时候,它会自己绑定到线程和消息队列上,这就解释了上一节的疑问了。
另外值得注意的一条规则:每个Handler只会和一个Thread绑定,即创建它的Thread。后面会讲到这条规则的用处。
这里面又引入了两个概念:Message和向消息队列发送消息。
消息体,查看源码大致包含了如下几个核心成员:
Bundle date ; // 消息体包含的数据
Handler target; // 消息对应的“target", 即由谁来处理
Runable callback; // 消息处理的回调
Message next; // 下一条消息
Looper的结构相对简单,主要有以下两个成员
{
final MessageQueue mQueue;
final Thread mThread;
}
通过成员变量,我们可以看出一个消息循环Looper均和一个Thread相关联,同时还会附带一个消息队列MessageQueue。
另外,还有两个静态成员:
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
该对象可以理解成管理Thread和Looper的关系就好了。
private static Looper sMainLooper;
主线程的Looper。这样,任何就可以在任何线程获取到主线程(UI线程)的消息循环体了,进而可以向UI线程发送消息。
最后,我们再看下之前代码中出现的Looper.prepare()和Looper.loop()。这两个函数均是静态函数
prepare()
通过ThreadLocal创建Looper。
loop():
简化后的伪代码大致如下:
public static void loop() { loop = getThisThreadLooper(); queue = loop.mQueue; for(;;){ msg = queue.next(); // 分发消息 msg.target.dispatchMessage(msg); msg.recycle(); } }
原来loop函数也就是一个无线循环,不断的从消息队列里面取出消息并交给Message中的目标Handler处理。好了,这就清晰了,消息循环原来如此简单。
附加:prepareMainLooper
public static void prepareMainLooper() { prepare(false); synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); } }
该函数顾名思义,就是来准备主线程(UI线程)的消息循环,代码的注释中也说明了:该函数是初始化application的主looper。该函数有android系统来调用,我们不应该自己来调用,不然会抛出异常。
上一节中出现了一个函数Handler.dispatchMessage(Message). 下面我们也会重点分析该函数的。
{ final MessageQueue mQueue; final Looper mLooper; final Callback mCallback; }
Handler中的成员主要有上面3个,消息队列、消息循环和回调Callback。
(1)初始化
获取创建自己的线程的消息循环以及消息队列:mLooper = Looper.myLooper();
另外用户可以为Handler指定一个Callback 。
(2)Callback 接口和handleMessage成员方法
Handler内部定义了如下接口:
public interface Callback { public boolean handleMessage(Message msg); // 返回值意思就是callback处理完之后是否还需要继续处理,true就是已经处理过了,不需要再进一步处理 }
另外值得注意的是Handler自己还有一个成员函数
public void handleMessage(Message msg) { }
擦,你这是要闹哪样!!
查看下代码的注释:
Callback:”Callback interface you can use when instantiating a Handler to avoid having to implement your own subclass of Handler.“
handleMessage成员函数:”Subclasses must implement this to receive messages.“
这样就水落石出了:成员函数主要用在用户自己继承Handler实现时来处理message的,而Callback接口则是为了用户直接创建Handler时能够处理message用的,而不必通过子类实现。
(3)dispatchMessage
Message中有一个Runnable的Callback成员,现在Handler中也有一个Callback,另外Handler还有一个成员函数handleMessage(Message),哇塞,晕掉了有木有,这三个必须要有一个执行次序。代码如下:
public void dispatchMessage(Message msg) { if (msg.callback != null) { msg.callback.run(); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
即优先级为:1. 消息体中的Runnable; 2. 如果为空则执行创建handler时指定的Callback;3. 如果没指定或者返回值为false的话就执行handler的handleMessage方法。
至此,android中线程的消息循环和消息队列就说明的差不多了。
在SDK文档中的描述Handler,其有两大主要用处:其一,安排message和runnable在某一时间点执行;其二,向其他线程的消息队列中发送消息(比如UI主线程)。
其中第二项功能就是我们通常用来在异步线程和UI线程打交道的方法了。这主要通过Handler提供的send和post系列方法完成。send对应message,post对应Runnable。
如下:
post(Runnable)
postAtTime(Runnable, long)
postDelayed(Runnable, long)
sendEmptyMessage(int)
sendMessage(Message)
sendMessageAtTime(Message, long)
sendMessageDelayed(Message, long)
每个方法的具体含义请自行查看SDK文档。这里还是要解释下post的实现:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); } public final boolean sendMessage(Message msg) { return sendMessageDelayed(msg, 0); } public boolean sendMessageAtTime(Message msg, long uptimeMillis) { MessageQueue queue = mQueue; if (queue == null) { // exception return false; } return enqueueMessage(queue, msg, uptimeMillis); }
原来post也是将Runnable转换成一个message(还记得Message有一个Runnable成员吗),然后调用send的delay方法。最终的sendMessage也只是将消息压入消息队列中。
注意:这里面的post和send含义和Windows应用程序的post和send的含义是不同的。
(1)线程内
上面的分析,我们就可以在线程内向handler发送自定义message或post一个runnable。
(2)线程间
之前我们说过:一个Handler只能和创建它的线程绑定在一起,这样我们就可以在主线程创建一个Handler,然后再工作线程中向这个Handler 进行send 或post操作,这样创建的message均会加入到UI线程的消息队列中并被执行,这也就实现了UI线程和普通工作线程的通信。如下一个代码示例:
class MyActivity{ Handler handler; ... ... void init(){ handler = new Handler() { @Override public void handleMessage(Message msg) { //处理消息:将网络获取的数据在UI上表现出来 ... ... } }; } void getDataFormNet(){ Thread thread = new Thread(new Runnable() { @Override public void run() { //网络操作 ... ... handler.sendMessage(new Message()); } }); thread.start(); } }
OVER了,学习时间不长,如有错误请指正~~