写点什么

Android 进阶:三、这一次,从入门到精通系列 Android 高级工程师路线介绍

用户头像
Android架构
关注
发布于: 刚刚

Handler handler = new Handler();


所以我们来看看它的构造函数的源码:


public Handler() {this(null, false);}


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


这段代码做了四件事:


1、校验是否可能内存泄漏 2、初始化一个 Looper mLooper3、初始化一个 MessageQueue mQueue


我们一件事一件事的看:


####1、校验是否存在内存泄漏


Handler 的构造函数中首先判断了 FIND_POTENTIAL_LEAKS 的值,为 true 时,会获取该对象的运行时类,如果是匿名类,成员类,局部类的时候判断修饰符是否为 static,不是则提示可能会造成内存泄漏。问:为什么匿名类,成员类,局部类的修饰符不是 static 的时候可能会导致内存泄漏呢?


答:因为,匿名类,成员类,局部类都是内部类,内部类持有外部类的引用,如果 Activity 销毁了,而 Hanlder 的任务还没有完成,那么 Handler 就会持有 activity 的引用,导致 activity 无法回收,则导致内存泄漏;静态内部类是外部类的一个静态成员,它不持有内部类的引用,故不会造成内存泄漏


这里我们可以思考为什么非静态类持有外部类的引用?为什么静态类不持有外部类的引用?


问:使用 Handler 如何避免内存泄漏呢?答:使用静态内部类的方式 ####2、初始化初始化一个 Looper mLooper 这里获得一个 mLooper,如果为空则跑出异常:


"Can't create handler inside thread that has not called Looper.prepare() "


如果没有调用 Looper.prepare()则不能再线程里创建 handler!我们都知道,如果我们在 UI 线程创建 handler,是不需要调用这个方法的,但是如果在其他线程创建 handler 的时候,则需要调用这个方法。那这个方法到底做了什么呢?我们去看看代码:


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


先取 sThreadLocal.get()的值,结果判断不为空,则跑出异常“一个线程里只能创建一个 Looper”,所以 sThreadLocal 里存的是 Looper;如果结果为空,则创建一个 Looper。那我


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


们再看看,myLooper()这个方法的代码:


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


总上我们得出一个结论:当我们在 UI 线程创建 Handler 的时候,sThreadLocal 里已经存了一个 Looper 对象,所以有个疑问:当我们在 UI 线程中创建 Handler 的时候 sThreadLocal 里的 Looper 从哪里来的?我们知道,我们获取主线程的 Looper 需要调用 getMainLooper()方法,代码如下:


public static Looper getMainLooper() {synchronized (Looper.class) {return sMainLooper;}}


所以我们跟踪一下这个变量的赋值,发现在方法 prepareMainLooper()中有赋值,我们去看看代码:


public static void prepareMainLooper() {prepare(false);synchronized (Looper.class) {if (sMainLooper != null) {throw new IllegalStateException("The main Looper has already been prepared.");}sMainLooper = myLooper();}}


  • 第一步调用了 prepare(false),这个方法我们刚才已经看了,是创建一个 Looper 对象,然后存到 sThreadLocal 中;\

  • 然后判断 sMainLooper 是否为空,空则抛出异常

  • sMainLooper 不为空,则 sMainLooper = myLooper()


至此 sMainLooper 对象赋值成功,所以,我们需要知道 prepareMainLooper()这个方法在哪调用的,跟一下代码,就发现在 ActivityThread 的 main 方法中调用了 Looper.prepareMainLooper();。现在真相大白:当我们在 UI 线程中创建 Handler 的时候 sThreadLocal 里的 Looper 是在 ActivityThread 的 main 函数中调用了 prepareMainLooper()方法时初始化的 ActivityThread 是一个在一个应用进程中负责管理 Android 主线程的执行,包括活动,广播,和其他操作的类


####3、初始化一个 MessageQueue mQueue


从代码里我们看出这里直接调用了:mLooper.mQueue 来获取这个对象,那这个对象可能在 Looper 初始化的时候就产生了。我们去看看 Looper 的初始化代码:


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


代码很简单,就是创建了 MessageQueue 的对象,并获得了当前的线程。


至此,Handler 的创建已经完成了,本质上就是获得一个 Looper 对象和一个 MessageQueue 对象!###二、使用 Handler 发送消息


Handler 的发送消息的方式有很多,我们跟踪一个方法 sendMessage 方法一直下去,发现最后竟然调用了 enqueueMessage(queue, msg, uptimeMillis),那我们看看这个方法的代码:


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


这段代码做了两件事:


1、给 msg.target 赋值,也就是 Handler 对象 2、给消息设置是否是异步消息。3、调用 MessageQueue 的 enqueueMessage(msg, uptimeMillis)方法我们只关注第三步:这一步把 Handler 的发送消息转给了 MessageQueue 的添加消息的方法。所以至此,Handler 发送消息的任务也已经完成了,本质上就是调用 MessageQueue 自己的添加消息的方法!###三、MessageQueue 添加消息 MessageQueue 的构造函数代码如下:


MessageQueue(boolean quitAllowed) {mQuitAllowed = quitAllowed;mPtr = nativeInit();}


也没做什么特别的事情。我们去看看 enqueueMessage(msg, uptimeMillis)方法代码:


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;

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
Android进阶:三、这一次,从入门到精通系列Android高级工程师路线介绍