AndroidHandler源码级分析及实现
来源:互联网 发布:高性能mysql epub 编辑:程序博客网 时间:2024/06/07 03:30
AndroidHandler源码级分析及实现
1.android handler 概述
百度百科:Handler主要用于异步消息的处理:当发出一个消息之后,首先进入一个消息队列,发送消息的函数即刻返回,而另外一个部分在消息队列中逐一将消息取出,然后对消息进行处理,也就是发送消息和接收消息不是同步的处理。 这种机制通常用来处理相对耗时比较长的操作。
handler,looper,message三者都是与android异步消息处理机制相关的概念。当调用handler.sendMessage()时,会调用queue.enqueueMessage()消息入队。然后出队时Looper消息轮循器会调用looper.loop(),然后MessageQueue.next(),再然后在回调message.dispatherMessage(),最后再调handlerMessage(),就完成了出队。
2.源码详细解析
先上一张图,有助于大家理解,下面我会刨析这张图:
第一步:handler.sendMessage()发送消息
不管你是调用哪个发送消息的方法,最终都会调用下面的方法,看到了return语句没有,是不是调用了queue.enqueueMessage(),这样就完成了消息入队,至于queue是什么,其实queue就是MessageQueue,可以理解为消息的仓库,简称消息队列。
private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) { msg.target = this; if (mAsynchronous) { msg.setAsynchronous(true); } return queue.enqueueMessage(msg, uptimeMillis); }
第二步:Lopper轮询器会轮询消息
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)); }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; // 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 final Printer logging = me.mLogging; if (logging != null) { logging.println(">>>>> Dispatching to " + msg.target + " " + msg.callback + ": " + msg.what); } final long traceTag = me.mTraceTag; if (traceTag != 0 && Trace.isTagEnabled(traceTag)) { Trace.traceBegin(traceTag, msg.target.getTraceName(msg)); } try { msg.target.dispatchMessage(msg);//消息转发再由handlerMessage()处理 } finally { if (traceTag != 0) { Trace.traceEnd(traceTag); } } 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(); } }loop->MessageQueue.next()->message.dispatherMessage()
public void dispatchMessage(Message msg) { if (msg.callback != null) { handleCallback(msg); } else { if (mCallback != null) { if (mCallback.handleMessage(msg)) { return; } } handleMessage(msg); } }可以看出message.dispatherMessage()->handlerMessage()
handler大致结构:
Handler类有:Looper looper;MessageQueue mQueue;
Looper类有:MessageQueue mQueue;prepare()->sThreadLocal.set();loop()->myLooper()->sThreadLocal.get()
MessageQueue类有:enqueueMessage()消息入队/生产者;next()消息出队/消费者
Message类有:Handler target;int what;Object obj;
下面将根据这个类结构去自定义handler
3.自定义handler
Handler:
public class Handler { private Looper mLooper; private MessageQueue mQueue; public Handler(){ mLooper=Looper.myLooper(); mQueue=mLooper.mQueue; } /** * 发送消息 * @param msg */ public void sendMessage(Message msg){ msg.target=this; mQueue.enqueueMessage(msg); } /** * 转发消息 * @param msg */ public void dispatherMessage(Message msg){ handlerMessage(msg); } /** * 处理消息 * @param msg */ public void handlerMessage(Message msg){ }}
Looper轮询器
/** * 一个线程只有一个lopper对象,prepare不为空抛异常 */public class Looper { //ThreadLocal 防止脏读 private static ThreadLocal<Looper>mThreadLocal=new ThreadLocal<Looper>(); public MessageQueue mQueue; public Looper(){ this.mQueue=new MessageQueue(); } /** * * @return 返回当前线程绑定的lopper对象 */ public static Looper myLooper(){ return mThreadLocal.get(); } public static void prepare(){ Looper lopper=myLooper(); if(lopper!=null){ throw new RuntimeException("only one looper may be created per thread"); } mThreadLocal.set(new Looper()); } /** * 轮询器轮询消息队列 */ public static void loop(){ Looper me=myLooper(); if(me==null){ throw new RuntimeException("No Looper:Looper.prepare()wasn't called on this thread."); } MessageQueue mQueue=me.mQueue; for(;;){ Message msg=mQueue.next(); if(msg==null){ continue; } //转发 msg.target.dispatherMessage(msg); } }}
消息队列MessageQueue,对于这个类我多做一点解释,防止不了解线程间通信的读者看不懂。
Object类有三个方法值得注意:wait(),notify(),notifyAll().这三个方法必须有同步监视器对象来调用
wait():导致当前线程等待,直到其它线程调用该同步监视器的notify()或者notifyAll()来唤醒该线程。
notify():唤醒在此同步监视器等待的单个线程,选择是任意性的。
notifyAll():唤醒在此同步监视器等待的所有线程。
传统的线程通信使用Synchronized修饰的同步方法,因为该类的默认实例(this)就是同步监视器,所以可以在同步方法中调用这三个方法。
传统的线程通信使用Synchronized修饰的同步代码块,同步监视器是Synchronized后括号里的对象,所以必须使用该对象调用这三个方法。
这是传统的使用Synchronized关键字来保证同步,如果不喜欢用这个,可以试试Lock对象,本程序用的就是这个对象。如果用的是Lock对象,程序中就不存在隐式的同步监视器,也就不能使用wait(),notify(),notifyAll()。所以java就提供了Conditionl类来保持协调。提供的三个方法await(),signal(),signalAll()与上面三个方法类似,不多累赘。
/** * //不加锁,供不应求 ,供大于求 * Created by Administrator on 2017/5/29. */public class MessageQueue { Message[]items;//消息数组 int putIndex;//入队索引 int takeIndex;//出队索引 int count=0;//消息个数 Lock lock;//添加锁 阻塞队列 java高并发编程 Condition notEmpty;//消费者 Condition notFull;//生产者 public MessageQueue(){ this.items=new Message[50]; this.lock=new ReentrantLock(); this.notEmpty=lock.newCondition(); this.notFull=lock.newCondition(); } /** * 消息入队(生产者) sendMessage()里面调用是子线程 * @param msg */ public void enqueueMessage(Message msg){ try { lock.lock();//加锁 if (count == items.length) { //阻塞 已经满了,不能再生产了 notFull.await(); } items[putIndex] = msg; putIndex = (++putIndex == items.length) ? 0 : putIndex; //队列不为空,通知消费者线程可以消费 notEmpty.signalAll();//防止多个子线程 没有唤醒 }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); } count++; } /** * 消息出队(消费者) looper调用 ,是主线程 * @r */ public Message next(){ Message msg=null; try { lock.lock(); //没有产品了,不能消费 while(count==0){ notEmpty.await(); } msg = items[takeIndex]; items[takeIndex] = null;//出队后置空 takeIndex = (++takeIndex == items.length) ? 0 : takeIndex; count--; notFull.signalAll();//防止多个子线程 没有唤醒 }catch (InterruptedException e){ e.printStackTrace(); }finally { lock.unlock(); } return msg; }}Message
public class Message { Handler target;//默认修饰符 public int what; public Object obj; @Override public String toString() { return obj.toString(); }}TestHandler测试类(消息机制不光光是更新ui,更准确的说是通信,所以下面的测试代码我故意放在了子线程。for循环开启10个子线程测试我写的handler是否是线程安全的,测试结果显示是线程安全的,因为消息队列加锁了)
public class TestHandler { public static void main(String[]args){ //消息机制不光光是更新ui的,最准确地来说是通信的,因为我可以放在ui线程执行也可以放在子线程 new Thread(){ @Override public void run() { Looper.prepare(); final Handler handler=new Handler(){ @Override public void handlerMessage(Message msg) { System.out.println(Thread.currentThread().getName() + " receiver:"+msg.toString()); } }; for(int i=0;i<10;i++) {//多线程跑测试自己的handler是不是线程安全的,结果发现是的 new Thread() { @Override public void run() { while (true) { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Message msg = new Message(); msg.what = 1; //多线程一起跑randomUUID要加个锁 synchronized (UUID.class){ msg.obj = UUID.randomUUID().toString(); } System.out.println(Thread.currentThread().getName() + " send:" + msg.toString() + " "); handler.sendMessage(msg); } } }.start(); } Looper.loop(); } }.start(); }}测试结果贴图:
Thread-0就是接受的子线程,就是消费者
Thread-1到Thread-10就是10个子线程,不停地发消息,10个生产者
从上面你可以看出1-10生产者生产出来的msg在0这个消费者总是能接受到消息
测试通过,万事大吉!
4.总结
学习是一个漫长的过程,彼此共勉!加油!!!
ps:上述如有不对的地方,欢迎指出,一起交流!
- AndroidHandler源码级分析及实现
- Android Handler 四个使用实例 及HandlerThread的使用,androidhandler实例,HandlerThread 源码分析
- HashMap实现原理及源码分析
- ShareSDK源码分析及部分代码实现
- google PLDA + 实现原理及源码分析
- Java数据结构----队列实现及源码分析
- disruptor实现细节及源码分析
- HashMap内部实现及源码分析
- HashMap实现原理及源码分析
- HashMap实现原理及源码分析
- HashMap实现原理及源码分析
- HashMap实现原理及源码分析
- ConcurrentHashMap实现原理及源码分析
- HashMap实现原理及源码分析
- HashMap实现原理及源码分析
- HashMap实现原理及源码分析
- HashMap实现原理及源码分析
- HashMap实现原理及源码分析
- 函数参数的传递问题(指针的指针)
- 解决ubuntu下vi上下左右方向键出现字母,backspace键不能删除字符
- leetcode 第三题 小白用最慢的算法做的
- [BFS] [Luogu P1299] 切孔机
- 微信公众号 全套免费 mx7f 教程视频 百度网盘
- AndroidHandler源码级分析及实现
- 聊聊安卓开发中主题样式的一种组织架构
- 每天学一点Swift----面向对象上(七)
- GDAL坐标转换
- 一个基于装饰者设计模式的上报框架
- Android XML 文件中引用资源的方法
- 使用 dbutils 进行批处理
- centos7 下安装canal,并实现将mysql数据同步到redis
- [bzoj3676]回文串