首页 源码 正文

最优秀的ANDROID源码-简单的androidapp开发

2024-06-21 09:06:20 38 0
admin

Handler作为跨线程的消息机制最优秀的ANDROID源码,能够帮助最优秀的ANDROID源码我们实现线程间通信。相信大家都很熟悉在实际开发过程中怎么去使用最优秀的ANDROID源码,但是其内部原理却很多人没有看过,为什么Handler能够实现跨线程通信而不会出现多线程并发访问问题。下面就让我们一起探索源码,从源码中找到答案。

我们都知道在非主线程中创建使用Handler创建子线程之前必须调用Looper.prepare()。如果在主线程中使用的话就要先调用prepareMainLooper() *** 。如下图最优秀的ANDROID源码

最优秀的ANDROID源码-简单的androidapp开发

public final class Looper { static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>(); private static Looper sMainLooper; // guarded by Looper.class final MessageQueue mQueue; final Thread mThread; private boolean mInLoop; ·········省略部分代码········· private static void prepare(boolean quitAllowed) { if (sThreadLocal.get() != null) { //通过threadLocal获取Looper,如果创建过了则会报错,所以perpare只能调用一次。 throw new RuntimeException("Only one Looper may be created per thread"); } //将创建好的looper保存在threadLocal中。 sThreadLocal.set(new Looper(quitAllowed)); } @Deprecated public static void prepareMainLooper() {//在主线程中创建Looper prepare(false); synchronized (Looper.class) { if (sMainLooper != null) {//只能保证有唯一一个looper throw new IllegalStateException("The main Looper has already been prepared."); } sMainLooper = myLooper();//调用此 *** 返回Looper } } public static Looper getMainLooper() { synchronized (Looper.class) { return sMainLooper; } } /** *返回与当前线程关联的 Looper 对象。 如果调用线程未与 Looper 关联,则返回 null。 */ public static @Nullable Looper myLooper() { return sThreadLocal.get();//从threadLocal中获取looper }

此时,我们知道了looper是保存在ThreadLocal中,不管是通过Looper.prepare(),还是prepareMainLooper(),都是从Threadlocal中获取。如果有同学对looper不熟悉的话,可以查看我的上一篇文章。接下来我们好好分析一下ThreadLocal是如果保存Looper的,这个即是Handler的难点也是面试必问的知识点。

首先我们看一下ThreadLocal这个类。

最优秀的ANDROID源码-简单的androidapp开发

public class ThreadLocal<T> {public ThreadLocal() private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; } /* *返回此线程局部变量的当前线程副本中的值。 如果该变量对于当前线程没有值, * 则首先将其初始化为调用initialValue *** 返回的值。 * 返回最优秀的ANDROID源码:此线程本地的当前线程的值 */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(); }/** *将此线程局部变量的当前线程副本设置为指定值。 大多数子类将不需要覆盖此 *** ,仅依赖于initialValue *** 来设置线程initialValue的值。 *参数:value -- 要存储在此线程本地的当前线程副本中的值 */public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }/** *删除此线程局部变量的当前线程值。 如果此线程局部变量随后读取当前线程, *它的价值将通过调用其重新初始化initialValue *** ,除非它的值设置在临时当前线程。 *这可能会导致在当前线程中多次调用initialValue *** **/ public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this); }

通过源码可知,ThreadLocal使用了TreadLocalMap进行保存Looper。ThreadLocalMap是一个Map,以键-值对的方式进行存储。我们先分析set *** 。

public void set(T value) { Thread t = Thread.currentThread();//获取当前线程 ThreadLocalMap map = getMap(t);//从当前线程中获取map if (map != null) map.set(this, value);//将当前线程作为key,存储value。 else createMap(t, value); } ThreadLocalMap getMap(Thread t) { return t.threadLocals; }

通过set *** 我们可以看出,ThreadLocalMap是线程中的局部变量。这个就说明每一个线程中都有一个自己的局部变量ThreadLocal。并且将当前线程作为key存储Looper。然后调用prepare() *** ,获取已经保存在当前线程中的Looper。

public T get() { Thread t = Thread.currentThread();//获取当前线程 ThreadLocalMap map = getMap(t);//获取线程中的Map if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result;//通过线程获取value } } return setInitialValue(); }

现在我们知道ThreadLocal用来保存Looper,实际上是保存在每个线程中的ThreadLocalMap中,并且将当前线程作为Key。当我们通过Handler创建子线程的时候,先调用prepare() *** ,来确保ThreadLocal中只有一个Looper。每个线程中都有自己的Looper,这就保证了每个线程的数据都有唯一的一份,即使是多线程的情况下,也能保证数据的唯一性。下篇文章让我们一下分析一下面试必问的消息屏障机制。

今天的问题是:

1.ThreadLocal会造成内存泄露吗?

收藏
分享
海报
0 条评论
38

本站已关闭游客评论,请登录或者注册后再评论吧~