您的位置:首页 > 产品设计 > UI/UE

Android UI 主线程,啥玩意?还有Handler+Looper+MessageQueue几个意思?

2015-08-11 09:15 726 查看
写在前面:以前有什么知识,都是写在云笔记里,因为有很多只是草稿,发出来怕误导别人。而且,要发出来就得严肃的写了,多麻烦。最近感觉人老了,有些事看开了,决定要分享,哪怕多花点时间,错就错,留着让别人指正呗。也许多年以后,我不做技术了,还能回忆回忆。好吧,let’s go…

以前写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,入门很快。

不扯了,吃饭去了!^^
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Handler Looper MessageQue