【齐天的博客】转载请注明出处(万分感谢!):
https://blog.csdn.net/qijinglai/article/details/80685226

关联文章:
Android多线程(Handler篇)
Android多线程(AsyncTask篇)
Android多线程(HandlerThread篇)
Android多线程(IntentService篇)

先放流程图:

由于Android中的耗时操作不能放入主线程中,所以实现多线程是必须的。今天的主角是Handler,本文将从使用及源码来分析探索其奥秘。

使用

步骤

  1. 创建Handler对象,实现handlMessage()方法
  2. 创建Runnable线程
  3. 此时产生一个Looper,并自动创建一个消息队列MessageQueue()
  4. Looper轮询MessageQueue交给Handler
  5. Handler做处理

其中1、2、5为使用步骤,其他在后面分析源码时会讲到
使用方法

public class MainActivity extends AppCompatActivity {@SuppressLint("HandlerLeak")Handler handler = new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what){case 1:
//                    处理事件break;}}};@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);new Thread(new Runnable() {@Overridepublic void run() {
//                耗时操作Message message = new Message();message.what = 1;handler.sendMessage(message);}}).start();}
}

使用就写这么多,接下来重点分析Handler原理。

原理分析

首先列一下将会讲到的几个对象

  1. ThreadLocal
  2. Looper
  3. MessageQueue
  4. Handler
  5. Message

ThreadLocal

关于他我看了几篇博客,很大一部分写的是错的,所以
注意

  • 他并不是解决共享对象的多线程访问问题的!!!
  • 他并没有创建对象的拷贝或副本!!!

目的:他只是为了保证每个线程都拥有同一个类的不同对象
实质:每个线程里都new了同一个类的对象
作用:你或许觉得这样做很傻,但是如果使用全局变量就要考虑线程安全问题,而线程安全是以消耗性能为前提的,所以这种设计可以说很巧妙
场景:每个线程都需要相同类型的对象,又各自独立,并且与他相关的操作不少时,如,Looper,ActivityThread,AMS 等

private static final ThreadLocal tSession = new ThreadLocal();
public static Session getSession() throws Exception{Session s = tSession.get();try{if(s==null){s = getSessionFactory.openSession();tSession.set(s);}}catch(Exception e){}return s;
}

我们看一下ThreadLocal的源码,主要分析get()和set()

 public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}return setInitialValue();}public void set(T value) {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)map.set(this, value);elsecreateMap(t, value);}ThreadLocalMap getMap(Thread t) {return t.threadLocals;}

set、get操作的都是ThreadLocalMap,key=当前线程,value=线程局部变量缓存值。可以看到get()实际上都是调用getMap(传入当前线程)来取得当前线程的ThreadLocalMap对象

  • set(),实际上是调用了ThreadLocalMap的set(),ThreadLocalMap的set()涉及到哈希散列算法,我会在之后博客里详细分析这里先不提算法的事。
  • get(),从当前线程中获取ThreadLocalMap,查询当前ThreadLocal变量实例对应的Entry,如果不为null,获取value,如果map为null,走初始化方法

由此看出不是如很多博主写的各线程用了同一个对象又相互独立那么神奇,只不过是用线程当做键在其中维护了一个私有变量而已。得到ThreadLocalMap后如何得到维护的变量呢,在这一句

ThreadLocalMap.Entry e = map.getEntry(this);
//this指代的ThreadLocal对象

所以过程就很清晰了,我们来总结一下:

  1. 声明一个全局公用的ThreadLocal实例作为key
  2. 在线程中new一个或取出已经存了的对象作为value
  3. 将此key-value放入ThreadLocalMap中
  4. 将当前线程作为key,ThreadLocalMap作为值放入ThreadLocal中

比较绕多读几遍就能明白了,再放一张图加深理解

Looper

两件事

  1. 创建消息队列
  2. 在队列中循环取消息

两个方法

  1. prepare()
  2. loop()

两个作用

  1. 保证当前线程只有一个looper和一个MessageQueue
  2. 从MessageQueue中取消息交给target的dispatchMessage

下面我们分析源码

#####构造方法

private Looper(boolean quitAllowed) {mQueue = new MessageQueue(quitAllowed);mThread = Thread.currentThread();}

很容易看出当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));}

通过前面ThreadLocal的分析可知sThreadLocal.get()得到了Looper对象,当Looper存在是报错,不存在是创建一个存入ThreadLocal中,保证线程里有且只有一个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 blockif (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 loggerfinal Printer logging = me.mLogging;if (logging != null) {logging.println(">>>>> Dispatching to " + msg.target + " " +msg.callback + ": " + msg.what);}final long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;final long traceTag = me.mTraceTag;if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {Trace.traceBegin(traceTag, msg.target.getTraceName(msg));}final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();final long end;try {msg.target.dispatchMessage(msg);end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();} finally {if (traceTag != 0) {Trace.traceEnd(traceTag);}}if (slowDispatchThresholdMs > 0) {final long time = end - start;if (time > slowDispatchThresholdMs) {Slog.w(TAG, "Dispatch took " + time + "ms on "+ Thread.currentThread().getName() + ", h=" +msg.target + " cb=" + msg.callback + " msg=" + msg.what);}}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();}}

1)拿到ThreadLocal中的Looper,若没有则prepare()
2)拿到Looper的MessageQueue
3)进入死循环,调用msg.target.dispatchMessage(msg),发给Handler
4)释放资源

MessageQueue

一个单链表结构的消息队列

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获取当前线程保存的Looper实例,再获取这个Looper的MessageQueue

#####发送消息

public final boolean sendMessage(Message msg){return sendMessageDelayed(msg, 0);}/*** Sends a Message containing only the what value.*  * @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 sendEmptyMessage(int what){return sendEmptyMessageDelayed(what, 0);}/*** 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);}

发送消息时所有方法都实际调用了sendMessageAtTime

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);}

是获取MessageQueue调用enqueueMessage();
#####接下来看enqueueMessage中

private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {msg.target = this;if (mAsynchronous) {msg.setAsynchronous(true);}return queue.enqueueMessage(msg, uptimeMillis);}

将msg的target属性赋值为Handler自己,实现了Message与Handler的绑定,并调用了MessageQueue中的enqueueMessage()方法

boolean enqueueMessage(Message msg, long when) {if (msg.target == null) {throw new IllegalArgumentException("Message must have a target.");}if (msg.isInUse()) {throw new IllegalStateException(msg + " This message is already in use.");}synchronized (this) {if (mQuitting) {IllegalStateException e = new IllegalStateException(msg.target + " sending message to a Handler on a dead thread");Log.w(TAG, e.getMessage(), e);msg.recycle();return false;}msg.markInUse();msg.when = when;Message p = mMessages;boolean needWake;if (p == null || when == 0 || when < p.when) {// New head, wake up the event queue if blocked.msg.next = p;mMessages = msg;needWake = mBlocked;} else {// Inserted within the middle of the queue.  Usually we don't have to wake// up the event queue unless there is a barrier at the head of the queue// and the message is the earliest asynchronous message in the queue.needWake = mBlocked && p.target == null && msg.isAsynchronous();Message prev;for (;;) {prev = p;p = p.next;if (p == null || when < p.when) {break;}if (needWake && p.isAsynchronous()) {needWake = false;}}msg.next = p; // invariant: p == prev.nextprev.next = msg;}// We can assume mPtr != 0 because mQuitting is false.if (needWake) {nativeWake(mPtr);}}return true;}

对前一个方法进行补充,把msg放入MessageQueue中,这时候轮询取消息(在前面Looper已经分析),调用dispatchMessage()

public void dispatchMessage(Message msg) {if (msg.callback != null) {handleCallback(msg);} else {if (mCallback != null) {if (mCallback.handleMessage(msg)) {return;}}handleMessage(msg);}}

最终调用handlerMessage()方法

/*** Subclasses must implement this to receive messages.*/public void handleMessage(Message msg) {}

一个空方法,在这里处理操作,end

##总结

  1. Looper.prepare()在本线程中存入ThreadLocalMap中一个Looper并创建一个MessageQueue对象
  2. Looper.loop()循环取Message中的消息,回调msg.target.dispatchMessage(msg)
  3. Handler构造得到Looper并与MessageQueue关联
  4. Handler.sendMessage会给msg.target赋值为自己并加入MessageQueue中
  5. 重写handlMessage()处理

Android多线程(Handler篇)相关推荐

  1. 真香定律!一文带你搞懂Android多线程Handler,成功入职腾讯

    Google 为了帮助 Android 开发者更快更好地开发 App,推出了一系列组件,这些组件被打包成了一个整体,称作 Android Jetpack,它包含的组件如下图所示: 老的 support ...

  2. Android多线程----异步消息处理机制之Handler

    虽然是国庆佳节,但也不能停止学习的脚步,我选择在教研室为祖国母亲默默地庆生. 关于Android的多线程知识,请参考本人之前的一篇博客:Android 多线程----AsyncTask异步任务详解 在 ...

  3. Android多线程源码学习笔记一:handler、looper、message、messageQueue

    最近在学习Android多线程相关知识的源码,现在把自己的笔记整理一下,写出来加深印象. Android多线程通讯的核心是handler.looper.message.messageQueue,这篇文 ...

  4. Android多线程分析之三:Handler,Looper的实现

    Android多线程分析之三:Handler,Looper的实现 罗朝辉 (http://blog.csdn.net/kesalin) CC 许可,转载请注明出处 在前文<Android多线程分 ...

  5. Android多线程UI更新-Handler

    Handler 处理程序允许您发送和处理Message与线程关联的可运行对象MessageQueue.每个 Handler 实例都与单个线程和该线程的消息队列相关联.当你创建一个新的 Handler ...

  6. Androidの多线程之更新ui(Thread+Handler+Message)

    流程: 1.点击开始,显示进度对话框,启动线程,循环发送消息(1-100) 2.更新对话框中的进度,和标题栏的进度 3.到达最大进度值时,关闭对话框,标题栏显示更新完成 解析: 1.在主线程处理han ...

  7. Android中Handler的使用

    在Android开发中,我们常常会遇到这样一种情况:在UI界面上进行某项操作后要运行一段非常耗时的代码,比方我们在界面上点击了一个"下载"button,那么我们须要运行网络请求,这 ...

  8. android多线程读取网页内容

    android必须使用子线程才能够做耗时操作,这点虽然比较符合优秀应用的特点,但是多线程真是让人有点头疼,不管怎么样,那几个runable,handler什么的,我真心记不住它里面有些什么,所以我写篇 ...

  9. 为什么Android项目mainactivity中有一个变量R_博客笔记大汇总,Android优化总结篇

    博客笔记大汇总[16年3月到至今],包括Java基础及深入知识点,Android技术博客,Python学习笔记等等,还包括平时开发中遇到的bug汇总,当然也在工作之余收集了大量的面试题,长期更新维护并 ...

最新文章

  1. 一步一步SharePoint 2007之八:允许所有域用户访问网站
  2. 利用Word将连着一起的字符按照自己指定的”字符串或者字换行“自动换行。
  3. java.util.Properties类,保存时保留注释及格式不变
  4. 苹果的 Metal 工程
  5. 解决layui数据表格table固定列行高不一致的情况
  6. python如何控制浏览器_控制使用Python浏览器?
  7. 工业级以太网交换机具有哪些优越特性
  8. 导出合并小文件_关于微信语音导出,这个方法强烈建议~
  9. 国家邮政局:9月份全国快递服务企业业务收入完成921.4亿元,同比增长11.8%
  10. Fatal Error LNK1123:转换到COFF期间失败:文件无效或损坏
  11. 差分约束系统 与 最短路
  12. [Mac OS] Mac OS X for x86 下载
  13. 免费手机电脑同屏神器——Mirroid
  14. Linux转发性能评估与优化-转发瓶颈分析与解决方案(补遗)
  15. 路由器关闭DHCP之后连接不到路由器设置界面?
  16. 软件质量管理-6-质量管理
  17. 康耐视VisionPro基础教程-GigE方式 连接相机
  18. C语言基础课 编写程序之1.编写一个判断素数的函数,并利用该函数输出100~200的所有素数2.编写一个函数fun(),函数功能是:判断一个整数是否既是5又是7的整倍数,若是,输出yes,不是,输出n
  19. web端网页变为灰色
  20. 麦克风阵列声音定位简介【转】

热门文章

  1. PointGrey相机的安装配置使用
  2. Spring AOP的作用,动态代理模式
  3. MATLAB中怎样初始化(创建)二维、三维、四维以及多维矩阵,各维度的索引顺序是怎样的?
  4. Cytoscape作图,边和节点的颜色设置
  5. [RK3568 Android12] GT911触摸屏调试
  6. 兰卡斯特的计算机科学与技术,兰卡斯特大学计算机专业好吗
  7. 店宝宝:淘宝直播到底应该怎么做?
  8. C++是如何从代码到游戏的?
  9. php 长微博程序,一个简单的长微博生成器
  10. stm32定时器与定时器中断