Android UI 主线程,啥玩意?还有Handler+Looper+MessageQueue几个意思?
2015-08-11 09:15
726 查看
写在前面:以前有什么知识,都是写在云笔记里,因为有很多只是草稿,发出来怕误导别人。而且,要发出来就得严肃的写了,多麻烦。最近感觉人老了,有些事看开了,决定要分享,哪怕多花点时间,错就错,留着让别人指正呗。也许多年以后,我不做技术了,还能回忆回忆。好吧,let’s go…
以前写App,都是迅速百度一下,复制黏贴修改,就O了!但作为一个在IC公司的人,做framework的人,竟然没去了解一下framework,实属罪过!我印象中,最没认真去研究的,就是UI主线程了。
每个android教程,都说更新UI要在主线程,要这么做,只要一个Handler就O了。但很多人不知道真正原理是啥。下面贴个例子。
为啥new 一个Handler,把画UI的事情放在handleMessage,就算在UI主线程干活了呢?其实这样从源头说起。
首先的首先,先给出个结论,就是Android app使用消息机制实现线程间的通信,线程通过Looper建立自己的消息循环,MessageQueue是FIFO的消息队列,Looper负责从MessageQueue中取出消息,并且分发到消息指定目标Handler对象。Handler对象绑定到线程的局部变量Looper,封装了发送消息和处理消息的接口。
(PS: 不要晕,看不懂就先跳过。。。^^)
写过代码的都知道,不管是C还是java,都有一个main函数作为主线程的入口。对android的一个应用程序来说,ActivityThread的main函数就是入口。只是android已经把这个main全部写好,不需要你写而已。
ActivityThread其实就是我们经常说的UI thread,也就是主线程。它的main函数恰恰创建了Looper+MessageQueue+Handler的一套东西。
来看一下这个main函数。
a) Looper.prepareMainLooper();
该函数创建了Looper。
b) sMainThreadHandler = thread.getHandler();
其实就指向了mH,mH就是Handler,它可以发送/处理各种高大上的消息。
c) Looper.loop();
Loop开始转起来。loop()函数其实就是一个while循环。不断的从MessageQueue取出消息(queue.next()),然后转发消息(dispatchMessage)。
也就是说,一个android程序,首先从ActivityThread的main函数启动,创建了一个主线程。
ActivityManagerService通过binder,与ActivityThread取得联系,并调用相关API,启动某个activity。
ActivityThread内部,启动某个activity就用了looper+message+handle一套东西。
大致流程如下。
a) scheduleLaunchActivity
b) sendMessage(H.LAUNCH_ACTIVITY, r) 传说中的mH发送消息,looper会接收消息,按先进先出的原则存储/转发消息,转发其实就是转发到mH,于是回到mH的回调函数handleMessage。
c) mH处理消息,执行handleLaunchActivity
d) performLaunchActivity
e) mInstrumentation.callActivityOnCreate(activity, r.state);
于是乎,进入到了我们自己写的OnCreate函数了!!
接下来,说一下Handler+Looper+MessageQueue。
为啥我们自己在某个Activity内部又写了一个Handler,也能自己转起来呢?简单的说,是因为一个Looper和MessageQueue可以指向多个Handler!而我们新建的Handler指向了ActivityThread已经创建好的Looper和MesageQueue。
要解释这个,有必要看一下Handler的代码。
假设我们new了3个Handler,这3个Handler对象,都会执行Looper的静态函数myLooper()。来看一下这个函数。
由于在ActivityThread已经执行过prepareMainLooper,因此sThreadLocal存了一个Looper对象。
当别人调用myLooper()时,sThreadLocal.get()获得的值,仍是同一个Looper对象。
也就是说,在当前的application,不管new几个Handler,Handler内部的mLooper变量都指向了主线程的Looper,内部的mQueue指向了主线程的mQueue(因为new一个Looper的时候,同时也new一个MessageQueue,因此也意味着指向同一个MessageQueue)。
有了上面的结论,我们就明白了,为啥写一个Activity,只需要简单的写一个Handler,实现handleMessage就可以实现异步通信了。
好了,接下来看看我们用Handler发消息,处理消息的过程。
我们经常看到,发消息简单的一句话就行。
mHandler.obtainMessage(MSG_SUCCESS).sendToTarget();
几个意思呢?
首先,mHandler.obtainMessage(MSG_SUCCESS),相当于new了一个Message。并且该Message会绑定到当前的Handler(其实就是他的内部变量target等于当前的mHandler)。且看其定义就明白。
看一下Message如何实现。
好了,那Message的一个对象,sendToTarget又会发生什么呢?
看一下Message的sendToTarget。
也就是,相当于执行。
mHandler.sendMessage(本消息);
好吧,又回到了Handler的sendMessage函数。来看一下。
最终调用到sendMessageAtTime,当看到
MessageQueue queue = mQueue;
我们就知道,把消息给queue到主线程的MessageQueue了!!!!
由上文已经知道,主线程的Looper会不断的从MessageQueue里拿出消息,然后分发(其实就是Looper的loop()函数干了这个事)。
也就是说,主线程的Looper,其loop()函数一个个拿出消息,上文贴出的loop()函数可以看到,拿出一个msssage执行了
msg.target.dispatchMessage(msg);
也就是说,指向了message对应的Handler的dispatchMessage函数。
现在,又回到了Handler了,且看他的dispatchMessage
也就是说,调用了dispatchMessage,最终就会调用handleMessage,而这个handleMessage是我们new一个Handler必须实现的回调函数,来处理自己想要关心的消息!
好了,现在整条路都理清楚了,是不是发现没辣么复杂?~~^^~~
总结一下。
一个android application,会有一个UI主线程。(不是Activity喔)。
UI主线程由android系统帮我们启动,不需要操心。
启动后,会实例化一个Looper和MessageQueue,甚至还有一个Handler。
这3个默认的东西,分别是如下作用。
Looper,消息泵,不断的从MessageQueue拿出消息,然后分发。
MessageQueue存储各种Message。
Handler,处理消息。默认的那个Handler(即ActivityThread创建的Handler),做的是很高大上的工作,比如,帮我们启动想要的activity,横竖屏切换,接收消息等,但我们看不见也不需要关心。
这些事做完后,会调用到Activity的oncreate函数,开始执行我们自己想要做的事情。
一个Looper对应一个MessageQueue,但可以对应多个Handler。
在某个Activity实例化一个Handler都会使用android背后默默创建的Looper和Message。对写app的人来说,都不需要关心消息怎么转,只需要负责发消息,收消息就行了!
但是,和UI无关的事情,又特别耗时间的,那就别麻烦Handler了,他管理UI已经很辛苦了,而且如果你做事情耗时,Handler光把时间花在这些上了,没空去更新UI,会造成UI很卡,所以这些事应该起一个子线程来搞了。
一般子线程与UI主线程的合作关系是:
子线程去做各种耗时的事情,忙完了,就发一个消息,消息体放着好不容易耗时做完的结果,消息放到MessageQuue,然后经过Loop一转,分发到Handler对象,处理这个结果。
下面就给出个经典的例子。
.
不扯了,吃饭去了!^^
以前写App,都是迅速百度一下,复制黏贴修改,就O了!但作为一个在IC公司的人,做framework的人,竟然没去了解一下framework,实属罪过!我印象中,最没认真去研究的,就是UI主线程了。
每个android教程,都说更新UI要在主线程,要这么做,只要一个Handler就O了。但很多人不知道真正原理是啥。下面贴个例子。
package com.test; import android.app.Activity; import android.os.Bundle; import android.os.Message; import android.util.Log; import android.os.Handler; import android.view.ImageView; public class UIHandlerActivity extends Activity { public static final String TAG = "UIHandlerActivity"; public static final int MSG_TEST = 1; public static final Handler mHandler; public ImageView mImageView; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mImageView.findViewById(R.id.image); Log.d(TAG, "The main thread id = " + Thread.currentThread().getId() + "/n"); mHandler = new Handler(){ public void handleMessage (Message msg) { switch(msg.what) { case MSG_TEST: Log.d(TAG, "The handler thread id = " + Thread.currentThread().getId()); mImageView.setImageDrawable(getContext().getDrawable(R.drawable.ic_play)); break; } } }; new myThread().start(); } class myThread extends Thread { public void run() { Message msg = new Message(); msg.what = MSG_TEST; mHandler.sendMessage(msg); //或者mHandler.obtainMessage(MSG_TEST).sendToTarget(); Log.d(TAG, "The worker thread id = " + Thread.currentThread().getId() + "/n"); } } }
为啥new 一个Handler,把画UI的事情放在handleMessage,就算在UI主线程干活了呢?其实这样从源头说起。
首先的首先,先给出个结论,就是Android app使用消息机制实现线程间的通信,线程通过Looper建立自己的消息循环,MessageQueue是FIFO的消息队列,Looper负责从MessageQueue中取出消息,并且分发到消息指定目标Handler对象。Handler对象绑定到线程的局部变量Looper,封装了发送消息和处理消息的接口。
(PS: 不要晕,看不懂就先跳过。。。^^)
写过代码的都知道,不管是C还是java,都有一个main函数作为主线程的入口。对android的一个应用程序来说,ActivityThread的main函数就是入口。只是android已经把这个main全部写好,不需要你写而已。
ActivityThread其实就是我们经常说的UI thread,也就是主线程。它的main函数恰恰创建了Looper+MessageQueue+Handler的一套东西。
ActivityThread
final ApplicationThread mAppThread = new ApplicationThread(); final Looper mLooper = Looper.myLooper(); final H mH = new H(); ...... public static final void main(String[] args) { SamplingProfilerIntegration.start(); …… Looper.prepareMainLooper(); ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } …… Looper.loop(); …… }
来看一下这个main函数。
a) Looper.prepareMainLooper();
该函数创建了Looper。
b) sMainThreadHandler = thread.getHandler();
其实就指向了mH,mH就是Handler,它可以发送/处理各种高大上的消息。
private class H extends Handler { public static final int LAUNCH_ACTIVITY = 100; public static final int PAUSE_ACTIVITY = 101; public static final int PAUSE_ACTIVITY_FINISHING= 102; public static final int STOP_ACTIVITY_SHOW = 103; public static final int STOP_ACTIVITY_HIDE = 104;
c) Looper.loop();
Loop开始转起来。loop()函数其实就是一个while循环。不断的从MessageQueue取出消息(queue.next()),然后转发消息(dispatchMessage)。
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ public static void loop() { for (;;) { Message msg = queue.next(); // might block if (msg == null) { // No message indicates that the message queue is quitting. return; } msg.target.dispatchMessage(msg); }
也就是说,一个android程序,首先从ActivityThread的main函数启动,创建了一个主线程。
ActivityManagerService通过binder,与ActivityThread取得联系,并调用相关API,启动某个activity。
ActivityThread内部,启动某个activity就用了looper+message+handle一套东西。
大致流程如下。
a) scheduleLaunchActivity
b) sendMessage(H.LAUNCH_ACTIVITY, r) 传说中的mH发送消息,looper会接收消息,按先进先出的原则存储/转发消息,转发其实就是转发到mH,于是回到mH的回调函数handleMessage。
c) mH处理消息,执行handleLaunchActivity
d) performLaunchActivity
e) mInstrumentation.callActivityOnCreate(activity, r.state);
于是乎,进入到了我们自己写的OnCreate函数了!!
接下来,说一下Handler+Looper+MessageQueue。
Handler+Looper+MessageQueue详解
通过上文,我们知道,一个application有一个UI主线程,对应一个Looper和MesageQueue,还有一个Handler。为啥我们自己在某个Activity内部又写了一个Handler,也能自己转起来呢?简单的说,是因为一个Looper和MessageQueue可以指向多个Handler!而我们新建的Handler指向了ActivityThread已经创建好的Looper和MesageQueue。
要解释这个,有必要看一下Handler的代码。
/** * Default constructor associates this handler with the {@link Looper} for the * current thread. * * If this thread does not have a looper, this handler won't be able to receive messages * so an exception is thrown. */ 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; }
假设我们new了3个Handler,这3个Handler对象,都会执行Looper的静态函数myLooper()。来看一下这个函数。
final MessageQueue mQueue; static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); public static Looper myLooper() { return sThreadLocal.get(); } 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)); } /** * 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(); } } public static MessageQueue myQueue() { return myLooper().mQueue; } private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }
由于在ActivityThread已经执行过prepareMainLooper,因此sThreadLocal存了一个Looper对象。
当别人调用myLooper()时,sThreadLocal.get()获得的值,仍是同一个Looper对象。
也就是说,在当前的application,不管new几个Handler,Handler内部的mLooper变量都指向了主线程的Looper,内部的mQueue指向了主线程的mQueue(因为new一个Looper的时候,同时也new一个MessageQueue,因此也意味着指向同一个MessageQueue)。
有了上面的结论,我们就明白了,为啥写一个Activity,只需要简单的写一个Handler,实现handleMessage就可以实现异步通信了。
好了,接下来看看我们用Handler发消息,处理消息的过程。
我们经常看到,发消息简单的一句话就行。
mHandler.obtainMessage(MSG_SUCCESS).sendToTarget();
几个意思呢?
首先,mHandler.obtainMessage(MSG_SUCCESS),相当于new了一个Message。并且该Message会绑定到当前的Handler(其实就是他的内部变量target等于当前的mHandler)。且看其定义就明白。
/** * Same as {@link #obtainMessage()}, except that it also sets the what member of the returned Message. * * @param what Value to assign to the returned Message.what field. * @return A Message from the global message pool. */ public final Message obtainMessage(int what) { return Message.obtain(this, what); }
看一下Message如何实现。
public static Message obtain() { ...... return new Message(); } public static Message obtain(Handler h, int what) { Message m = obtain(); m.target = h; m.what = what; return m; }
好了,那Message的一个对象,sendToTarget又会发生什么呢?
看一下Message的sendToTarget。
/** * Sends this Message to the Handler specified by {@link #getTarget}. * Throws a null pointer exception if this field has not been set. */ public void sendToTarget() { target.sendMessage(this); }
也就是,相当于执行。
mHandler.sendMessage(本消息);
好吧,又回到了Handler的sendMessage函数。来看一下。
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); }
最终调用到sendMessageAtTime,当看到
MessageQueue queue = mQueue;
我们就知道,把消息给queue到主线程的MessageQueue了!!!!
由上文已经知道,主线程的Looper会不断的从MessageQueue里拿出消息,然后分发(其实就是Looper的loop()函数干了这个事)。
也就是说,主线程的Looper,其loop()函数一个个拿出消息,上文贴出的loop()函数可以看到,拿出一个msssage执行了
msg.target.dispatchMessage(msg);
也就是说,指向了message对应的Handler的dispatchMessage函数。
现在,又回到了Handler了,且看他的dispatchMessage
/** * Subclasses must implement this to receive messages. */ public void handleMessage(Message msg) { } /** * 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); } }
也就是说,调用了dispatchMessage,最终就会调用handleMessage,而这个handleMessage是我们new一个Handler必须实现的回调函数,来处理自己想要关心的消息!
好了,现在整条路都理清楚了,是不是发现没辣么复杂?~~^^~~
总结一下。
一个android application,会有一个UI主线程。(不是Activity喔)。
UI主线程由android系统帮我们启动,不需要操心。
启动后,会实例化一个Looper和MessageQueue,甚至还有一个Handler。
这3个默认的东西,分别是如下作用。
Looper,消息泵,不断的从MessageQueue拿出消息,然后分发。
MessageQueue存储各种Message。
Handler,处理消息。默认的那个Handler(即ActivityThread创建的Handler),做的是很高大上的工作,比如,帮我们启动想要的activity,横竖屏切换,接收消息等,但我们看不见也不需要关心。
这些事做完后,会调用到Activity的oncreate函数,开始执行我们自己想要做的事情。
一个Looper对应一个MessageQueue,但可以对应多个Handler。
在某个Activity实例化一个Handler都会使用android背后默默创建的Looper和Message。对写app的人来说,都不需要关心消息怎么转,只需要负责发消息,收消息就行了!
Handler的用途
如前文所说,我们new一个Handler,是运行在UI主线程中的,Handler通过handleMessage,来更新我们想要的UI动作,比如画个图呀,动态更新文字呀啥的。总之,所有和UI更新有关的,都放到Handler中。但是,和UI无关的事情,又特别耗时间的,那就别麻烦Handler了,他管理UI已经很辛苦了,而且如果你做事情耗时,Handler光把时间花在这些上了,没空去更新UI,会造成UI很卡,所以这些事应该起一个子线程来搞了。
一般子线程与UI主线程的合作关系是:
子线程去做各种耗时的事情,忙完了,就发一个消息,消息体放着好不容易耗时做完的结果,消息放到MessageQuue,然后经过Loop一转,分发到Handler对象,处理这个结果。
下面就给出个经典的例子。
public class MyHandlerActivity extends Activity { Button button; MyHandler myHandler; protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.handlertest); button = (Button) findViewById(R.id.button); myHandler = new MyHandler(); // 当创建一个新的Handler实例时, 它会绑定到当前线程和消息的队列中,开始分发数据.我们在onCreate new,所以就绑定到UI主线程。 MyThread m = new MyThread(); new Thread(m).start(); } /** * 接受消息,处理消息 ,此Handler会与当前主线程一块运行 * */ class MyHandler extends Handler { public MyHandler() { } public MyHandler(Looper L) { super(L); } // 子类必须重写此方法,接受数据 @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub Log.d("MyHandler", "handleMessage......"); super.handleMessage(msg); // 此处可以更新UI Bundle b = msg.getData(); String txt = "" + b.getInt("txt"); MyHandlerActivity.this.button.setText(txt); } } class MyThread implements Runnable { int demo_txt = 1; public void run() { while(1) { //做各种耗时的累活,咱这里装睡。很多usecase是去网络上download数据。 try { Thread.sleep(10000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } Log.d("thread.......", "mThread........"); Message msg = new Message(); Bundle b = new Bundle();// 存放数据 b.putInt("txt", demo_txt++); msg.setData(b); MyHandlerActivity.this.myHandler.sendMessage(msg); // 向Handler发送消息,更新UI } } } }
.
结束
所以啦,android系统确实有人家的好,让写app的人只需要做很少的工作,就可以写一个app,入门很快。不扯了,吃饭去了!^^
相关文章推荐
- Android开发笔记之:Handler Runnable与Thread的区别详解
- android的消息处理机制(图文+源码分析)―Looper/Handler/Message
- Android中的Looper对象详细介绍
- Android消息处理机制Looper和Handler详解
- AsyncTask陷阱之:Handler,Looper与MessageQueue的详解
- Android中的Handler与多线程应用实例
- android开发教程之handler异步更新ui
- Android定时器和Handler用法实例分析
- Toast和Handler的间隔使用实例
- android开发教程之android的handler使用方法
- Android开发笔记 Handler使用总结
- Android中Handler引起的内存泄露问题解决办法
- Handler与Android多线程详解
- android中的handler
- android定时方法
- Service通知Activity修改UI
- Message、Handler、Message Queue、Looper、Thread之间的关系(未完成)
- Android Looper简介
- Android Handler