Android中的异步消息机制--Handler

1,437 阅读5分钟
原文链接: nicelyjust.github.io

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.

蹩脚的英文翻译 ==!:

handler允许你发送和处理消息Message对象与一个线程的{@link messagequeue}相关联的runnable 对象。每个Handler实例实例都与单个线程和该线程的消息队列相关联。当你创建一个新的handler时,它绑定到正在创建它的线程的线程/消息队列 - 从那时开始,它将把messages和可运行消息(runnables)传递到消息队列并在它们出来消息队列时执行它们

简而言之,Handler是Android中一种消息循环处理机制,负责线程之间的通信.

问题带入

我们学习Android时候就知道 只有主线程才能更新UI,在子线程中我们只能通过handler.sendMessage或者runOnUiThread方法达到更新UI的操作,相信大家至少会有两个疑问,1.handler是怎么处理这一过程的;2.为什么非得主线程才能更新UI?

问题分析

ok,带着我们的疑问去看问题,我们通过handler的用法步骤,从而进一步的探究

// 代码A
 private static class MyHandler extends Handler {

    private  WeakReference<MainActivity> mWeakReference;

    public MyHandler(MainActivity mainActivity) {
        this.mWeakReference = new WeakReference<>(mainActivity);
    }

    @Override
    public void handleMessage(Message msg) {
        int what = msg.what;
        if(what==1){
            mWeakReference.get().upData(msg.obj);
        }

    }
}
mHandler = new MyHandler();

new Thread(new Runnable() {
        @Override
        public void run() {
            try {
                Thread.sleep(500);
                // do someThing
                Message message = new Message();
                message.what = 1;
                mHandler.sendMessage(message);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }).start();

new Handler()内部做了什么

首先我们new了一个handler对象,默认走父类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;
}

这里一个Looper类的静态方法myLooper()的得到looper对象,是空的话就抛异常–>不能在没有调用looper.prepare()的线程中创建handler.

①
/**
 * Return the Looper object associated with the current thread.  Returns
 * null if the calling thread is not associated with a Looper.
 * 返回当前线程相关联的Looper对象,如果调此方法的线程没有相关联的Looper
 */
public static @Nullable Looper myLooper() {
    return sThreadLocal.get();
}

我们接下来看看Looper类,looper通过ThreadLocal.get()获取,(每个线程都有一个ThreadLocal variables,暂时理解为一个线程本地变量,这里不展开讲了,大家有兴趣研究一下),我们看一下looper在哪里set到threadLocal里的,好像前面myLooper()方法得到null的对象时有说没调用Looper.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的构造函数私有化并实例化了消息队列MessageQueue

private Looper(boolean quitAllowed) {
    mQueue = new MessageQueue(quitAllowed);
    mThread = Thread.currentThread();
}

这里估计又要问候下f*** source code,我们回顾下代码A并没有调用prepare()或者prepare(boolean quitAllowed),但使用时并没有抛异常呢,必定其他地方有调用了, looper类里搜一下prepare,发现

/**
 * 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()}
 */
public static void prepareMainLooper() {
    prepare(false);
    synchronized (Looper.class) {
        if (sMainLooper != null) {
            throw new IllegalStateException("The main Looper has already been prepared.");
        }
        sMainLooper = myLooper();
    }
}

发现这个方法有两个地方ActivityThread类的main()方法以及SystemServer类(选择性忽略,待研究…滑稽脸.jpg)调用到

public static void main(String[] args) {
   // ...

    Looper.prepareMainLooper();

    if (sMainThreadHandler == null) {
        // 这里得到handler
        sMainThreadHandler = thread.getHandler();
    }

    if (false) {
        Looper.myLooper().setMessageLogging(new
                LogPrinter(Log.DEBUG, "ActivityThread"));
    }

    // ...
    Looper.loop();
    //...
}

原来我们的应用创建时就在主线程就创建好了looper,接下来调用了loop()方法

 /**
 * Run the message queue in this thread. Be sure to call
 * {@link #quit()} to end the loop. 
 * BTW 退出应用时会调用此方法
 */
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;

    // ...
    // 无限循环
    for (;;) {
        // 划重点,从消息队列中取出消息
        Message msg = queue.next(); // might block
        if (msg == null) {
            // No message indicates that the message queue is quitting.
            return;
        }

        // ...
        try {
            // 划重点,分发消息
            msg.target.dispatchMessage(msg);
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }

        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.recycleUnchecked();
    }
}

handler.sendMessage()又做了些什么

handler实例调用sendMessage()

 public final boolean sendMessage(Message msg){
    return sendMessageDelayed(msg, 0);
}

最终调用了:sendMessageAtTime()

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) {
    // message这时持有了当前handler实例,这也是handler会内存leak
    msg.target = this;
    if (mAsynchronous) {
        msg.setAsynchronous(true);
    }
    return queue.enqueueMessage(msg, uptimeMillis);
}

将消息放入消息队列中,looper中loop()方法,轮询去消息队列取消息messagequeue.next(),假若消息不为空,将此消息派发下去 msg.target.dispatchMessage(msg)(target即当前handler对象);

 /**
 * Handle system messages here.
 */
public void dispatchMessage(Message msg) {
    if (msg.callback != null) {

        // callback 来自handler.post(Runnable r)
        handleCallback(msg);
    } else {
        // handler构造函数 传的callback

        if (mCallback != null) {
            if (mCallback.handleMessage(msg)) {
                return;
            }
        }
        // handle 消息 外部重写的方法
        handleMessage(msg);
    }
}

问题总结

说了这么多,问题1其实整个流程可总结为一张图:Handler,Looper,MessageQueue

原图出处:blog.csdn.net/iispring/ar…

问题二:至于怎么在子线程中更新UI,如果看懂了上面的整个流程,我们只需要仿照主线程操作即可,

class LooperThread extends Thread {
   public Handler mHandler;

   public void run() {
   Looper.prepare();

   mHandler = new Handler() {
       public void handleMessage(Message msg) {
           // process incoming messages here
           // update UI new 一个progressBar
       }
   };

   Looper.loop();
   // 发送伪代码
   mHanler.sendMessage()
 }
  }

这样就可以达到更新UI的,这里我们需要自己去维护loop,其实android中HandlerThread类就是做这个事的,大家有兴趣的可以去看看.

-------------本文结束感谢您的阅读-------------