之前的文章写的都是关于Bitmap和内存的优化技术,这一篇文章给大家谈谈Handler。
Handler是Android系统中比较重要的一个知识,在Android多线程面试经常会被问到,在实际项目中的确也经常用到。当然也比较复杂,知识比较多,牵扯到的类有Thread、Looper、Message、MessageQueue。
Android是支持多线程的,通常应用程序中与用户相关的UI事件都是运行在主线程中,比如点击屏幕、按钮等,为了保持主线程顺畅相应用户事件不被阻塞就需要把耗时的操作(主要是联网、操作大文件等)放到子线程中,这个时候你可能会想到Handler(当然还你可以用其他的比如:异步任务,,这个以后再讲),但是Handler又是怎么和Thread联系起来的呢?这个咱们来看一下Android主线程是怎么创建的。
在ActivityThread.java中有一个main()函数,这个函数就是在一个应用启动的入口,调用关系是:ActivityManagerService.java中的startProcessLocked函数调用如下代码:
// Start the process. It will either succeed and return a result containing // the PID of the new process, or else throw a RuntimeException. Process.ProcessStartResult startResult = Process.start("android.app.ActivityThread", app.processName, uid, uid, gids, debugFlags, app.info.targetSdkVersion, null);
Process.start又做了如下的操作,只看方法注释就行,现在不需要知道具体做了什么:
/** * Start a new process. * * <p>If processes are enabled, a new process is created and the * static main() function of a <var>processClass</var> is executed there. * The process will continue running after this function returns. * * <p>If processes are not enabled, a new thread in the caller's * process is created and main() of <var>processClass</var> called there. * * <p>The niceName parameter, if not an empty string, is a custom name to * give to the process instead of using processClass. This allows you to * make easily identifyable processes even if you are using the same base * <var>processClass</var> to start them. * * @param processClass The class to use as the process's main entry * point. * @param niceName A more readable name to use for the process. * @param uid The user-id under which the process will run. * @param gid The group-id under which the process will run. * @param gids Additional group-ids associated with the process. * @param debugFlags Additional flags. * @param targetSdkVersion The target SDK version for the app. * @param zygoteArgs Additional arguments to supply to the zygote process. * * @return An object that describes the result of the attempt to start the process. * @throws RuntimeException on fatal start failure * * {@hide} */ public static final ProcessStartResult start(final String processClass, final String niceName, int uid, int gid, int[] gids, int debugFlags, int targetSdkVersion, String[] zygoteArgs) { try { return startViaZygote(processClass, niceName, uid, gid, gids, debugFlags, targetSdkVersion, zygoteArgs); } catch (ZygoteStartFailedEx ex) { Log.e(LOG_TAG, "Starting VM process through Zygote failed"); throw new RuntimeException( "Starting VM process through Zygote failed", ex); } }
通过注释也能看到上面的函数会找到ActivityThread的main函数并且执行。main函数中创建了Looper,Looper的作用就是利用线程创建一个消息处理队列,并且维护这个消息队列:
public static void main(String[] args) { Looper.prepareMainLooper();//创建Looper if (sMainThreadHandler == null) { sMainThreadHandler = new Handler(); } ActivityThread thread = new ActivityThread(); thread.attach(false);//应用所有的逻辑都在这个方法中 Looper.loop();//开启一个消息循环,不断的读取MessageQueue中的Message。 }
Looper.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()} */ public static void prepareMainLooper() { prepare(); setMainLooper(myLooper()); myLooper().mQueue.mQuitAllowed = false; }
上面的方法注释已经说的很明白,创建了主线程的Looper,这段代码是系统调用的。先看prepare方法做了什么操作。
/** 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()}. */ public static void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); } private Looper() { mQueue = new MessageQueue(); mRun = true; mThread = Thread.currentThread();//获取当前线程 }
创建主线程的Looper,每一个Looper对应一个Thread、一个MessageQueue,创建Looper的时候会创建一个MessageQueue。到目前位置创建了应用的主线程(Thread)、Looper、MessageQueue,调用Looper.loop(),开始不断的从MessageQueue中读取Message并处理,如果没有消息则等待。现在有了消息循环,有了管理消息循环的Looper就差发送消息和处理消息的Handler了。
这个时候你在你的应用中创建一个Handler,一般都是下面的代码:
private static final Handler mHandler = new Handler() { public void handleMessage(Message msg) { .......... } } };
这个Handler是在主线程中创建的,Handler的构造函数如下:
/** * Default constructor associates this handler with the queue for the * current thread. * * If there isn't one, this handler won't be able to receive messages. */ public Handler() { mLooper = Looper.myLooper();//获取上面在主线程创建的Looper if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } mQueue = mLooper.mQueue;//获取Looper的MessageQueue mCallback = null;//默认为null在后面处理msg时会就行检查 }
创建完Handler你就可以用了,比如你发一个消息:
mHandler.sendEmptyMessage(MSG_WHAT);
在系统中会走最终走到Handler.java下面的方法:
/** * Enqueue a message into the message queue after all pending messages * before the absolute time (in milliseconds) <var>uptimeMillis</var>. * <b>The time-base is {@link android.os.SystemClock#uptimeMillis}.</b> * You will receive it in {@link #handleMessage}, in the thread attached * to this handler. * * @param uptimeMillis The absolute time at which the message should be * delivered, using the * {@link android.os.SystemClock#uptimeMillis} time-base. * * @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. Note that a * result of true does not mean the message will be processed -- if * the looper is quit before the delivery time of the message * occurs then the message will be dropped. */ public boolean sendMessageAtTime(Message msg, long uptimeMillis) { boolean sent = false; MessageQueue queue = mQueue; if (queue != null) { msg.target = this;//注意这行代码后面会用,把Handler赋值给Msg的target对象 sent = queue.enqueueMessage(msg, uptimeMillis);//把msg放到MsgQueue中 } else { RuntimeException e = new RuntimeException( this + " sendMessageAtTime() called with no mQueue"); Log.w("Looper", e.getMessage(), e); } return sent; }
上面的方法第二个是延时毫秒数,queue.enqueueMessage把消息发送到MessageQueue后剩下的就是等待消息被处理,前面不是说了Looper.loop()方法开始轮询消息队列吗,你发送的消息就是在loop方法中读取到的,读取到后谁去处理呢?在loop()方法中有一句代码:
msg.target.dispatchMessage(msg);
msg就是你发送到MessageQueue的消息,被读取后调用target.dispatchMessage(),这个target就是上面Handler发送消息是赋值的,就是发送消息的Handler本身,然后Handler调用自己的下面方法就行消息处理:
/** * Handle system messages here. */ public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg);//在这会调用到上面重写的handleMessage方法。 } }
因为在new Message的时候callback为空,并且Handler的mCallback = null,所以会调用到你上面new Handler时重写的handleMessage方法。
每一个线程中都对应一个Looper,每一个Looper都对应一个MessageQueue,这个Looper是用来管理消息队列的,主要是读取消息队列和把消息发送给Message的target去处理。到这你应该清除Thread、Handler、Message、MessageQueue和Looper他们之间的关系了吧。
大家如果对编程感兴趣,想了解更多的编程知识,解决编程问题,想要系统学习某一种开发知识,我们这里有java高手,C++/C高手,windows/Linux高手,android/ios高手,请大家关注我的微信公众号:程序员互动联盟or coder_online,大牛在线为您提供服务。