在Android中,更新UI的操作都必须在主线程中进行,不能做阻塞主线程的操作。
当我们要执行一个耗时的操作并且最终要去更新UI(比如将计算结果反映到UI上)时,我们会考虑新开一个线程,去执行这个耗时的操作,执行完毕之后,再在主线程中更新UI。
为了解决这种问题,android为我们提供了很多办法。
一、handler和message机制
下边这个小Demo演示了Handler最简单常见的用法,在新开的线程里模拟一个耗时的操作timeConsumingOperation(),操作完成后利用handler.sendEmptyMessage(0)发送消息,然后在主线程中mProgressDialog.dismiss()更新UI:
- public class MainActivity extends Activity implements OnClickListener{
- private Button mButton;
- private ProgressDialog mProgressDialog;
- private Handler handler = new Handler(){
- public void handleMessage(Message msg){
- super.handleMessage(msg);
- mProgressDialog.dismiss();
- }
- };
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mButton= (Button)findViewById(R.id.button1);
- mButton.setOnClickListener(this);
- }
- @Override
- public void onClick(View v) {
- newThread();
- }
- private void newThread(){
- mProgressDialog= ProgressDialog.show(this, "提示", "耗时操作中,请稍后 ……");
- new Thread(){
- public void run(){
- timeConsumingOperation();
- handler.sendEmptyMessage(0);
- }
- }.start();
- }
- private void timeConsumingOperation(){
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
在上述代码中,发送消息的 sendEmptyMessage() 方法是由Handler的子类对象调用的,更新UI的方法mProgressDialog.dismiss()也是在Handler的子类复写的handleMessage()方法中定义的,所以我们先从创建Handler的子类对象new Handler()讲起
Handler类概要:- /**
- * A Handler allows you to send and process {@link Message} and Runnable
- * objects associated with a thread's {@link 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.
- * <p>There are two main uses for a Handler: (1) to schedule messages and
- * runnables to be executed as some point in the future; and (2) to enqueue
- * an action to be performed on a different thread than your own ... ...
- */
- //一个Handler允许你发送和处理和一个线程的Message Queue相关联的Message和Runnable对象,
- //每一个Handler和一个线程及线程的Message Queue相关联... ...
- public class Handler { ...... }
(注释太多不全列出了)通常我们执行new Handler()时,以下构造函数会被调用:
- public Handler(Callback callback, boolean async) {
- ... ...
- final MessageQueue mQueue;
- 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;
- ... ...
- }
其中mLooper是Handler的成员变量final Looper mLooper, Looper类的作用是管理此线程里的Message Queue。先来看看Looper类的继承关系及类概要:
- /**
- * 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
- * {@link #prepare} in the thread that is to run the loop, and then
- * {@link #loop} to have it process messages until the loop is stopped.
- *
- * <p>Most interaction with a message loop is through the
- * {@link Handler} class.
- */
- public final class Looper {}
此类用来为一个线程run一个message loop,一个线程默认是没有和其关联的message loop的,如果想要创建,则调用prepare方法,然后调用loop方法来处理消息......
而myLooper()方法的注释如下:
- /**
- * Return the Looper object associated with the current thread. Returns
- * null if the calling thread is not associated with a Looper.
- */
- //该方法用于获取当前线程的Looper对象,如果没有则返回null
- public static Looper myLooper() {
- return sThreadLocal.get();
- }
该方法用于获取当前线程的Looper对象,如果没有则返回null
需要注意的是:系统默认为主线程而没有为子线程创建looper对象,所以在子线程中直接new Handler()会报如下错误(参考public Handler(Callback callback, boolean async)方法):
"Can't create handler inside thread that has not called Looper.prepare()");
我们来看看系统在主线程中创建looper对象的相关代码,从ActivityThread.Java中我们可以看到:
- public static void main(String[] args) {
- ... ...
- Looper.prepareMainLooper();
- ... ...
- Looper.loop();
- ... ...
- }
其中,prepareMainLooper()方法的注释如下:
- /**
- * Initialize the current thread as a looper, marking it as an
- * application's main looper. The main looper for your application
- * is created by the Android environment, so you should never need
- * to call this function yourself. See also: {@link #prepare()}
- */
- //初始化当前的线程为一个looper线程,标记它为一个应用的main looper,应用的main looper为
- //系统自动创建,你不需要自己调用该方法
- public static void prepareMainLooper() {
- prepare(false);
- ... ...
- }
- /** Initialize the current thread as a looper.
- * This gives you a chance to create handlers that then reference
- * this looper, before actually starting the loop. Be sure to call
- * {@link #loop()} after calling this method, and end it by calling
- * {@link #quit()}.
- */
- //初始化当前线程为一个looper线程,这使得你有机会去创建一个handler,在调用这个方法之后,
- //在正式开始loop之前,记得调用loop方法,最后调用quit方法end it
- public static void prepare() {
- prepare(true);
- }
loop()方法的注释如下:
- /**
- * Run the message queue in this thread. Be sure to call
- * {@link #quit()} to end the loop.
- */
- //让这个线程里的message queue run起来,确保调用quit方法来end the loop
- public static void loop() { ... ... }
- /**
- * <p>This is a typical example of the implementation of a Looper thread,
- * using the separation of {@link #prepare} and {@link #loop} to create an
- * initial Handler to communicate with the Looper.
- * <pre>
- * 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();
- * }
- * }</pre>
- */
- public final class Looper {}
接下来,我们看prepare()方法的主要逻辑:
- public final class Looper {
- final MessageQueue mQueue;
- public static void prepare() {
- prepare(true);
- }
- private static void prepare(boolean quitAllowed) {
- ... ...
- sThreadLocal.set(new Looper(quitAllowed));
- ... ...
- }
- private Looper(boolean quitAllowed) {
- mQueue = new MessageQueue(quitAllowed);
- ... ...
- }
- }
- /**
- * Low-level class holding the list of messages to be dispatched by a
- * {@link Looper}. Messages are not added directly to a MessageQueue,
- * but rather through {@link Handler} objects associated with the Looper.
- *
- * <p>You can retrieve the MessageQueue for the current thread with
- * {@link Looper#myQueue() Looper.myQueue()}.
- */
- public final class MessageQueue { }
- /**
- * Returns a new {@link android.os.Message Message} from the global message pool. More efficient than
- * creating and allocating new instances. The retrieved message has its handler set to this instance (Message.target == this).
- * If you don't want that facility, just call Message.obtain() instead.
- */
- //直接从message pool中返回一个message,这将比直接创建一个message更有效率,返回的message已经有了
- //和它关联的handler(这是在Message类中的obtain(Handler h)方法中通过m.target = h实现的),
- //你也可以直接调用Message.obtain()方法来创建消息
- public final Message obtainMessage(){
- return Message.obtain(this);
- }
- /**
- * Same as {@link #obtain()}, but sets the values of the <em>target</em>, <em>what</em>,
- * <em>arg1</em>, <em>arg2</em>, and <em>obj</em> members.
- */
- public static Message obtain(Handler h, int what, int arg1, int arg2, Object obj) {
- Message m = obtain();
- m.target = h;
- m.what = what;
- m.arg1 = arg1;
- m.arg2 = arg2;
- m.obj = obj;
- return m;
- }
- /**
- * Return a new Message instance from the global pool. Allows us to
- * avoid allocating new objects in many cases.
- */
- //从global pool中返回一个新的message对象,能避免我们在很多情况下去新建对象
- public static Message obtain() {
- ... ...
- }
- public final class Message implements Parcelable {
- /**
- * User-defined message code so that the recipient can identify
- * what this message is about. Each {@link Handler} has its own name-space
- * for message codes, so you do not need to worry about yours conflicting
- * with other handlers.
- */
- //可以理解为一个用于区分消息的标示符
- public int what;
- /**
- * arg1 and arg2 are lower-cost alternatives to using
- * {@link #setData(Bundle) setData()} if you only need to store a
- * few integer values.
- */
- //如果你只是想携带一些比较简单的integer数据的话,相对于setData(Bundle)方法来讲
- //arg1和arg2是一种比较低耗的选择
- public int arg1;
- /**
- * arg1 and arg2 are lower-cost alternatives to using
- * {@link #setData(Bundle) setData()} if you only need to store a
- * few integer values.
- */
- public int arg2;
- Bundle data;
- Handler target;
- Runnable callback; ... ...
- }
- Bundle mBundle = new Bundle();
- mBundle.putBoolean("myKey1", true);
- mBundle.putString("myKey2", "myValue");
- Message mMessage = Message.obtain(handler, 5, 111, 112, mBundle);
- //或者:
- Bundle mBundle = new Bundle();
- mBundle.putBoolean("myKey1", true);
- mBundle.putString("myKey2", "myValue");
- Message mMessage = new Message();
- mMessage.setData(mBundle);
- //在接收到message时,可以通过Message的getData()方法获取mBundle对象
- /**
- * Pushes a message onto the end of the message queue after all pending messages
- * before the current time. It will be received in {@link #handleMessage},
- * in the thread attached to this handler.
- *
- * @return Returns true if the message was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting.
- */
- public final boolean sendMessage(Message msg){
- return sendMessageDelayed(msg, 0);
- }
- /**
- * Sends a Message containing only the what value.
- *
- * @return Returns true if the message was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting.
- */
- //发送一条仅仅含有what值得消息,返回true(false)如果(没有)成功将消息发送到消息队列
- public final boolean sendEmptyMessage(int what){
- return sendEmptyMessageDelayed(what, 0);
- }
- /**
- * Causes the Runnable r to be added to the message queue.
- * The runnable will be run on the thread to which this handler is
- * attached.
- * @param r The Runnable that will be executed.
- * @return Returns true if the Runnable was successfully placed in to the
- * message queue. Returns false on failure, usually because the
- * looper processing the message queue is exiting.
- */
- public final boolean post(Runnable r){
- return sendMessageDelayed(getPostMessage(r), 0);
- }
- private static Message getPostMessage(Runnable r) {
- Message m = Message.obtain();
- m.callback = r;
- return m;
- }
- private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
- msg.target = this;
- if (mAsynchronous) {
- msg.setAsynchronous(true);
- }
- return queue.enqueueMessage(msg, uptimeMillis);
- }
在enqueueMessage()方法的第一行,先执行msg.target = this,将发送消息的这个handler赋值给Message的成员变量Handler target,然后再将消息发送到消息队列
- public static void loop() {
- for (;;) {
- Message msg = queue.next();
- msg.target.dispatchMessage(msg);
- }
- }
在loop()方法中,循环从MessageQueue中获取Message对象(Message msg = queue.next();),然后调用 msg.target.dispatchMessage(msg)方法进行处理,哪个handler发送的消息就由哪个handler来处理,而我们在讲发送消息时也做过铺垫了。来看dispatchMessage()方法的定义:
- public void dispatchMessage(Message msg) {
- if (msg.callback != null) {
- handleCallback(msg);
- } else {
- if (mCallback != null) {
- if (mCallback.handleMessage(msg)) {
- return;
- }
- }
- handleMessage(msg);
- }
- }
- private static void handleCallback(Message message) {
- message.callback.run();
- }
- /**
- * Subclasses must implement this to receive messages.
- */
- //子类必须实现该方法来处理消息
- public void handleMessage(Message msg) {
- //空的
- }
我们从一个简单常见的例子入手,先以Handler对象的创建为出发点,大致将Handler、Looper和MessageQueue 的相互关系理清,接下来,分析在子线程中创建消息、发送消息和在主线程中处理消息的过程及在这些过程中的常用方法。