Looper与Handler解析
来源:互联网 发布:python 版本切换 编辑:程序博客网 时间:2024/04/28 20:51
写这篇文章的目的并不仅仅是为了将Looper和Handler,一方面是为后面其他相关的介绍做铺垫,另一方面是这个东西比较重要,自己也想认真的梳理一下,也想提醒一下大家,虽然关于这个的介绍网上已经有很多。
大家通常都会使用Handler+Thread组合,在Thread里面执行耗时的操作,然后使用Handler来执行UI的更新操作,但可能根本不知道其中的缘由,或者根本就不知在Handler的背后还有Looper的存在。
Looper和Handle的完整组合是这样的:
1、创建Looper:Looper.prepare();
2、执行消息队列的循环:Looper.loop()
3、handler向Looper的消息队列中发送消息。
首先我们来看段代码:
public class MainActivity extends Activity { private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Looper.prepare(); Looper.loop(); mHandler.sendEmptyMessage(0); }}
这段代码完全按照上面的步骤,但是运行后,程序直接崩溃报错。当然我们一般都不是这么来用的,这里为了说明一个问题。
首先来看看错误信息:java.lang.RuntimeException: Only one Looper may be created per thread
意思是说在每个线程中只能创建一个Looper,这就证明了,我们平时在UI主线程中使用Handle的时候,其实背后已经执行过了Looper.prepare()和Looper.loop(),并且创建了Looper,不用我们再来运行了。
我们通常的用法是这样的:
public class MainActivity extends Activity { private Handler mHandle = new Handler() { @Override public void handleMessage(Message msg) { //进行界面的更新操作 super.handleMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); new Thread(new Runnable() { @Override public void run() { //执行耗时操作 //执行完了通知UI线程进程进行界面更新 mHandle.sendEmptyMessage(0); } }).start(); }}
当然上面的代码存在着内存泄漏的问题,这里我们先不做考虑,把心思放在我们今天要讲的重点上,关于为什么出现这个问题后面再做讨论。这里就直接在我们定义的线程和UI线程中使用Handle进行通信,下面我们要进行两步讲解:
1、在UI线程中,Looper.prepare()和Looper.loop()在哪里执行的?
在Activity启动过程中会执行系统源码frameworks/base/core/java/android/app/ActivityThread.java文件。
我们来看看里面的main()函数:
public static void main(String[] args) { ...... Looper.prepareMainLooper(); ...... Looper.loop(); ......}
这下你应该看到了两个比较熟悉的面孔了吧!
2、怎样使用Looper和Handler实现线程间的通信?
首先让我们来看看Looper这个类主要包含哪些东西:
里面有一个ThreadLocal对象,有一个MessageQueue对象。
ThreadLocal主要用来保存数据,它是主要跟线程绑定,即存放对应线程中对应的数据,具体的原理这里就不展开了。
再来看源码,首先看Looper.prepare():
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)); }
看到if语句里面抛出的异常,是不是感觉比较熟悉,我们前面的那个异常就是这里抛出的,在prepare里面会创建一个Looper对象,把它放入ThreadLocal里面,但是在这个线程里面,Looper对象只能创建一次,所以前面会首先取出当前线程中的Looper进行判断,看是否已经创建,如果已经存在就会报错,这也是我们前面报错的原因,也就是说Looper对象只能有一份。
再看看Looper.loop()函数:
public static void loop() { final Looper me = myLooper(); //从返回的异常可以看出,调用这个函数前,必须先调用Looper.prepare()函数。 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } //在创建Looper对象的构造函数中,会创建消息队列,所以这里就可以直接使用 final MessageQueue queue = me.mQueue; // 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(); // might block if (msg == null) { // No message indicates that the message queue is quitting. 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); } //target里面存放的是Handle对象,使用handle进行消息分发,最终就会回调handleMessage方法 msg.target.dispatchMessage(msg); 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(); } }
现在我们来好好的分析一下我通常一般使用Handler的内部流程:
public class MainActivity extends Activity { private Handler mHandle = new Handler() { @Override public void handleMessage(Message msg) { //进行界面的更细操作 super.handleMessage(msg); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); new Thread(new Runnable() { @Override public void run() { //执行耗时操作 //执行完了通知UI线程进程进行界面更新 mHandle.sendEmptyMessage(0); } }).start(); }}
上面已经提过,在UI线程中已经运行过了Looper.prepare()和Looper.loop()。所以在UI线程中已经有一个Looper对象,并且在Looper对象中有一个消息队列。
在我们自定义线程中来向UI线程发送消息是怎么实现的呢?我接着看Handle里面的源码,一步一步进行跟踪,我们先看Handler的构造函数,因为我们首先创建了一个Handler对象:
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()); } } //这里获取到UI线程中创建的那个Looper对象 mLooper = Looper.myLooper(); if (mLooper == null) { throw new RuntimeException( "Can't create handler inside thread that has not called Looper.prepare()"); } //获取Looper里面的消息队列 mQueue = mLooper.mQueue; mCallback = callback; mAsynchronous = async; }
接着就是发送消息的流程:
public final boolean sendEmptyMessage(int what) { return sendEmptyMessageDelayed(what, 0); } public final boolean sendEmptyMessageDelayed(int what, long delayMillis) { //创建一个消息,把这个消息发送出去 Message msg = Message.obtain(); msg.what = what; return sendMessageDelayed(msg, delayMillis); } 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) { //把Looper里的消息队列赋值给queue 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) { //这里会把handler对象赋值给Message的taget对象,这样就可以在消息队列中回调Handle里面的handleMessage方法 msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } //最终往Looper那个对象的消息队列中把消息放进去 return queue.enqueueMessage(msg, uptimeMillis); }
消息放进去了之后,我们再来看看上面的Looper.loop()函数,在里面有msg.target.dispatchMessage(msg)这条语句,获取到消息后,因为msg的target里面存放的是就是这个Handle对象,这样就可以得到这个Handler对象,然后利用这个Handler进行消息的分发。
我们接着跟踪源码:
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
可以看到在这里最终回调到了handleMessage这个方法。这样我们就在UI线程中通过Looper的消息队列获取到这个消息,并且回调了Handler里的handleMessage方法,就可以进行UI的更新操作。
- Looper与Handler解析
- Handler、Looper与MessageQueue源码解析
- Handler,MessageQueue,Looper解析
- Handler、Looper、MessageQueue解析
- android looper ,handler 解析
- Handler Looper MessageQueue解析
- Looper.prepare() 与 handler
- Looper与Handler
- Looper、Handler与HandlerThread
- Looper与Handler
- handler looper message具体解析
- Handler Looper Message具体解析 .
- Handler、Looper、MessageQueue深入解析
- Handler,message,Looper源码解析
- Looper, Handler,Message关系解析
- Handler Looper MessageQueue源码解析
- Handler、Looper、Message源码解析
- Looper Handler MessageQueue源码解析
- Java基础之异常、io流
- 应届毕业生外包公司体验
- RTTI
- 十个Android Material Design库
- x-apple-amd-action_message错误 苹果客服提供的解决办法,亲测有效
- Looper与Handler解析
- poj 3225 Help with Intervals(线段树区间更新)
- 噩梦5Tilt Shift (Lens Blur) 倾斜移位(镜头模糊)
- 第一章:实验 访问命令行
- c++ primer 学习笔记-第一章
- go-并发与并行
- 【LeetCode】198:House Robber
- POJ 1265 Area(Pick定理)
- win7下cmd问题