写在前面:以前有什么知识,都是写在云笔记里,因为有很多只是草稿,发出来怕误导别人。而且,要发出来就得严肃的写了,多麻烦。最近感觉人老了,有些事看开了,决定要分享,哪怕多花点时间,错就错,留着让别人指正呗。也许多年以后,我不做技术了,还能回忆回忆。好吧,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,入门很快。
不扯了,吃饭去了!^^