参考文章:http://blog.csdn.net/woshiwxw765/article/details/38146185?locationNum=10
关键相关的类,Handler,Message,MessageQueue,Looper
- Handler:用来发送消息,处理消息
- Message:消息实体对象,handler通过sendMsg将实体放到消息队列里面 ,感觉是封装消息数据的意思
- MessageQueue:通过链表的方式存放消息的队列
-
Looper:消息轮询器,轮询消息队列的消息然后取出,交给handler处理
-
为什么会出现Handler,因为在子线程中不能更新UI,否则会挂掉,那么只能借助Message对象封装数据,然后通过handler对象发送到主线程,再进行UI更新。即创建一个Message对象,然后借助Handler发送出去,之后在Handler的handleMessage()方法中获得刚才发送的Message对象,然后在这里进行UI操作就不会再出现崩溃了。
-
在子线程中创建的Handler是会导致程序崩溃的,提示的错误信息为 Can't create handler inside thread that has not called Looper.prepare() 。说是不能在没有调用Looper.prepare() 的线程中创建Handler,但在主线程中却不需要先调用Looper.prepare()方法,
-
Handler的无参构造函数如下所示:会检查是否有Looper对象
public Handler() { 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()); } } //这里获得了一个Looper对象,如果对象为空就抛异常 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 = null; }
-
那什么时候Looper对象才可能为空呢?这就要看看Looper.myLooper()中的代码了,如下所示:
public static final Looper myLooper() { return (Looper)sThreadLocal.get(); }
-
这个方法非常简单,就是从sThreadLocal对象中取出Looper。如果sThreadLocal中有Looper存在就返回Looper,如果没有Looper存在自然就返回空了。因此你可以想象得到是在哪里给sThreadLocal设置Looper了吧,当然是Looper.prepare()方法!我们来看下它的源码:
public static final void prepare() { if (sThreadLocal.get() != null) { throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper()); }
-
可以看到,首先判断sThreadLocal中是否已经存在Looper了,如果还没有则创建一个新的Looper设置进去。这样也就完全解释了为什么我们要先调用Looper.prepare()方法,才能创建Handler对象。同时也可以看出每个线程中最多只会有一个Looper对象。
-
主线程中的Handler也没有调用Looper.prepare()方法,为什么就没有崩溃呢?细心的朋友我相信都已经发现了这一点,这是由于在程序启动的时候,系统已经帮我们自动调用了Looper.prepare()方法。查看ActivityThread中的main()方法,代码如下所示:
public static void main(String[] args) { SamplingProfilerIntegration.start(); CloseGuard.setEnabled(false); Environment.initForCurrentUser(); EventLogger.setReporter(new EventLoggingReporter()); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper();//这里调用这个方法,其实是同过Looper的内部成员ThreadLocal类型的变量sThreadLocal,将当前线程的数据存储起来,存起来的就是创建一个Looper对象,ThreadLocal是根据不同线程来存储数据的,同一个线程存储的数据,后面去获取还是原来线程的数据,线程之间只能存取自己的数据。 ActivityThread thread = new ActivityThread(); thread.attach(false);//这里初始化了一个Hndler对象,并且向消息池中发送两个主要的消息,一个是用于初始化Application,一个是用于初始化Activity,当然在这期间还调用了远程服务,通过IPC(Inter process communication进程间通信)调用了ActivityManagerService(AMS)服务里的方法,获取相关的信息,并作为消息对象传递 if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler();//拿到上一步初始化的handler } AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop();//这里调用loop()方法时,就是去获取之前存储在ThreadLocal变量里面的Looper对象,还有消息队列MessageQueue对象,这个queue对象也是在创建Looper对象时创建的,在构造方法里创建了。 throw new RuntimeException("Main thread loop unexpectedly exited"); }
-
可以看到,在第7行调用了Looper.prepareMainLooper()方法,而这个方法又会再去调用Looper.prepare()方法,代码如下所示:
public static final void prepareMainLooper() { prepare(); setMainLooper(myLooper()); if (Process.supportsProcesses()) { myLooper().mQueue.mQuitAllowed = false; } }
-
因此我们应用程序的主线程中会始终存在一个Looper对象,从而不需要再手动去调用Looper.prepare()方法了。
-
这样基本就将Handler的创建过程完全搞明白了,总结一下就是在主线程中可以直接创建Handler对象,而在子线程中需要先调用Looper.prepare()才能创建Handler对象。