源码分析looper,messagequeue及handler的创建调用过程
2015-08-13 23:14
176 查看
首先我从ui线程的调用过程中开始分析。
(1) 在ui线程中系统会初始化一个looper对象,源码中在activityThread中的main方法中调用了Looper.prepareMainLooper()方法对looper进行初始化,并且调用loop方法开始执行无限循环的中messagequeue中获取msg并利用handler发送(这个源码下面会说)。
(2)在调用prepareMainLooper的时候会调用prepare方法:
sMainLooper的声明为private static Looper sMainLooper; 即当前线程的looper对象,从static 和下面贴出的looper的私有构造函数可以知道looper是单例的。
sThreadLocal声明为static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();可以理解为用来保存looper的对象。
mylooper()是调用的sThreadLocal.get(),来获取保存的looper对象
而在looper的构造函数之中,我们会创建一个MessageQueue对象,因为上面的代码保证了sThreadLocal即looper的唯一性,所以MessageQueue也是唯一的。
那么我们现在得到了用来管理message的messagequeue,和用来取出message的looper对象。
(3)调用loop方法,利用死循环来不断获取messagequeue中的message
(4)上面的可以简单的理解为,ui线程会自己调用looper.prepare方法来初始化looper及messagequeue,然后调用loop方法,通过无限循环来利用looper对象取出保存在messagequeue中的message对象,然后利用handler处理消息。
那么现在看下msg.target.dispatchMessage(msg);是怎么运行的,首先知道msg.target即为一个handler对象,dispatchMessage方法点进去之后会调用handleMessage
handleMessage在源码中就是个空方法,每次我们新建handler对象的时候都会重写此方法,进行操作。这就是完整的消息机制。
而在handler.sendMessage方法中会执行一步一步的执行到enqueueMessage方法,在这个方法中会间msg.target赋值为handler,之后将其中的message保存到messagequeue之中。然后通过loop的无限循环可以使其执行dispatchMessage方法,取出队列中的对象,进行处理。
那么在不是ui线程中想要利用handler消息机制的话,那么就必须自己调用prepare和loop方法了,如下:
总结下: 1.调用prepare()方法创建looper对象及其对应的messagequeue
2.创建handler对象来通过其sendMessage方法发送message到messagequeue中。
3.通过loop方法中的无限循环以先进先出的原则来通过handler对象的handleMessage方法来处理消息
(1) 在ui线程中系统会初始化一个looper对象,源码中在activityThread中的main方法中调用了Looper.prepareMainLooper()方法对looper进行初始化,并且调用loop方法开始执行无限循环的中messagequeue中获取msg并利用handler发送(这个源码下面会说)。
public static void main(String[] args) { SamplingProfilerIntegration.start(); CloseGuard.setEnabled(false); Environment.initForCurrentUser(); EventLogger.setReporter(new EventLoggingReporter()); Security.addProvider(new AndroidKeyStoreProvider()); Process.setArgV0("<pre-initialized>"); Looper.prepareMainLooper(); //这也是为什么ui线程中不需要执行prepare及loop方法的理由了 ActivityThread thread = new ActivityThread(); thread.attach(false); if (sMainThreadHandler == null) { sMainThreadHandler = thread.getHandler(); } AsyncTask.init(); if (false) { Looper.myLooper().setMessageLogging(new LogPrinter(Log.DEBUG, "ActivityThread")); } Looper.loop(); throw new RuntimeException("Main thread loop unexpectedly exited"); }
(2)在调用prepareMainLooper的时候会调用prepare方法:
sMainLooper的声明为private static Looper sMainLooper; 即当前线程的looper对象,从static 和下面贴出的looper的私有构造函数可以知道looper是单例的。
sThreadLocal声明为static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();可以理解为用来保存looper的对象。
mylooper()是调用的sThreadLocal.get(),来获取保存的looper对象
public static void prepareMainLooper() { prepare(false); //创建looper对象及MessageQueue synchronized (Looper.class) { if (sMainLooper != null) { throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper(); //获取looper对象 } }prepare方法中进行了对looper的判断,如果当looper存在的时候继续调用prepare方法时就会抛出Only one Looper may be created per thread的运行异常,这就是为什么一个线程只能拥有一个looper的原因了。
private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { //使其保证一个线程只拥有一个looper throw new RuntimeException("Only one Looper may be created per thread"); } sThreadLocal.set(new Looper(quitAllowed)); }
而在looper的构造函数之中,我们会创建一个MessageQueue对象,因为上面的代码保证了sThreadLocal即looper的唯一性,所以MessageQueue也是唯一的。
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); //保存当前线程,在使用时可以用来获取或者判断是否为当前本地线程 }
那么我们现在得到了用来管理message的messagequeue,和用来取出message的looper对象。
(3)调用loop方法,利用死循环来不断获取messagequeue中的message
public static void loop() { final Looper me = myLooper(); //获取当前线程的looper对象 if (me == null) { throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread."); } final MessageQueue queue = me.mQueue; //获取当前线程的消息队列 Binder.clearCallingIdentity(); final long ident = Binder.clearCallingIdentity(); for (;;) { Message msg = queue.next(); // 获取消息队列的下一个消息,如果没有消息,将会阻塞 if (msg == null) { //如果消息为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为handler对象,下面会讲此方法。 if (logging != null) { logging.println("<<<<< Finished to " + msg.target + " " + msg.callback); } //保证在分发消息的过程中线程标识符不会被修改 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(); //释放资源 } }
(4)上面的可以简单的理解为,ui线程会自己调用looper.prepare方法来初始化looper及messagequeue,然后调用loop方法,通过无限循环来利用looper对象取出保存在messagequeue中的message对象,然后利用handler处理消息。
那么现在看下msg.target.dispatchMessage(msg);是怎么运行的,首先知道msg.target即为一个handler对象,dispatchMessage方法点进去之后会调用handleMessage
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }
handleMessage在源码中就是个空方法,每次我们新建handler对象的时候都会重写此方法,进行操作。这就是完整的消息机制。
而在handler.sendMessage方法中会执行一步一步的执行到enqueueMessage方法,在这个方法中会间msg.target赋值为handler,之后将其中的message保存到messagequeue之中。然后通过loop的无限循环可以使其执行dispatchMessage方法,取出队列中的对象,进行处理。
那么在不是ui线程中想要利用handler消息机制的话,那么就必须自己调用prepare和loop方法了,如下:
public void looper() { new Thread() { @Override public void run() { // TODO Auto-generated method stub super.run(); //非主线程中默认没有创建looper对象 //Toast.makeText(mContext, "不可以打toast", 0).show(); Looper.prepare(); Toast.makeText(mContext, "可以打toast", 0).show(); Looper.loop(); } }.start(); }
总结下: 1.调用prepare()方法创建looper对象及其对应的messagequeue
2.创建handler对象来通过其sendMessage方法发送message到messagequeue中。
3.通过loop方法中的无限循环以先进先出的原则来通过handler对象的handleMessage方法来处理消息
相关文章推荐
- UITableView 属性 方法 协议 大全
- UVA 10911 Forming Quiz Teams
- IBM Bluemix云计算大会见闻
- gradle 配置文件 build.gradle 属性详解
- UI 07 _ 导航视图控制器 与 属性传值
- Innodb/MyISAM在自增/UUID主键下的性能与索引空间比较
- Havok_2014-1-0_Pc_Xs_User_Guide(2.2.2-模拟仿真一个Physics2012世界)
- Havok_2014-1-0_Pc_Xs_User_Guide(1.5.7-计时器)
- UIViewController _loadViewFromNibNamed:bundle:
- -[UIViewController _loadViewFromNibNamed:bundle
- spoj1182 Sorted bit squence
- UIPageControl 与 UIScrollView
- Android中Looper的quit方法和quitSafely方法
- UVa 11955 I Can Guess the Data Structure!
- The Unique MST prim(次小生成树)
- 【转载】UITableView
- iOS开发 -- UILable详解
- iOS开发 -- UIView详解
- KVC(Key-value coding)机制
- UIPageViewController