参考1
参考2
提到这三个类,我们很容易联系到异步消息机制,那么异步消息机制的作用是什么呢?
解决如下问题:
由于1,Android中的只有UI线程才能更新UI线程
2,并且UI线程里不能进行耗时操作,否则会报出ANR异常。
那么我们如何进行耗时操作,并且更新UI界面的变化,这时异步消息机制就可以大展拳脚了!
Looper:负责维护一个MessageQueue,并且与当前线程绑定(利用ThreadLocal),一个线程只能对应一个looper对象,
Looper.prepare(): sThreadLocal是一个ThreadLocal对象,下面的代码说明只能调用一次prepare,否则会异常,
static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
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)); }
从上面代码可知,prepare()应该在loop()之前,因为,必须先sThreadLocal.set(XXX)之后,才能sThreadLocal.get(XXX).否则会报空指针异常,除非创建ThreadLocal实例时,需要覆盖initialValue()方法。具体为什么,可以详细参看ThreadLocal源码。
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; //Looper维护的MessageQueue // Make sure the identity of this thread is that of the local process, // and keep track of what that identity token actually is. Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // 无限循环,取出消息 if (msg == null) { // 如果消息为空,就退出 return; } // This must be in a local variable, in case a UI event sets the logger Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } msg.target.dispatchMessage(msg);//处理消息的地方,这里msg.target就是Handle对象 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.recycle(); } }
上面提到,Looper内部维护一个MessageQueue
public static MessageQueue myQueue() { return myLooper().mQueue; }
public static Looper myLooper() { return sThreadLocal.get(); }
从上面的源码中可以看出来,Looper先通过prepare()获得一个looper实例,然后通过loop(),维护一个MessageQueue,逐条取出消息,并分发消息。由于分发消息要用到msg.target(其实就是Handler对象)的dispatchMessage方法,因此在调用Looper.loop()方法前需要先初始化Handle对象。
下面追踪一下Handler的dispatchMessage方法:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }从上面可知,最终调用了handleMessage方法,大家一定不陌生,这个方法就是我们实例化Handler时用匿名内部类覆盖方法handleMessage,里面包含了我们具体处理消息的逻辑。
下面具体看一下Handler内部的实现,看看它是怎么和Looper以及MessageQueue关联起来的。
先看构造函数
public Handler() { this(null, false); }
public Handler(Callback callback, boolean async) { if (FIND_POTENTIAL_LEAKS) { final Class<? extends Handler> klass = getClass(); if ((klass.isAnonymousClass() || klass.isMemberClass() || klass.isLocalClass()) && (klass.getModifiers() & Modifier.STATIC) == 0) { Log.w(TAG, "The following Handler class should be static or leaks might occur: " + klass.getCanonicalName()); } } 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 = callback; mAsynchronous = async; }从上面的注释中可以看出来Handler实例化过程中,和Looper,Message关联起来。
前面已经分析了Looper,Handler,Message之间的关系,
流程是这样的:Looper.prepare()获取Looper实例,并维护一个MessageQueue,然后初始化一个handler对象(一般会复写handleMessage方法,采用匿名内部类),此时Looper,MessageQueue,Handler都有了,然后调用Looper.loop()循环取出消息,并分发处理消息(分发消息的过程中会调用handleMessage方法)。
其实还遗漏了一点,就是如何得到Message的?
其实,除了分发消息以外,发送消息也是Handler要做的工作,要不怎么说回调呢,下面看看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); }
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this;//是不是很熟悉?前面已经提到,msg.target就是Handler对象 if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
有时候,我们会不用Handler.sendMessage方法发送信息,而是用Handler.post(Runnable r)方法,这样一来实例化handler对象就不用覆盖handleMessage方法了,比较方便。
Handler .post方法代码如下:
public final boolean post(Runnable r) { return sendMessageDelayed(getPostMessage(r), 0); }
private static Message getPostMessage(Runnable r) { Message m = Message.obtain(); <span style="white-space:pre"> </span><span style="color:#ff0000;"> m.callback = r;</span> return m; }
那么就会有人有疑问了,如果连handleMessage方法都不需要编写了,那消息具体是如何处理的呢?,问的好!大家是否还记得Handler分发消息的方法:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }在分发消息的实现中,并不是只有handleMessage()这一条逻辑,当msg.callback!=null时,会走handleCallback(msg)逻辑,从上面getPostMessage(Runnable r)方法里面加红的代码中可以看出,当调用post方法时,msg.target!=null因此会走handleCallback(msg)逻辑。
private static void handleCallback(Message message) { message.callback.run(); }注意这里调用的是run方法,如果大家知道run方法与start方法的区别,就会知道run方法并没有开启一个独立的线程,调用run方法就相当于普通方法的调用,并没有产生新的线程。从上面的代码可以知道message.callback.run()运行的是post(Runnable r)里面的run方法里面的内容.
示例1,
MainActivity1里面的代码:
private Button btn; private TextView text; private Handler handler = new Handler(){ private int process = 0; @Override public void handleMessage(Message msg) { switch(msg.what){ case 0://更细下载进度 process += 1; text.setText("下载" + process + "%");//在主线程中更新UI界面 break; case 1://提示下载完成 text.setText("下载完成");//在主线程中更新UI界面 break; default: break; } } }; //onCreate之类的生命周期的方法就是允许在UI主线程中 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btn); text = (TextView) findViewById(R.id.text); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { new Thread(){ @Override public void run() { //为了不阻塞主线程,在子线程中进行下载耗时操作 for(int i = 0; i < 100; i++){ try { Thread.sleep(200);//休眠0.2秒,模拟耗时操作 } catch (InterruptedException e) { e.printStackTrace(); } handler.sendEmptyMessage(0);//发送消息到handler,通知下载进度 } handler.sendEmptyMessage(1);//发送消失到handler,通知主线程下载完成 } }.start(); } }); }
示例2,
MainActivity2里面的代码:
private Button btn; private TextView text; private Handler handler = new Handler();//简化了实例化过程,没有handleMessage(msg) //onCreate之类的生命周期的方法就是允许在UI主线程中 @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn = (Button) findViewById(R.id.btn); text = (TextView) findViewById(R.id.text); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { new Thread(){ @Override public void run() { //防止阻塞UI线程,所以在子线程中进行下载耗时操作 try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } handler.post(new Runnable() { @Override public void run() { text.setText("下载完成");<span style="font-family: Arial, Helvetica, sans-serif;">/<span style="color:#ff0000;">/发送消息到handler,通知主线程下载完成,这里的run()里其实是在主线程里,因此不能有耗时操作</span> </span> } }); } }.start(); }对于加红色的注释,大家也许会有很大疑问,质疑这句话的正确性,下面会再次提到,这里先简单说一下(当前UI线程会自动调用Looper.prepare()和Looper.loop()方法,大家还记得Looper.loop()里面的msg.target.dispatchMessage(msg)吗?)好了,看下面流程
在UI线程中:Looper.prepare()->合适的时候发送消息:handle.postMessage(Runnable r)->Looper.loop()->mag.target.dispatchMessage(msg)->handleMesssage(Runnable r)()->msg.callback.run(此时的run方法就是我标注红色注释不能有耗时操作的地方)
看完流程大家应该就没疑问了。
写了那么多内容,大家是否还有点疑惑,为甚么在UI线程里面使用异步消息机制时,我们只是实例化了Handler(覆盖了handleMessage方法),并在合适的地方调用sendMessge方法或者调用postMessage(Runnable r),我们似乎并没有看到Looper的身影,是的,在UI线程中,我们虽然显示的看不到Looper.prepare(),和Looper.loop()这样异步消息机制所必须的方法,但是实际上是系统已经帮我们隐式的执行了Looper.prepare(),和Looper.loop()方法,注意我一直强调UI线程,因为在Activity的启动代码中,已经在当前UI线程调用了Looper.prepare()和Looper.loop()方法。当然如果是非UI线程,那么就必须按照我们以前提到的流程调用代码,因为此时不会有其他人帮我们调用了。一般我们会把这一系列流程放入单独的Thread线程里。
最后再啰嗦一句UI线程:
在一个Android 程序开始运行的时候,会单独启动一个Process。默认的情况下,所有这个程序中的Activity或者Service(Service和 Activity只是Android提供的Components中的两种,除此之外还有Content Provider和Broadcast Receiver)都会跑在这个Process。
一个Android 程序默认情况下也只有一个Process,但一个Process下却可以有许多个Thread。在这么多Thread当中,有一个Thread,我们称之为UI Thread。UI Thread在Android程序运行的时候就被创建,是一个Process当中的主线程Main Thread,主要是负责控制UI界面的显示、更新和控件交互。在Android程序创建之初,一个Process呈现的是单线程模型,所有的任务都在一个线程中运行。因此,我们认为,UI Thread所执行的每一个函数,所花费的时间都应该是越短越好。而其他比较费时的工作(访问网络,下载数据,查询数据库等),都应该交由子线程去执行,以免阻塞主线程。