Android进阶知识:Handler相关

一、前言

HandlerAndroid中的地位没必要说了,学习安卓开发必然跳不过Handler,讲解Handler的文章也是很是的多,这里我对我所了解的Handler这种Android中多线程间的通讯方式的相关知识作一个总结。面试

二、Handler使用

Handler做为线程间通讯的方式,最常使用的地方就是子线程更新UI。由于AndroidUI控件不是线程安全的,若是在多线程下并发访问可能会致使UI控件处于不可预期的状态。因此在子线程想要更新UI的时候会使用handler.sendMessage(Message msg)方法通知主线程更新。
关于Handler的经常使用方法,除了sendMessage系列方法,handler还有一个post系列方法,能够在子线程中经过handler.post(Runnable r)方法进行一些在主线程的操做。
sendMessage系列方法:安全

public final boolean sendMessage(Message msg)
public final boolean sendEmptyMessage(int what)
public final boolean sendEmptyMessageDelayed(int what, long delayMillis)
public final boolean sendEmptyMessageAtTime(int what, long uptimeMillis)
public final boolean sendMessageDelayed(Message msg, long delayMillis)
public final boolean sendMessageAtFrontOfQueue(Message msg)
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
复制代码

post系列方法:bash

public final boolean post(Runnable r)
public final boolean postAtTime(Runnable r, long uptimeMillis)
public final boolean postAtTime(Runnable r, Object token, long uptimeMillis)
public final boolean postDelayed(Runnable r, long delayMillis)
public final boolean postDelayed(Runnable r, Object token, long delayMillis)
public final boolean postAtFrontOfQueue(Runnable r)
复制代码

简单运用:网络

private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MESSAGE_SEND:
                    mTitleText.setText((String) msg.obj);
                    break;
            }
        }
    };
    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.button:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                           //子线程耗时操做
                          Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        handler.post(new Runnable() {
                            @Override
                            public void run() {
                                mTitleText.setText("post");
                            }
                        });
                    }
                }).start();
                break;
            case R.id.button2:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            //子线程耗时操做
                            Thread.sleep(3000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                        //发送消息
                        Message message = Message.obtain();
                        message.what = MESSAGE_SEND;
                        message.obj = "sendMessage";
                        handler.sendMessage(message);
                    }
                }).start();
                break;
        }
    }
复制代码

三、Handler源码阅读

学会使用后下一步就是学习原理 ,学习原理就免不了要阅读源码。 从上面的图已经能看出 Handler的基本工做流程。过程当中主要涉及了如下四个类:

  • Handler
  • Looper
  • MesageQueue
  • Message

接下来首先就从Handler看起。多线程

Handler的构造方法:

使用Handler第一步就是建立一个Handler对象,从而首先调用的就是Handler的构造方法。固然Handler构造方法有不少的不一样参数的重载,这里只看最主要的两个。并发

public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    
 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());
            }
        }
        //获取当前线程的Looper
        mLooper = Looper.myLooper();
        if (mLooper == null) {
            throw new RuntimeException(
                "Can't create handler inside thread " + Thread.currentThread()
                        + " that has not called Looper.prepare()");
        }
        //给Handler中的成员变量初始化
        mQueue = mLooper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
复制代码

第一个是传参带有Looper的所调用的方法,其中就是作了些初始化的操做,调用这个方法建立的HandlerLooper就是做为参数传入的Looper
第二个构造方法中第一个if判断当前Handler类是否有内存泄漏的隐患,主要是检验当前类是不是匿名类、成员类、局部类是否静态修饰等。接着经过Looper.myLooper()方法获取当前线程中的Looper对象。接下来判断若是这个Looper为空,说明当前Handler初始化所在线程没有Looper,会抛出Exception。这里就决定了Handler初始化所在线程必须有Looper,因此在子线程中建立Handler以前先要经过Looper.prepare()方法建立Looper。接着就是对Handler中一些成员变量进行初始化,将Looper中的消息队列引用传递给mQueue,将构造中传入的callback初始化等。
来看下LoopermyLooper()方法:app

public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }
复制代码

这里是从ThreadLocal中获取到一个Looper。关于ThreadLocal的相关知识点能够看Android进阶知识:ThreadLocalless

Handler建立以后,在须要进行主线程操做的时候,咱们会使用handlersendMessage系列方法,或者post系列方法。这里一样有不少重载,具体的方法在前文中已经列举。这里先看post方法:异步

post系列方法:
public final boolean post(Runnable r){
    return  sendMessageDelayed(getPostMessage(r), 0);
 }
复制代码

能够看到无论哪一个post方法中,都是经过getPostMessage()方法构建一个Message最终仍是调用对应的sendMessage方法。async

private static Message getPostMessage(Runnable r) {
        Message m = Message.obtain();
        m.callback = r;
        return m;
    }
复制代码

getPostMessage方法中经过Message.obtain()获取一个Message将传入的Runnable赋给Message中的callback,接着返回这个Message

sendMessage系列方法:

由于post方法的最后又都调用了对应的sendMessage方法,因此接下来看sendMessage方法的实现:

public final boolean sendMessage(Message msg)
 {
    return sendMessageDelayed(msg, 0);
 }
 
 public final boolean sendMessageDelayed(Message msg, long delayMillis)
 {
   if (delayMillis < 0) {
       delayMillis = 0;
    }
    return sendMessageAtTime(msg, SystemClock.uptimeMillis() + delayMillis);
 }
 
 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);
    }
    
复制代码

这里看到通过层层调用最终执行了enqueueMessage方法。这里要注意的是sendMessageDelayed方法中设置的延迟时间是经过SystemClock.uptimeMillis()+ 延迟时间来计算的。
SystemClock.uptimeMillis()方法是获取从开机到如今的毫秒数,与System.currentTimeMillis()获取从1970年1月1日到如今的毫秒数不一样,后者会受到手机系统时间影响,而系统时间能够手动修改。sendMessageAtTime方法中对MessageQueue进行是否为null的判断,为null抛出异常,这里的MessageQueue就是在Handler构造函数中Looper中的Queue

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

复制代码

enqueueMessage方法中经过msg.target=this这一句,将Handler的引用传递给了Message中的target。接着调用了MessageQueueenqueueMessage方法。接下来进入MessageQueue

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.next prev.next = msg; } // We can assume mPtr != 0 because mQuitting is false. if (needWake) { nativeWake(mPtr); } } return true; } 复制代码

MessageQueueenqueueMessage方法中,传入的msg就是要插入到消息队列的新消息,when是这个任务的延时时间。方法里首先对消息里的target也就是Handler进行空判断,为空抛出异常。接着判断了这个消息是否被使用和消息队列是否退出等。看到msg.when = when;这一行将延迟时间传递保存到消息内部,下一行定义了一个临时指针p用来指向mMessage,这个mMessage就是消息队列的首结点,接下来的这个if-else作的就是将新消息根据他的when大小,将他按顺序加入到队列中合适位置上。这里能够看出这个消息队列其实是个链表,每一个Message是一个结点,结点中有一个next指针存放下一个结点位置。这里的新消息的添加,就是向这个链表中插入一个节点。
先看if中判断,p==null即首结点为nullwhen=0及延时时间为0,当即执行,when<p.when即新节点的延时时间小于当前链表首结点的延时时间,这三种状况下直接将新消息节点插到链表头部,即msg.next=p;mMessages=msg这两行的操做,而后唤醒消息线程处理新消息。else就要将新节点根据when的大小插入到链表中合适位置,这里又定义了一个临时指针prev,指向p指向的前一个节点,看到for (;;)循环中,将p指针不断向后移,直到p等于null即链表结尾或者新结点的when<p.when的时候,即这个链表是按照节点when的从小到大的顺序排列插入的。此时break出循环,将新节点插入到此处,即msg.next=p;prev.next=msg。到此消息发送加入消息队列的过程节结束了。

取出处理消息:

有往消息队列里加消息,就有从消息队列取消息。谁来取呢?就是Looper,以前看到在Handler的构造方法里,经过Looper.myLooper()方法获取到当前线程(handler建立所在线程)的Looper对象。并且还知道了建立Handler的线程必须存在一个Looper对象不然会抛出异常。这也是咱们不能在子线程里直接建立使用Handler的缘由。那么为何主线程能够直接建立Handler呢?是由于主线程中有Looper。那么主线程的Looper又是哪来的呢?这须要看到ActivityThread类里的代码。

public static void main(String[] args) {
        ......
       
        Looper.prepareMainLooper();

        ......
        ActivityThread thread = new ActivityThread();
        thread.attach(false, startSeq);

        if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }

        if (false) {
            Looper.myLooper().setMessageLogging(new
                    LogPrinter(Log.DEBUG, "ActivityThread"));
        }

        // End of event ActivityThreadMain.
        Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
        Looper.loop();

       ......
    }
复制代码

ActivityThreadmain方法在app启动时调用,这里省去了一些无关代码,只看与主线程Looper相关的。能够看到在main方法里调用了Looper.prepareMainLooper()方法,以后获取了一个主线程的Handler,接着调用了Looper.loop()方法。一个方法一个方法来看,先是prepareMainLooper()方法:

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));
    }
    public static void prepareMainLooper() {
        prepare(false);
        synchronized (Looper.class) {
            if (sMainLooper != null) {
                throw new IllegalStateException("The main Looper has already been prepared.");
            }
            sMainLooper = myLooper();
        }
    }
复制代码

prepareMainLooper()方法中调了prepare(false)方法,这里quitAllowed传的是false,这个标记会传递到MessageQueue中,这里说明主线程的消息队列是不容许退出的。prepare()方法里初始化了new了一个Looper对象,并将它添加到当前线程的ThreadLocal中。接着到Looper的构造函数中看看:

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

Looper的构造函数很简单,建立了MessageQueue消息队列保存了一个当前线程的对象。从这里看出来建立一个Handler须要传入建立线程的Looper,而建立Looper又对应建立了一个MessageQueue。下面回到main方法中看thread.getHandler()这个获取主线程Handler方法:

final Handler getHandler() {
        return mH;
    }
复制代码

这里直接返回mH这个Handler,那么这个Handler是在哪里建立的呢?

final H mH = new H();
复制代码

跟踪下去发现这个mHActivityThread类的成员变量,而且直接初始化。因此这个Handler就是在main方法中建立ActivityThread对象时就初始化了。最后调用Looper.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;
        ......
        for (;;) {
            Message msg = queue.next(); // might block
            if (msg == null) {
                // No message indicates that the message queue is quitting.
                return;
            }
            ......
            try {
                msg.target.dispatchMessage(msg);
                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            ......            
        }
    }
复制代码

这里一样省略了部分代码,来看主要的流程,首先仍是获取当前线程Looper对空作了校验。而后从Looper中获取到MessageQueue,接着进入for循环,调用queue.next()从消息队列中取出消息,根据注释,这个方法有可能会阻塞,若是返回的msg为null,说明消息队列正在退出。接着在try代码块中调用了msg.target.dispatchMessage(msg)方法,这个msg.target就是在前面enqueueMessage方法中设置的发送消息的Handler,因此这里调用了Handler的dispatchMessage(msg)方法。

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

dispatchMessage方法中对callback进行了判断,这里有两个callback,msg.callback是对应Handler中的post方法,将传入的Runnable存入Messagecallback中,若是调用post方法msg.callback不为空调用handleCallback方法,最终会执行Runnablerun方法,开始执行post时传进来的任务。

private static void handleCallback(Message message) {
        message.callback.run();
    }
复制代码

第二个mCallback,对应的是建立Handler时的传参,若是不为空会执行mCallback.handleMessage方法。若是初始化时没传mCallback,就会执行handleMessage(msg)方法:

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

这个方法就是咱们本身须要实现的处理消息的方法,也是咱们最经常使用重写的方法。至此UI主线程中建立HandlerLooper,而且Looper开启轮询到调用了HandlerdispatchMessage处理消息的过程就结束了。

子线程Handler使用:

回到上面说的,这是主线程中HandlerLooper的初始化,那么要在子线程使用Handler该怎么作呢?

new Thread(new Runnable() {
            @Override
            public void run() {
                Looper.prepare();
                Handler handler = new Handler(Looper.myLooper()) {
                    @Override
                    public void handleMessage(Message msg) {
                        //处理消息
                    }
                };
                Looper.loop();
            }
        }).start();
复制代码

其实和主线程同样,由于子线程中没有Looper因此须要咱们本身建立Looper而且调用Looper.loop()方法开始轮询。这里的Looper.prepare()方法和prepareMainLooper()方法同样最终会调用prepare(boolean quitAllowed)方法,这时传入的quitAllowed为true,表示消息队列能够退出。
至此Handler机制相关类HandlerLooperMessageQueue的主要方法源码都看完了,他们之间的工做流程相互关系也都清楚了。

Message类:

其实还剩一个Message消息类,Message类中主要看一个obtain()方法,Message除了能够经过new来建立,还能够经过obtain()方法来得到,而且obtain()方法是从全局池中获取Message对象,能避免从新分配对象。

public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }
复制代码

这里看到只要sPool不等于null,就从sPool头上去一个消息节点返回。因此使用obtain方法,相对直接new一个Message能减小内存分配。

四、Handler相关面试题

Q1:Handler机制中涉及到了哪些类?做用分别是什么?

A1:主要涉及到HandlerLooperMessageQueueMessage这四个类。
Handler:发送消息到消息队列。
Looper:从消息队列中取出消息,交给Handler的dispatchMessage方法处理。
MessageQueue:消息队列存储管理消息插入取出。
Message:消息类,携带着消息数据。

Q2:MessageQueue 中的 Message有顺序吗?若是有是按什么顺序排列的?

A2:经过以前的源码阅读知道,是有顺序的,是根据Message.when这个相对时间排列的。

Q3:子线程中能够建立 Handler 对象吗?

A3:一样从源码中能够知道,子线程中不能直接建立HandlerHandler建立须要指定一个Looper,子线程中没有Looper,须要先建立Looper,调用Looper.loop方法。

Q4:MessageQueue内部实现是一个队列吗?

A4:不是,内部实现实际上是一个单链表。

Q5:Looper的quit方法和quitSafely方法有什么区别?

A5:quit方法会清空消息队列中的全部消息,quitSafely方法只会清除全部延迟消息,非延迟消息仍是分发出去交给Handler处理。具体仍是看源码:

public void quit() {
        mQueue.quit(false);
    }

    public void quitSafely() {
        mQueue.quit(true);
    }
复制代码

这里其实是调用了MessageQueuequit方法:

void quit(boolean safe) {
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }
        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;
            if (safe) {
                removeAllFutureMessagesLocked();
            } else {
                removeAllMessagesLocked();
            }
            // We can assume mPtr != 0 because mQuitting was previously false.
            nativeWake(mPtr);
        }
    }

复制代码

quit方法传入的是false调用的是removeAllMessagesLocked()方法,quitSafely传入的是true调用的是removeAllFutureMessagesLocked方法。

private void removeAllMessagesLocked() {
        Message p = mMessages;
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }
复制代码

removeAllMessagesLocked方法中直接将全部消息清空。

private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        Message p = mMessages;
        if (p != null) {
            if (p.when > now) {
                removeAllMessagesLocked();
            } else {
                Message n;
                for (;;) {
                    n = p.next;
                    if (n == null) {
                        return;
                    }
                    if (n.when > now) {
                        break;
                    }
                    p = n;
                }
                p.next = null;
                do {
                    p = n;
                    n = p.next;
                    p.recycleUnchecked();
                } while (n != null);
            }
        }
    }
复制代码

removeAllFutureMessagesLocked方法中先作判断若是首节点when大于当前时间说明全是延迟消息,就一样调用removeAllMessagesLocked处理所有清空,不然循环找到队列中when大于now也就是大于当前时间的节点位置,将该节点消息同其后的全部消息清空。

Q6:为何主线程不会由于Looper.loop()里的死循环卡死?

A6:这个涉及到LinuxEpoll机制。简单来讲就是Android应用程序的主线程在进入消息循环过程前,会在内部建立一个Linux管道(Pipe),这个管道的做用是使得Android应用程序主线程在消息队列为空时能够进入空闲等待状态,而且使得当应用程序的消息队列有消息须要处理时唤醒应用程序的主线程。
具体解释:Android中为何主线程不会由于Looper.loop()里的死循环卡死?

五、Handler引起内存泄漏

Handler致使的内存泄露,是平时写代码不注意很是容易出现的问题,并且内存泄露多了对应用性能影响较大,因此单独研究下。
通常咱们使用Handler更新UI都是这样的:

public class HandlerActivity extends Activity {
    private TextView mTextView;
    private final int MESSAGE_SEND = 0x01;
    private MyHandler handler = new MyHandler();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        mTextView = findViewById(R.id.textView);
        Message obtain = Message.obtain();
        obtain.what = MESSAGE_SEND;
        obtain.obj = "文字";
        handler.sendMessageDelayed(obtain, 1000 * 60 * 10);
    }

    class MyHandler extends Handler {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MESSAGE_SEND) {
                mTextView.setText((String) msg.obj);
            }
        }
    }
}

复制代码

这里看到进入Activity就发送了一个延迟消息,现实开发中多是网络有延迟又或者进入一个界面后马上离开这时数据还没加载好,只要是耗时任务尚未完成,当前的Activity又须要销毁,这时候由于此时MyHander,它隐式持有外部类的引用,当Activity销毁时,此时异步耗时任务尚未结束,仍然持有Activity的引用,使得Activity没法回收,形成内存泄漏。 经过集成LeakCanary能够检测到内存泄漏,以下图:

LeakCanary
一样经过 AndroidStudio Profiler能够查看到内存泄露:
经过屡次打开关闭 HandlerActivity,而后观察内存状况,能够发现即便在我手动 GC屡次后,仍然存在多个实例没有被回收的现象。

内存泄漏解决方法:

  1. Handler定义为静态,静态内部类不会持有外部类的引用。
  2. 由于静态内部类不持有外部类引用,因此在Handler中没法访问外部类的成员,须要用一个外部类的弱引用来访问外部成员,又由于是弱引用,在GC时能够将其回收,不会形成内存泄露。
  3. ActivityonDestory方法中调用removeCallbacksAndMessages方法清除消息队列。

解决内存泄漏:

public class WeakHandlerActivity extends Activity {
    private TextView mTextView;
    private static final int MESSAGE_SEND = 1;
    private MyHandler handler = new MyHandler(this);

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        mTextView = findViewById(R.id.textView);
        Message obtain = Message.obtain();
        obtain.what = MESSAGE_SEND;
        obtain.obj = "文字";
        handler.sendMessageDelayed(obtain, 1000 * 60 * 10);
    }
   //静态内部类
    static class MyHandler extends Handler {
        private final WeakReference<WeakHandlerActivity> mActivty;
        public MyHandler(WeakHandlerActivity activity) {
        //初始化Activity的弱引用
            mActivty = new WeakReference<WeakHandlerActivity>(activity);
        }
        @Override
        public void handleMessage(Message msg) {
            WeakHandlerActivity activity = mActivty.get();
            if (activity != null) {
                if (msg.what == MESSAGE_SEND) {
                    activity.mTextView.setText((String) msg.obj);
                }
            }
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //清除消息队列
        handler.removeCallbacksAndMessages(null);
    }
}
复制代码

经过这三个方法结合使用,就能够解决Handler致使的内存泄漏的问题。此次再经过Profiler来查看内存状况:

能够看到在屡次打开关闭界面后,仍然会存在多个 WeakHandlerActivity实例。
可是在 GC事后内存中的 WeakHandlerActivity已经被所有回收,不会继续占用内存,形成泄漏。

六、总结

  • Handler是Android提供的一种线程间通讯方式。由于Android中UI控件不是线程安全的,多线程并发访问会出现同步问题,因此若是子线程想更新UI一般经过Handler来完成线程间通讯。

  • Handler的工做流程主要是由Handler发送消息,将消息添加到消息队列MessageQueue中,再经过轮询器Looper从消息队列中取出消息,交给Handler去分发处理消息对应任务。

  • Handler使用时容易发生内存泄露,记得经过静态内部类+弱引用的方式使用Handler,而且在Activity的onDestory方法里记得清除消息队列。

相关文章
相关标签/搜索