android源码解析(3)--handler消息机制
来源:互联网 发布:微信直接打开淘宝链接 编辑:程序博客网 时间:2024/04/29 07:35
最近正在找工作,正好也有时候整理整理知识点。今天整理一下Android的消息机制,这个也是面试当中必问的知识点了。
1.先看看我们是如何使用的
public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mLooperThread = new LooperThread("回家"); mLooperThread.start(); } /** * 1.最常规用法,使用在主线程中直接new一个,然后发送消息 */ private Handler mMainHandler = new Handler() { @Override public void handleMessage(Message msg) { Log.e("TAG", "handleMessage: " + Thread.currentThread().getName()); } }; /** * 2.在分线程中使用方法 */ private LooperThread mLooperThread; class LooperThread extends Thread { public Handler mHandler; public LooperThread(String name) { super(name); } public void run() { //第一步 Looper.prepare(); //第二步 mHandler = new Handler() { public void handleMessage(Message msg) { Log.e("TAG", "handleMessage: " + Thread.currentThread().getName()); } }; //第三步 Looper.loop(); } } @Override protected void onResume() { super.onResume(); mLooperThread.mHandler.sendEmptyMessageDelayed(1, 1000); mMainHandler.sendEmptyMessageDelayed(1, 1000); } }2.具体怎么使用不用多说,然后我们来分析一下原理,还是从分线程中的使用开始分析,因为分线程可以说是最中规中矩的用法,主线程待会再说
1)第一步Looper.prepare();方法,那么他到底做了什么呢?看源码
final MessageQueue mQueue; final Thread mThread; private Printer mLogging; /** Initialize the current thread as a looper. * This gives you a chance to create handlers that then reference * this looper, before actually starting the loop. Be sure to call * {@link #loop()} after calling this method, and end it by calling * {@link #quit()}. */ 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)); }我们可以看到,这个方法有三个功能,第一就是new Looper,第二就是将这个looper和当前的thread绑定,我认为和我们平时给view设置tag一样,目的就是为了便于查找绑定,同时也说明Looper所在的线程才是我们需要处理结果的线程,这个待会验证。第三个功能就是创建之前的判断,如果当前线程中已经绑定过looper了,那就会抛异常。然后看看new looper做了什么呢:
private Looper(boolean quitAllowed) { mQueue = new MessageQueue(quitAllowed); mThread = Thread.currentThread(); }好像也很简单,就是创建了一个消息队列,但是注意这个消息队列可是Looper的一个属性,也就是说完成了Looper和消息队列的绑定。
2)第二步:创建handler,无参构造器最终会调用下面构造器
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(); 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; }这里看Looper.myLooper()方法:
/** * Return the Looper object associated with the current thread. Returns * null if the calling thread is not associated with a Looper. */ public static @Nullable Looper myLooper() { return sThreadLocal.get(); }是不是就是返回当前线程绑定的Looper,也就是说handler创建所在线程绑定的looper(当然我们也可以指定其他线程looper,下面再说),这样就把handler、looper、messagequeue三者绑定到一起了。
3)Looper.Loop();方法
/** * Run the message queue in this thread. Be sure to call * {@link #quit()} to end the loop. */ 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; // 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); } 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.recycleUnchecked(); } }一堆代码其实就几行我们需要,首先开启了死循环去消息队列中不停的取消息,注意这里queue.next()方法是可以阻塞的,也就是说如果有消息来了他就会自动唤醒,没有消息了,就是自动释放cpu资源直到有消息来了再次唤醒,这个就要研究linux了(本人不懂)。继续,如果取到消息了,就会进入msg.target进行处理,其实就是handler自己,待会看;但是如果msg为null的时候呢,就会跳出循环,结束掉,什么情况会执行呢,就是looper.quit()执行后,就会结束掉,重新开启应该再次调用此方法。好了,总之这里就是有了消息就交给handler处理。
现在是消息队列也有了,looper也开始工作不停的取消息了,handler也等待着处理消息了,那么就差我们发消息了。
mMainHandler.sendEmptyMessageDelayed(1, 1000);消息来了,那么是怎么发的呢?我们可以看看源码:
/** * Sends a Message containing only the what value, to be delivered * after the specified amount of time elapses. * @see #sendMessageDelayed(android.os.Message, long) * * @return Returns true if the message was successfully placed in to the * message queue. Returns false on failure, usually because the * looper processing the message queue is exiting. */ 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) { 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); }这里取到了Looper中的消息队列了,然后开始向队列中塞入数据:
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
关键点来了,第一行代码给mag设置了一个target,就是当前的handler,还记得刚才取出来消息处理是不是调用的msg.target.dispatchMessage()方法,不就是又交给handler自己处理了吗!这里再说一下消息队列里的消息其实是根据延迟时间来进行排序,并不是真正的队列先进后出原则。好了整个过程就分析完了,现在我们就可以在任意一个分线程中发送消息,然后会回到Looper所在的线程中处理结果,这样就实现了跨线程的通信。
忘了把handler分发事件处理的代码贴出来了:
/** * 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); } }这里我们可以看到,其实handler里面我们重写的那个方法级别最低了。
4)为什么说Looper所在的线程才是决定处理消息的线程呢,我么可以验证一下:
class LooperThread extends Thread { public Handler mHandler; public LooperThread(String name) { super(name); } public void run() { //第一步 Looper.prepare(); //第二步 mHandler = new Handler(getMainLooper()) { public void handleMessage(Message msg) { Log.e("TAG", "handleMessage: " + Thread.currentThread().getName()); } }; //第三步 Looper.loop(); } }这里只变化了一点,那就是创建handler的时候我们将主线程的looper穿进去了,那么handler再处理消息就也跑到主线程去了,而如果向我们最开始一样使用空参构造器则会使用当前线程的looper,所以说looper所在的线程才是真正最后结果处理完所在的线程。至于为什么,因为looper才是真正和线程关联在一起的,而handler是和looper关联在一起的,handler是通过looper间接和线程关联的。
2.使用中、面试中的问题:
1)既然looper里面是个死循环,为什么不会阻塞主线程呢?这个问题上面已经说过了。
2)如果最开始上面的使用方法,AS会提示内存泄漏的,确实也会存在,解决方法有如下两种:
第一种:
@Override protected void onDestroy() { mMainHandler.removeCallbacksAndMessages(null); super.onDestroy(); }这种方法一般情况下没有问题,但是如果activity销毁的时候不调用此方法该怎么办呢?(极个别手机在极端情况下确实存在不走这个方法),如果忽略可以这么做。
第二种方法:
private static class MyHandler extends Handler { private final WeakReference<HandlerActivity2> mActivity; public MyHandler(HandlerActivity2 activity) { mActivity = new WeakReference<HandlerActivity2>(activity); } @Override public void handleMessage(Message msg) { if (mActivity.get() == null) { return; } mActivity.get().todo(); } }网上抄的一段代码,不过确实是这么解决使用弱引用来解决,但是问题是弱引用容易被回收啊,那就会导致我本来应该处理一段逻辑,但是由于内存紧张弱引用被回收了,逻辑也处理不了了,那悲剧了!所以这种也不是百分之百好。
- android源码解析(3)--handler消息机制
- Android消息机制Handler源码简单解析
- Android 源码解析Handler消息传递机制
- Android消息机制源码解析(Handler)
- Handler消息机制源码解析
- Handler消息机制源码解析
- handler消息机制源码解析
- Handler消息机制 -- 源码解析
- Handler消息机制源码解析
- 【Android源码系列】消息机制:Handler源码解析
- android开发-android的消息机制(Handler)源码解析
- Android消息机制Handler解析(源码+Demo)
- Android消息机制Handler,MessageQueue,Looper源码解析
- Android Handler消息机制源码解析(上)
- Android Handler消息机制源码解析(下)
- 从源码来一步一步解析Android中Handler消息机制
- Android Handler机制 源码解析
- Android Handler机制源码解析
- Unity和虚幻的比较
- 工作中经常用的表单验证,全部好用,验证好多次!
- (第4篇)hadoop之魂--mapreduce计算框架,让收集的数据产生价值
- html5实现摄像头拍照并使用java进行照片保存
- java.io.IOException: offset < 0相关问题研究
- android源码解析(3)--handler消息机制
- 浅谈互联网金融安全
- bzoj1070: [SCOI2007]修车
- powerdesigner 概念模型(conceptualDataModel)创建详解
- eclipse下搭建JBPM6运行环境和测试
- Unity导入FBX自动进行动画切分
- STM32 nvic 解释
- openssl——aes加密
- 矩阵论笔记(七)——矩阵的微分和积分