加入收藏 | 设为首页 | 会员中心 | 我要投稿 东莞站长网 (https://www.0769zz.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 站长资讯 > 评论 > 正文

Android消息机制Handler,有必要再讲一次

发布时间:2019-07-28 07:42:38 所属栏目:评论 来源:Engineers
导读:副标题#e# 我们在日常开发中,总是不可避免的会用到 Handler,虽说 Handler 机制并不等同于 Android 的消息机制,但 Handler 的消息机制在 Android 开发中早已谙熟于心,非常重要! 通过本文,你可以非常容易得到一下问题的答案: Handler、Looper、Message

再来看看 myLooper() 方法:

  1. public static @Nullable Looper myLooper() { 
  2.  return sThreadLocal.get(); 

看看 sThreadLocal 是什么:

  1. static final ThreadLocal sThreadLocal = new ThreadLocal(); 

这个 ThreadLocal 是什么呢?

ThreadLocal

关于 ThreadLocal,我们直接采取 严振杰文章 中的内容。

看到 ThreadLocal 的第一感觉就是该类和线程有关,确实如此,但是要注意它不是线程,否则它就该叫 LocalThread 了。

ThreadLocal 是用来存储指定线程的数据的,当某些数据的作用域是该指定线程并且该数据需要贯穿该线程的所有执行过程时就可以使用 ThreadnLocal 存储数据,当某线程使用 ThreadnLocal 存储数据后,只有该线程可以读取到存储的数据,除此线程之外的其他线程是没办法读取到该数据的。

一些读者看完上面这段话应该还是不理解 ThreadLocal 的作用,我们举个栗子:

  1. ThreadLocal<Boolean> local = new ThreadLocal<>(); 
  2. // 设置初始值为true. 
  3. local.set(true); 
  4. Boolean bool = local.get(); 
  5. Logger.i("MainThread读取的值为:" + bool); 
  6. new Thread() { 
  7.  @Override 
  8.  public void run() { 
  9.  Boolean bool = local.get(); 
  10.  Logger.i("SubThread读取的值为:" + bool); 
  11.  // 设置值为false. 
  12.  local.set(false); 
  13.  } 
  14. }.start(): 
  15. // 主线程睡1秒,确保上方子线程执行完毕再执行下面的代码。 
  16. Thread.sleep(1000); 
  17. Boolean newBool = local.get(); 
  18. Logger.i("MainThread读取的新值为:" + newBool); 

代码没什么好说的吧,打印出来的日志,你会看到是这样的:

  • MainThread读取的值为:trueSubThread读取的值为:nullMainThread读取的值为:true

第一条 Log 无可置疑,因为设置了值为 true,因为打印结果没什么好说的。对于第二条 Log,根据上方介绍,某线程使用 ThreadLocal 存储的数据,只能被该线程读取,因此第二条 Log 的结果是:null。紧接着在子线程中设置了 ThreadLocal 的值为 false,然后第三条 Log 将被打印,原理同上,子线程中设置了 ThreadLocal 的值并不影响主线程的数据,所以打印是 true。

实验结果证实:就算是同一个 ThreadLocal 对象,任一线程对其的 set() 和 get() 方法的操作都是相互独立互不影响的。

Looper.myLooper()

我们回到 Looper.myLooper():

  1. static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); 

我们看看是在哪儿对 sThreadLocal 操作的。

  1. public static void prepare() { 
  2.  prepare(true); 
  3. private static void prepare(boolean quitAllowed) { 
  4.  if (sThreadLocal.get() != null) { 
  5.  throw new RuntimeException("Only one Looper may be created per thread"); 
  6.  } 
  7.  sThreadLocal.set(new Looper(quitAllowed)); 

所以知道了吧,这就是在子线程中使用 Handler 前,必须要调用 Looper.prepare() 的原因。

可能你会疑问,我在主线程使用的时候,没有要求 Looper.prepare() 呀。

原来,我们在 ActivityThread 中,有去显示调用 Looper.prepareMainLooper():

  1. public static void main(String[] args) { 
  2.  // ... 
  3.  Looper.prepareMainLooper(); 
  4.  // ... 
  5.  if (sMainThreadHandler == null) { 
  6.  sMainThreadHandler = thread.getHandler(); 
  7.  } 
  8.  //... 
  9.  Looper.loop(); 
  10.  // ... 
  11.  } 

我们看看 Looper.prepareMainLooper():

  1. public static void prepareMainLooper() { 
  2.  prepare(false); 
  3.  synchronized (Looper.class) { 
  4.  if (sMainLooper != null) { 
  5.  throw new IllegalStateException("The main Looper has already been prepared."); 
  6.  } 
  7.  sMainLooper = myLooper(); 
  8.  } 

(编辑:东莞站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

热点阅读