文章目录

  • 概述
  • ThreadLocal
  • MessageQueue工作原理
  • Looper工作原理
  • Handler工作原理
  • 主线程消息循环
  • HandlerThread
  • Handler 引发的内存泄漏

概述

Handler是Android消息机制的上层接口,这使得在开发过程中只需要和Handler交互即可。
Android消息机制主要指的是Handler的运行机制,Handler底层运行需要MessageQueue和Looper的支撑。

  • MessageQueue :消息队列,它内部存储了一组消息,对外提供插入和删除。内部存储结构是链表。
  • Looper :MessageQueue只是一个消息的存储单元,不能处理消息,而Looper就填补了这个功能,Looper以无限循环的方式查询是否有新消息,如果有的话就处理,否则就一直等待。
  • ThreadLocal :ThreadLocal的作用是在每个线程中存储数据,ThreadLocal可以在不同的线程中互不干扰的存储并提供数据,通过ThreadLocal可以轻松的获取每个线程的Looper。

创建Handler

给当前线程创建Looper,或者在一个有Looper的线程创建Handler。否则会抛出异常。

创建完Handler,Looper以及MessageQueue就可以和Handler一起协同工作了。

可以通过Handler的post方法将一个Runnable投递到Handler内部的Looper中去处理,也可以通过Handler的send方法发送一个消息,这个消息同样会在Looper中处理
Android——消息机制_第1张图片

ThreadLocal

这里和书上的不一样,ThreadLocal的源码已经发成了很大的改变。

ThreadLocal是一个数据结构,有点像HashMap,可以保存一个值,并且保证各个线程的数据互不干扰。

ThreadLocal localName = new ThreadLocal();localName.set("aaa");String name = localName.get();

线程1初始化为aaa,线程2在此get时,值将会是null。

源码

 public void set(T value) {    Thread t = Thread.currentThread();    ThreadLocalMap map = getMap(t);    if (map != null)        map.set(this, value);    else        createMap(t, value);}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();}ThreadLocalMap getMap(Thread t) {    return t.threadLocals;}

可以看出每个线程有一个ThreadLocalMap的数据结构,当执行set时,会将值存在指定的ThreadLocalMap中,由于每个线程的ThreadLocalMap是不一样的,所以各个线程互不干扰。

ThreadLoalMap

ThreadLoalMap 是一个类似HashMap的数据结构。
在ThreadLoalMap中,是初始化一个大小为一个Entry的数组,Entry对象用来保存每一个key-value键值对,只不过可以永远是ThreadLocal对象。
Android——消息机制_第2张图片

看一看ThreadLoalMap插入一个key-values的实现。

private void set(ThreadLocal<?> key, Object value) {    Entry[] tab = table;    int len = tab.length;    int i = key.threadLocalHashCode & (len-1);    for (Entry e = tab[i];         e != null;         e = tab[i = nextIndex(i, len)]) {        ThreadLocal<?> k = e.get();        if (k == key) {            e.value = value;            return;        }        if (k == null) {            replaceStaleEntry(key, value, i);            return;        }    }    tab[i] = new Entry(key, value);    int sz = ++size;    if (!cleanSomeSlots(i, sz) && sz >= threshold)        rehash();}

每个ThreadLocal都有一个Hash值threadLocalHashCode
插入过程中根据ThreadLocal对象的hash值,定位到table中的位置i
插入过程:

  1. 如果这个位置是空的,那么就初始化一个Entry对象放在位置i上
  2. 如果i已经有对象了并且key值是一样的,那么直接覆盖之前的值。
  3. 如果i的entry对象和即将设置的key没关系,那么只能找下一个空位置。

MessageQueue工作原理

消息队列指的是MessageQueue,MessageQueue主要包含两个操作,插入和读取,对应方法enqueueMessage和next,虽然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.next                prev.next = msg;            }            // We can assume mPtr != 0 because mQuitting is false.            if (needWake) {                nativeWake(mPtr);            }        }        return true;    }

其就是链表的插入操作。

next源码

    Message next() {·····        int pendingIdleHandlerCount = -1; // -1 only during first iteration        int nextPollTimeoutMillis = 0;        for (;;) {            if (nextPollTimeoutMillis != 0) {                Binder.flushPendingCommands();            }            nativePollOnce(ptr, nextPollTimeoutMillis);            synchronized (this) {                // Try to retrieve the next message.  Return if found.                final long now = SystemClock.uptimeMillis();                Message prevMsg = null;                Message msg = mMessages;                if (msg != null && msg.target == null) {                    // Stalled by a barrier.  Find the next asynchronous message in the queue.                    do {                        prevMsg = msg;                        msg = msg.next;                    } while (msg != null && !msg.isAsynchronous());                }                if (msg != null) {                    if (now < msg.when) {                        // Next message is not ready.  Set a timeout to wake up when it is ready.                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);                    } else {                        // Got a message.                        mBlocked = false;                        if (prevMsg != null) {                            prevMsg.next = msg.next;                        } else {                            mMessages = msg.next;                        }                        msg.next = null;                        if (DEBUG) Log.v(TAG, "Returning message: " + msg);                        msg.markInUse();                        return msg;                    }                } else {                    // No more messages.                    nextPollTimeoutMillis = -1;                }······        }    }

next是一个无限循环的方法,如果消息队列中没有消息,那么next方法会一直阻塞在这里。当有新消息时,next方法会返回这条消息,并从单链表中删除。

Looper工作原理

Looper在Android消息机制中扮演着消息循环的角色,会不断的从MessageQueue中查看是否有消息,如果有新消息就会立即处理,否则就会阻塞在那里。在构造方法中会创建一个MessageQueue消息队列然后保存当前线程对象。

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

Handler工作需要Looper,没有Looper的线程会报错,通过Looper.prepare()即可为当前线程创建一个Looper,通过Looper.loop()开启消息循环

        new Thread(new Runnable() {            @Override            public void run() {                Looper.prepare();                Handler handler = new Handler();                Looper.loop();            }        }).start();

除了prepare,还提供了了prepareMainLooper方法,这个方法主要是给主线程也就是ActivityThread创建Looper使用的。
Looper提供quit和quitSafely来退出一个Looper,区别是前者会直接退出,后者会处理完所有的消息后才退出。退出后Handler的send方法会返回false。
在子线程中如果手动创建了一个Looper,那么在事情完成之后需要调用quit方法终止消息循环,否则子线程会一直处于等待的状态。

Looper最重要的一个方法是loop方法,只有调用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();        // Allow overriding a threshold with a system prop. e.g.        // adb shell 'setprop log.looper.1000.main.slow 1 && stop && start'        final int thresholdOverride =                SystemProperties.getInt("log.looper."                        + Process.myUid() + "."                        + Thread.currentThread().getName()                        + ".slow", 0);        boolean slowDeliveryDetected = false;        for (;;) {            Message msg = queue.next(); // might block            if (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 logger            final Printer logging = me.mLogging;            if (logging != null) {                logging.println(">>>>> Dispatching to " + msg.target + " " +                        msg.callback + ": " + msg.what);            }            final long traceTag = me.mTraceTag;            long slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;            long slowDeliveryThresholdMs = me.mSlowDeliveryThresholdMs;            if (thresholdOverride > 0) {                slowDispatchThresholdMs = thresholdOverride;                slowDeliveryThresholdMs = thresholdOverride;            }            final boolean logSlowDelivery = (slowDeliveryThresholdMs > 0) && (msg.when > 0);            final boolean logSlowDispatch = (slowDispatchThresholdMs > 0);            final boolean needStartTime = logSlowDelivery || logSlowDispatch;            final boolean needEndTime = logSlowDispatch;            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));            }            final long dispatchStart = needStartTime ? SystemClock.uptimeMillis() : 0;            final long dispatchEnd;            try {                msg.target.dispatchMessage(msg);                dispatchEnd = needEndTime ? SystemClock.uptimeMillis() : 0;            } finally {                if (traceTag != 0) {                    Trace.traceEnd(traceTag);                }            }            if (logSlowDelivery) {                if (slowDeliveryDetected) {                    if ((dispatchStart - msg.when) <= 10) {                        Slog.w(TAG, "Drained");                        slowDeliveryDetected = false;                    }                } else {                    if (showSlowLog(slowDeliveryThresholdMs, msg.when, dispatchStart, "delivery",                            msg)) {                        // Once we write a slow delivery log, suppress until the queue drains.                        slowDeliveryDetected = true;                    }                }            }            if (logSlowDispatch) {                showSlowLog(slowDispatchThresholdMs, dispatchStart, dispatchEnd, "dispatch", msg);            }            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();        }    }

源码比较好理解,loop方法是一个死循环,唯一跳出循环的方式是MessageQueue的next方法返回了null,当Looper的quit方法被调用后,Looper就会调用MessageQueue的quit或quitSaftly方法通知消息队列退出,此时他的next方法会返回null,当没有消息时,next方法会一直堵塞在那里,这也导致loop方法一直阻塞在那里。

如果MessageQueue的next方法返回了新消息,Looper就会处理这条消息。

 msg.target.dispatchMessage(msg);

msg.target是发送这条消息的对象。

最终发送的消息最终又交给他的dispatchMessage();方法处理了。

Handler的dispatchMessage方法是在创建Handler时所使用的Looper中执行的,这样就成功的将代码切换到指定的线程中去了。

Handler工作原理

Handler的主要工作是发送消息和接收消息。消息的发送可以通过post的一系列方法以及send的一系列方法实现,post最终是通过send的一系列方法来实现的。
Android——消息机制_第3张图片
Android——消息机制_第4张图片

    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);    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {        msg.target = this;        if (mAsynchronous) {            msg.setAsynchronous(true);        }        return queue.enqueueMessage(msg, uptimeMillis);    }

发送消息的方法最终会调用sendMessageAtTime并且最后调用queue.enqueueMessage(msg, uptimeMillis);请消息插入到消息队列。然后MessageQueue的next方法就会返回这个消息给Looper,Looper接收到消息后交给Handler处理,这样就会调用dispatchMessage()方法。

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

可以清楚的看到,先检查Message的callback是否为null,不为null就通过handleCallBack来处理。
Message的callBack的一个Runnable对象,实际上是post方法传递的Runnable

private static void handleCallback(Message message) {    message.callback.run();}

handleCallback逻辑很简单,实际上就是运行run方法。

然后就是mCallback,检查mCallBack是否为null,不为null就调用他的handleMessage方法。

  public interface Callback {      /**       * @param msg A {@link android.os.Message Message} object       * @return True if no further handling is desired       */      public boolean handleMessage(Message msg);  }

Callback是一个接口,就是在创建Handler Handler handler = new Handler(callback)时构造方法里面的参数。

 Handler handler = new Handler(new Handler.Callback() {     @Override     public boolean handleMessage(Message msg) {         return false;     } });

最后调用Handler本身的方法handleMessage处理消息。它是一个空实现。

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

Handler的流程处理消息的流程图。
Android——消息机制_第5张图片

主线程消息循环

    public static void main(String[] args) {        Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "ActivityThreadMain");        // CloseGuard defaults to true and can be quite spammy.  We        // disable it here, but selectively enable it later (via        // StrictMode) on debug builds, but using DropBox, not logs.        CloseGuard.setEnabled(false);        Environment.initForCurrentUser();        // Set the reporter for event logging in libcore        EventLogger.setReporter(new EventLoggingReporter());        // Make sure TrustedCertificateStore looks in the right place for CA certificates        final File configDir = Environment.getUserConfigDirectory(UserHandle.myUserId());        TrustedCertificateStore.setDefaultUserDirectory(configDir);        Process.setArgV0("");        Looper.prepareMainLooper();        // Find the value for {@link #PROC_START_SEQ_IDENT} if provided on the command line.        // It will be in the format "seq=114"        long startSeq = 0;        if (args != null) {            for (int i = args.length - 1; i >= 0; --i) {                if (args[i] != null && args[i].startsWith(PROC_START_SEQ_IDENT)) {                    startSeq = Long.parseLong(                            args[i].substring(PROC_START_SEQ_IDENT.length()));                }            }        }        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();        throw new RuntimeException("Main thread loop unexpectedly exited");    }

主线程通过Looper.prepareMainLooper(); 来创建Looper和MessageQueue。

  class H extends Handler {        public static final int BIND_APPLICATION        = 110;        public static final int EXIT_APPLICATION        = 111;        public static final int RECEIVER                = 113;        public static final int CREATE_SERVICE          = 114;        public static final int SERVICE_ARGS            = 115;        public static final int STOP_SERVICE            = 116;        public static final int CONFIGURATION_CHANGED   = 118;        public static final int CLEAN_UP_CONTEXT        = 119;        public static final int GC_WHEN_IDLE            = 120;        public static final int BIND_SERVICE            = 121;        public static final int UNBIND_SERVICE          = 122;        public static final int DUMP_SERVICE            = 123;        public static final int LOW_MEMORY              = 124;        public static final int PROFILER_CONTROL        = 127;        public static final int CREATE_BACKUP_AGENT     = 128;        public static final int DESTROY_BACKUP_AGENT    = 129;        public static final int SUICIDE                 = 130;        ······  }

主线程的消息循环开始了以后,ActivityThread还需要一个Handler来和消息队列进行交互,这个Handler就是Activity.H,它内部定义了一组消息类型,主要包括四大组件的启动和停止。

ActivityThread通过ApplicationThread和AMS进行进程间通信,AMS以进程间通信的方式完成ActivityThread的请求后会回调Application中的Binder方法,然后ApplicationThred会向H发送消息,H收到消息后会将Application中的逻辑切换到ActivityThread中去执行,即切换到主线程中去执行。这个过程就是主线程的消息循环模型。

这段话暂时没理解,先留着。

收藏网上的几张图

Android——消息机制_第6张图片

Android——消息机制_第7张图片

Android——消息机制_第8张图片

HandlerThread

Handler是一种可以使用Handler的Thread,他的实现很简单,就是在run方法中通过Looper.prapare()来创建消息队列,并通过Looper.loop()来开启消息循环。

基本使用

 HandlerThread handlerThread = new HandlerThread(""); handlerThread.start(); Handler handler1 = new Handler(handlerThread.getLooper(), new Handler.Callback() {     @Override     public boolean handleMessage(Message msg) {         //处理UI         return true;     } }); handler1.post(new Runnable() {     @Override     public void run() {         //子任务     } });

上面步骤不能乱。

源码

@Overridepublic void run() {    mTid = Process.myTid();    Looper.prepare();    synchronized (this) {        mLooper = Looper.myLooper();        notifyAll();    }    Process.setThreadPriority(mPriority);    onLooperPrepared();    Looper.loop();    mTid = -1;}

从源码实现看,他和普通Thread有显著的不同,普通Thread主要是执行一个耗时任务,而HandlerThread在内存创建了消息队列,外界需要通过Handler的消息方式来通知HandlerThread执行一个具体的任务。

注意到

notifyAll();

这是由于线程同步问题的存在。

Handler handler1 = new Handler(handlerThread.getLooper(), new Handler.Callback() {
public Looper getLooper() {    if (!isAlive()) {        return null;    }        // If the thread has been started, wait until the looper has been created.    synchronized (this) {        while (isAlive() && mLooper == null) {            try {                wait();            } catch (InterruptedException e) {            }        }    }    return mLooper;}

在创建Handler 传参getLooper()时,如果线程存活并且mLooper ==null表示HandlerThread的run方法还没有执行完,Looper对象还是null,所以进入等待状态,当run方法执行完时,mLooper !=null,可以由源码看到执行了notifyAll()方法来唤醒线程,来完成Handler的创建。

Handler 引发的内存泄漏

    Handler handler = new Handler(new Handler.Callback() {        @Override        public boolean handleMessage(Message msg) {            return false;        }    });

使用内部类包括匿名类来创建Handler的时候Handler对象会隐式的持有Activity的引用。
Handler通常伴随着一个耗时的后台线程一起出现,这个后台线程执行完毕后发送消息去更新UI,当Activity不在使用,它就有可能在GC检查时被回收掉,但是由于线程尚未执行完,而该线程持有Handler的引用,这个Handler又持有Activity的引用,就导致该Activity无法被回收。

另外如果执行了postDelayed方法,那么在设定的delay到达之前,会有一条 MessageQueue -> Message -> Handler -> Activity 的链,导致你的Activity被持有引用而无法回收。

解决:弱引用。

    static class MyHandler extends Handler {        WeakReference<Activity> weakReference;        MyHandler(Activity activity) {            weakReference = new WeakReference<>(activity);        }        @Override        public void handleMessage(Message msg) {            Activity activity = weakReference.get();            if (activity != null) {                // 处理activity的UI            }        }    }

使用内部静态类,这样内部类就不会在持有外部类即Activity的引用,所以不会导致外部类实例的内存泄漏。

参考
《Android 开发艺术探索》
https://www.jianshu.com/p/377bb840802f
https://blog.csdn.net/ly502541243/article/details/52062179
https://blog.csdn.net/qq_30379689/article/details/53394061
https://www.jianshu.com/p/f0b23ee5a922
https://www.cnblogs.com/leipDao/p/8005520.html

更多相关文章

  1. 彻底解决Android 应用方法数不能超过65K的问题
  2. Android异步更新UI-线程池-Future-Handler实例分析
  3. android 获取路径目录方法以及判断目录是否存在,创建目录
  4. 【Android 开发】:Android五种布局的使用方法
  5. Android 进程和线程模型
  6. Android Handler 线程消息机制
  7. 【Android】源码分析 - Handler消息机制再梳理
  8. Android中的gen文件为空或者不存在的处理方法
  9. Android——进程与线程

随机推荐

  1. Android(安卓)图片加载Bitmap OOM错误解
  2. android与H5混合开发
  3. android之单元测试――上
  4. [置顶] Unity与Android交互-android的安
  5. Android(安卓)Makefile分析
  6. 浙大网新仿真实训android培训好不好
  7. 献给android原生应用层开发初学者技术架
  8. Android优缺点
  9. 学生怎么入门Android?这四点很重要!
  10. Android撬动IT市场的新支点