Handler学习笔记

Handler学习笔记

概要:Handler就是我们常说的消息管理机制,它是实现线程间通信的方式,它的内部原理主要是利用内存共享来达到线程通信的。

应用启动了解

在学习Handler前,我们先了解一下Android应用的启动:
整个手机屏幕相当于一个lancher(app)桌面应用程序,点击屏幕中的其他应用图标就相当于与点击lancher中的某个控件,就会响应进行zygote操作,zygote就会给当前需要运行的应用fork一个进程同时分配一个jvm,然后这个jvm就会启动ActivityThread.java 中的main()函数。

zygote 为什么要给每个应用都分配一个jvm:目的是为了实现每个应用间的隔离,应用间相互独立互不影响,当一个应用崩溃了不会影响到其他应用的正常运行。

public final class ActivityThread {
	......
public static void main(String[] args) {
   	......
    Environment.initForCurrentUser();//初始化环境
	.....
    Looper.prepareMainLooper();//准备主线程的Looper

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

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

从上面代码可以看出一个应用的主线程的Handler机制中的Looper循环维持着整个应用的运行,从Looper.loop()方法的源码中可以看出里面维持着一个死循环操作,不断的从MessageQueue中拿出message,再由handler来进行分发处理。

public final class Looper {
	......
	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();
	    //开始循环
    	for (;;) {
        Message msg = queue.next(); // might block
        if (msg == null) {
	     //当msg为null时才会退出死循环
            // No message indicates that the message queue is quitting.
            return;
        }
	......
        try {
	    //开始分发消息
            msg.target.dispatchMessage(msg);
            end = (slowDispatchThresholdMs == 0) ? 0 : 							SystemClock.uptimeMillis();
        } finally {
            if (traceTag != 0) {
                Trace.traceEnd(traceTag);
            }
        }
      	.......
        msg.recycleUnchecked();
    	}
	}
	......
}

Handler 运行流程

在这里插入图片描述
在上面的机械运动示例图中,好像message 在运动传递;实际在Handler 机制中 Message 就是一块内存,内存是不区分线程的,所以Handler 才能在线程间(主线程与子线程、子线程与子线程)之间通过Message传递事务。

Handler消息入队流程

Handler调用post/sendMessage()方法------》
最终都会进入Handler.sendMessageAtTime()------》
接着调用Handler.enqueMessage()------》
最后执行MessageQueue.enqueueMessage()将消息放入MessageQueue消息队列中。
在这里插入图片描述

public class Handler {
	......
	//post方法
    public final boolean post(Runnable r){
       	return  sendMessageDelayed(getPostMessage(r), 0);
    }
    //send方法
    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);
    }
    //post/send 最终都会进入的方法
    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);
    }
}
public final class 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;
    }
}
Handler消息出队流程

Looper.loop()------》
在for循环中调用 MessageQueue.next()方法取出message------》
再由 Handler.dispatchMessage() 分发消息------》
最后执行Handler.handleMessage()处理消息。

public final class Looper {
	......
	    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();

        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 slowDispatchThresholdMs = me.mSlowDispatchThresholdMs;

            final long traceTag = me.mTraceTag;
            if (traceTag != 0 && Trace.isTagEnabled(traceTag)) {
                Trace.traceBegin(traceTag, msg.target.getTraceName(msg));
            }
            final long start = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            final long end;
            try {
            	//在此分发消息,msg.target 就是 handler
                msg.target.dispatchMessage(msg);
                end = (slowDispatchThresholdMs == 0) ? 0 : SystemClock.uptimeMillis();
            } finally {
                if (traceTag != 0) {
                    Trace.traceEnd(traceTag);
                }
            }
            if (slowDispatchThresholdMs > 0) {
                final long time = end - start;
                if (time > slowDispatchThresholdMs) {
                    Slog.w(TAG, "Dispatch took " + time + "ms on "
                            + Thread.currentThread().getName() + ", h=" +
                            msg.target + " cb=" + msg.callback + " msg=" + msg.what);
                }
            }

            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();
        }
    }
	......
}
public class Handler {
	......
	    public void dispatchMessage(Message msg) {
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //在此调用方法处理消息
            handleMessage(msg);
        }
    }
	......
}

Handler机制的组成部分

Message

Message消息是事务的载体,它可以通过 new Message() 与 Message.obtain() 方法进行实例化。

MessageQueue

用于存放Message的消息队列:它是是一个由单链表实现的优先级队列。结构如下:
在这里插入图片描述

Message ------>Next------>Message ----->Next …
Next是Message 中的一个属性,类型也是Message

存Message
public final class 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;
	}
}

enqueueMessage()示例:
在这里插入图片描述
在上面实例中在执行MessageQueue.enqueueMessage()中的for循环时,会从M1开始循环比较M1与M4的执行时间M1.time<M4.time,继续比较M2.time < M4.time, 再继续比较 发现 M3.time >M4.time , 因此可以得出 M4要晚于M2执行早于M3执行,所以将M4插入M2、M3之间。

取Message
public final class MessageQueue {
	Message next() {
    // Return here if the message loop has already quit and been disposed.
    // This can happen if the application tries to restart a looper after quit
    // which is not supported.
 	......
    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;
            }
            //锁结束的地方
	   .............
	}
}

Message.next() 示例:
在这里插入图片描述
在enqueueMessage()、next()方法中,存在:

 synchronized (this)

synchronized锁是一个内置锁,也就是由系统控制锁的lock unlock时机的。这个锁,说明的是对所有调用同一个MessageQueue对象的线程来说,他们都是互斥的,然而,在我们的Handler里面,一个线程是对应着一个唯一的Looper对象,而Looper中又只有一个唯一的MessageQueue(这个在下文中会有介绍)。所以,我们主线程就只有一个MessageQueue对象,也就是说,所有的子线程向主线程发送消息的时候,主线程一次都只会处理一个消息,其他的都需要等待,那么这个时候消息队列就不会出现混乱。

Looper

核心知识点:Looper的构造函数、Loooer.loop()方法、ThreadLocal。
Looper的构造函数是一个私有的构造函数,因此在外部无法直接通过new来实例化Looper;因此Looper提供了相应的Looper实例化的方法。

私有化构造函数:在构造Looper的同时,也创建了一个MessageQueue的实例。

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

主线程准备looper的方法:

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

子线程准备looper的方法:

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

在prepare()中发现是先从ThreadLocal中获取存储的looper 进行空判断,如果ThreadLocal中存在了looper再调用prepare()方法时会抛出异常,如果不存在则new一个looper放入ThreadLocal中。

通过比较发现主线程准备looper的方法中实则也是执行的prepare()方法。只是他们传入的参数不一样,主线程出入的是false ,通过梳理代码发现该参数会被传递进入MessageQueue中,在调用quit()方法时会用到。mQuitAllowed就是传入的参数。

 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);
        }
    }
public final class Looper {
	......
	    public void quit() {
        mQueue.quit(false);
    }
    ......
}

通过上面代码可以发现主线程是不能通过调用quit()方法来停止轮询的,否则会抛出 throw new IllegalStateException(“Main thread not allowed to quit.”)异常。

ThreadLocal

TreadLocal 多线程必须掌握的知识,是线程上下文的存储变量。

public class Thread implements Runnable {
	 /*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
	ThreadLocal.ThreadLocalMap threadLocals = null;
}

每一个线程中都有一个ThreadLocal.ThreadLocalMap threadLocals = null;这个成员变量,用于存放跟线程相关的上下文环境数据的。

一个线程如何保证其只有一个Looper且是不可变的啦?

public class Thread implements Runnable {
	/*
     * InheritableThreadLocal values pertaining to this thread. This map is
     * maintained by the InheritableThreadLocal class.
     */
     ThreadLocal.ThreadLocalMap threadLocals = null;
}
public final class Looper {
	......
	static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
    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 class ThreadLocal<T> {
    public void set(T value) {
    	//获取当前线程
        Thread t = Thread.currentThread();
        //获取当前线程的ThreadLocalMap 
        ThreadLocalMap map = getMap(t);
        if (map != null)
        //将looper存入ThreadLocalMap中,这里的this其实就是Looper类中的sThreadLocal 
            map.set(this, value);
        else
     		//如果线程的ThreadLocalMap 为空  则给该线程创建一个ThreadLocalMap 同时把looper 存入
            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();
    } 
    
    void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }   
    
    /**
    *ThreadLocalMap 它是ThreadLocal的一个静态内部类
    ***/
	static class ThreadLocalMap {
			//存数据方法
		   private void set(ThreadLocal<?> key, Object value) {
            // We don't use a fast path as with get() because it is at
            // least as common to use set() to create new entries as
            // it is to replace existing ones, in which case, a fast
            // path would fail more often than not.
            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();
        }
        //取数据方法
        private Entry getEntry(ThreadLocal<?> key) {
            int i = key.threadLocalHashCode & (table.length - 1);
            Entry e = table[i];
            if (e != null && e.get() == key)
                return e;
            else
                return getEntryAfterMiss(key, i, e);
        }
	}
 
}

从上面代码可以看出当我们Looper.prepare()的时候,会调用ThreadLocal的set()方法,这时会判断Thread中的ThreadLocalMap是否已经创建,没创建会调用ThreadLocal.createMap()进行创建(每一个线程Thread,都有一个自己的ThreadLocalMap),创建成功后就会将Looper存入到Thread中的 ThreadLocalMap中,存的这行代码很关键:

  map.set(this, value);

众所周知map.put(key1,value1),如果我们对相同的key1再次赋值map.put(key1,value2),则key1处的value1就会被value2覆盖。然而在这里this实际就是sThreadLocal

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

由于sThreadLocal是由static final 修饰的变量,它是全局不可变的,所以map.set(sThreadLocal ,value)中的key就是不可变的。既然key唯一了,那么可以继续对这个key设置value进行覆盖吗。答案是不可以的,因为在:

 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()从当前线程的ThreadLocalMap中通过唯一的key==sThreadLocal来拿一次Looper,如果获取到了Looper !=null,则会抛出throw new RuntimeException(“Only one Looper may be created per thread”)异常;如果发现ThreadLocalMap中没有存入Looper,这时才会new一个Looper存入当前线程中的ThreadLocalMap中。这样就保证了一个线程中只有一个Looper且是不可变的。
通俗点讲就是先限定ThreadLocalMap中的key唯一,然后如果这个key对应的value有值就抛出异常,没有就将Looper存入ThreadLocalMap。所以在线程中多次调用Looper.prepare()是会抛异常的。

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

在Looper的构造方法中,会创建一个MessageQueue,由于一个线程只有一个Looper所以Looper的构造方法在当前线程中只会被调用一次,所以MessageQueue也只有一个。如果一个线程中没有进行调用Looper.prepare()方法,则该线程中是不存在Looper和MessageQueue的。

同步屏障

屏障的意思即为阻碍,顾名思义,同步屏障就是阻碍同步消息,只让异步消息通过。
在Handler中,大致分为3种Message,分别是同步消息,异步消息和同步屏障。

public class Handler {
	......
	final boolean mAsynchronous;//异步开关
    public Handler() {
    	//通常new Handler() 默认false 表示不将消息设置为异步消息
        this(null, false);
    }
    public Handler(Callback callback) {
    	//通常new Handler() 默认false 表示不将消息设置为异步消息
        this(callback, false);
    }
    public Handler(Looper looper) {
    	//通常new Handler() 默认false 表示不将消息设置为异步消息
        this(looper, 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;
    }
    public Handler(Looper looper, Callback callback, boolean async) {
        mLooper = looper;
        mQueue = looper.mQueue;
        mCallback = callback;
        mAsynchronous = async;
    }
    private boolean enqueueMessage(MessageQueue queue, Message msg, long uptimeMillis) {
        msg.target = this;
        if (mAsynchronous) {
        	//设置Message为异步消息
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }
	......
}

同步消息:通过上面的代码可以看出,平时使用的消息是同步消息,因为Handler中传入的mAsynchronous为false。

异步消息:就是将mes.setAsynchronous(true)的消息称为异步消息,可以通过下面的方式设置异步消息:

HandlerHandler mHandler = new Handle(true)

同步屏障:虽然没有消息两字,但是同步屏障本质也是Message对象,同步屏障可以把它解释为:屏障同步消息的消息,它和前面同步、异步消息的最大区别就是,Message.target变量为null,它的作用就是在消息队列(MessageQueue)取出消息的时候,屏蔽掉同步消息,优先获取异步消息进行处理的作用。

  1. 如何开启同步屏障?
    在MessageQueue中有对应的开启同步屏障的方法:
    public int postSyncBarrier() {
        return postSyncBarrier(SystemClock.uptimeMillis());
    }

    private int postSyncBarrier(long when) {
        // Enqueue a new sync barrier token.
        // We don't need to wake the queue because the purpose of a barrier is to stall it.
        synchronized (this) {
            final int token = mNextBarrierToken++;
            //从消息池中获取Message
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;
			//就是这里!!!初始化Message对象的时候,并没有给target赋值,因此 target==null
            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                	//如果开启同步屏障的时间(假设记为T)T不为0,且当前的同步消息里有时间小于T,则prev也不为null
                    prev = p;
                    p = p.next;
                }
            }
            //根据prev是不是为null,将 msg 按照时间顺序插入到 消息队列(链表)的合适位置
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

可以看到,Message 对象初始化的时候并没有给 target 赋值,因此, target == null 的 来源就找到了。上面消息的插入也做了相应的注释。这样,一条 target == null 的消息就进入了消息队列。

  1. 那么,开启同步屏障后,所谓的异步消息又是如何被处理的呢?
public final class MessageQueue {
	......
	Message next() {
	......
    for (;;) {
        if (nextPollTimeoutMillis != 0) {
            Binder.flushPendingCommands();
        }
       ......
        synchronized (this) {
            // Try to retrieve the next message.  Return if found.
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;; //当前链表的头结点
	    	//target == null 表示开启了同步屏障 然后开始循环消息队列中的消息
	    	//如果消息是同步消息这继续循环,直到获取消息为异步消息,取出异步消息,跳出循环,然后处理异步消息。
            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());
            }
	......
       }
}
	......
}

从上面可以看出,当消息队列开启同步屏障的时候(即标识为 msg.target == null ),消息机制在处理消息的时候,优先处理异步消息。这样,同步屏障就起到了一种过滤和优先级的作用。

  1. 移除同步屏障
    在MessageQueue中也有对应的方法:
	//@param token The synchronization barrier token that was returned by{@link #postSyncBarrier}.
    public void removeSyncBarrier(int token) {
        // Remove a sync barrier token from the queue.
        // If the queue is no longer stalled by a barrier then wake it.
        synchronized (this) {
            Message prev = null;
            Message p = mMessages;
            while (p != null && (p.target != null || p.arg1 != token)) {
                prev = p;
                p = p.next;
            }
            if (p == null) {
                throw new IllegalStateException("The specified message queue synchronization "
                        + " barrier token has not been posted or has already been removed.");
            }
            final boolean needWake;
            if (prev != null) {
                prev.next = p.next;
                needWake = false;
            } else {
                mMessages = p.next;
                needWake = mMessages == null || mMessages.target != null;
            }
            p.recycleUnchecked();

            // If the loop is quitting then it is already awake.
            // We can assume mPtr != 0 when mQuitting is false.
            if (needWake && !mQuitting) {
                nativeWake(mPtr);
            }
        }
    }

传入的token调用postSyncBarrier()方法的返回值,也称为同步屏障令牌。

  1. 同步消息的应用场景
    似乎在日常的应用开发中,很少会用到同步屏障。那么,同步屏障在系统源码中有哪些使用场景呢?Android 系统中的 UI 更新相关的消息即为异步消息,需要优先处理。比如,在 View 更新时,draw、requestLayout、invalidate 等很多地方都调用了
    ViewRootImpl#scheduleTraversals() ,如下:
    void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            //开启同步屏障
            mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();
            //发送异步消息
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
            pokeDrawLockIfNeeded();
        }
    }

postCallback() 最终走到了 Choreographer.postCallbackDelayedInternal() :

    private void postCallbackDelayedInternal(int callbackType,
            Object action, Object token, long delayMillis) {
        if (DEBUG_FRAMES) {
            Log.d(TAG, "PostCallback: type=" + callbackType
                    + ", action=" + action + ", token=" + token
                    + ", delayMillis=" + delayMillis);
        }

        synchronized (mLock) {
            final long now = SystemClock.uptimeMillis();
            final long dueTime = now + delayMillis;
            mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token);

            if (dueTime <= now) {
                scheduleFrameLocked(now);
            } else {
                Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action);
                msg.arg1 = callbackType;
                //设置异步消息
                msg.setAsynchronous(true);
                mHandler.sendMessageAtTime(msg, dueTime);
            }
        }
    }

这里就开启了同步屏障,并发送异步消息,由于 UI 更新相关的消息是优先级最高的,这样系统就会优先处理这些异步消息。
最后,当要移除同步屏障的时候需要调用 ViewRootImpl#unscheduleTraversals()方法:

    void unscheduleTraversals() {
        if (mTraversalScheduled) {
            mTraversalScheduled = false;
           //移除同步屏障
            mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);
            mChoreographer.removeCallbacks(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
        }
    }

同步屏障总结:同步屏障的设置可以方便地处理那些优先级较高的异步消息。当我们调用Handler.getLooper().getQueue().postSyncBarrier() 并设置消息的setAsynchronous(true) 时,target 即为 null ,也就开启了同步屏障。当在消息轮询器 Looper 在 loop() 中循环处理消息时,如若开启了同步屏障,会优先处理其中的异步消息,而阻碍同步消息。

HandlerThread

Handy class for starting a new thread that has a looper. The looper can then be used to create handler classes. Note that start() must still be called.
很方便的类,用于启动具有循环程序的新线程。 然后可以使用循环程序创建处理程序。 请注意,仍必须调用start()。

public class HandlerThread extends Thread {
	......
    @Override
    public void run() {
        mTid = Process.myTid();
        //准备Looper
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        //开启loop循环
        Looper.loop();
        mTid = -1;
    }
    ......
}

从上面代码可以看出HandlerTread在期内部run()方法中已经创建了looper并开启了循环程序。
HandlerThread存在的意义:
1.方便使用:a.方便初始化,b.方便获取子线程的Looper;
2.为了保证线程的安全。

我们先来看看常规Thread的可能出现的问题:

class  MyThread extends Thread{
    public Looper getLooper() {
        return looper;
    }
    Looper looper;
    @Override
    public void run() {
        super.run();
        Looper.prepare();
        looper = Looper.myLooper();
        //在子线程内部创建了一个子线程的handler
        Handler handler = new Handler(looper);
        Looper.loop();
    }
}
private void test(){
    MyThread myThread = new MyThread();
    //开启线程
    myThread.start();
    //获取子线程的looper
    Looper looper = myThread.getLooper();
    //创建子线程的handler
    Handler threadHandler = new Handler(looper);
}

执行上面的test()方法,当new Handler(looper)时可能会因为looper为空而抛出异常。为什么会出现这种情况啦,是因为子线程myThread.start()的时候 myThreadd的run()方法是异步的,然而new Handler(looper)则是同步的,这就有可能导致myThread线程中的looper还未创建所以传入Handler的looper为空。

那么HandlerThread是如何避免这个问题的啦?

public class HandlerThread extends Thread {
	......
    @Override
    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll();
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }
    //获取looper
    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;
    }
    ......
}

在上面代码中如果先调用getLooper()当进入synchronized内的代码,发现mLooper为null首先会执行wait()等待, wait()方法会释放synchronized所持有的锁,此时run()方法执行了,然后run()方法中的synchronized锁住其内部代码,执行到notifiAll()会唤醒等待,但是notifiAll()不会释放锁这时getlooper()的循环还是不能立刻执行,需要等到run()方法中的synchronized代码块执行完了以后才能执行,因此就保证了looper是一定能获取的。

IntentService

IntentService:也是一种service,一般用于后台处理耗时任务。

public abstract class IntentService extends Service {
    private volatile Looper mServiceLooper;
    private volatile ServiceHandler mServiceHandler;
    private String mName;
    private boolean mRedelivery;
    //内部类ServiceHandler 
    private final class ServiceHandler extends Handler {
        public ServiceHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
        	//5.就会调用接收message, 在onHandleIntent()中处理自己的逻辑(如耗时操作等)
            onHandleIntent((Intent)msg.obj);
            //6.自动停止service
            stopSelf(msg.arg1);
        }
    }

    public IntentService(String name) {
        super();
        mName = name;
    }

    public void setIntentRedelivery(boolean enabled) {
        mRedelivery = enabled;
    }

    @Override
    public void onCreate() {
        super.onCreate();
        //1.创建一个子线程 由于使用的是HandlerThread 所以内部自动生成了looper,并且调用了Looper.loop()
        HandlerThread thread = new HandlerThread("IntentService[" + mName + "]");
        thread.start();
        //2.获取子线程的looper
        mServiceLooper = thread.getLooper();
        //3.创建一个属于子线程的 handler
        mServiceHandler = new ServiceHandler(mServiceLooper);
    }
	//4.当调用 service的onStart()生命周期方法时,就会自动通过子线程的handler向子线程发送一个message
    @Override
    public void onStart(@Nullable Intent intent, int startId) {
        Message msg = mServiceHandler.obtainMessage();
        msg.arg1 = startId;
        msg.obj = intent;
        mServiceHandler.sendMessage(msg);
    }

    @Override
    public int onStartCommand(@Nullable Intent intent, int flags, int startId) {
        onStart(intent, startId);
        return mRedelivery ? START_REDELIVER_INTENT : START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        mServiceLooper.quit();
    }

    @Override
    @Nullable
    public IBinder onBind(Intent intent) {
        return null;
    }

    @WorkerThread
    protected abstract void onHandleIntent(@Nullable Intent intent);
}

Service的生命周期是由后台调度的,在IntentService的onCreate()方法中创建了一个异步线程HandlerTread同时开启它,并在onStart()方法中向异步线程发送一个Message消息,然后通过异步线程的handleMessage()方法调用onHandleIntent()方法处理耗时操作。onHandleIntent是一个抽象方法,在new IntentService时需要实现该方法。IntentService还有一个好处是:在handleMessage中处理完onHandleIntent()中的耗时任务后,会执行stopSelf()方法来自动停止Service,从而达到内存的自动释放。

IntentService的应用场景:
有一项任务分成几个子任务,子任务按顺序先后执行,子任务全部执行完后,这项任务才算成功。
方式一:可以用多个线程来执行 一个线程执行完—>下一个线程---->下一个线程—>…
方式二:就采用IntentService来完成,IntentService能够很好的管理线程,保证只有一个子线程处理工作,而且是一个一个的完成任务,按顺序执行的,因为其内部是由一个HandlerThread的MessageQueue来保证任务顺序的执行。

public void onClick(View view) {
    //请求1
    Intent intent1 = new Intent("cn.service");
    intent1.setPackage(getPackageName());
    Bundle bundle1 = new Bundle();
    bundle1.putString("Flag","one");
    intent1.putExtras(bundle1);
    startService(intent1);
    //请求2
    Intent intent2 = new Intent("cn.service");
    intent2.setPackage(getPackageName());
    Bundle bundle2 = new Bundle();
    bundle2.putString("Flag","two");
    intent2.putExtras(bundle2);
    startService(intent2);
}
public class MyIntentService extends IntentService {
    private static final String TAG = "MyIntentService";
    public MyIntentService() {
        super("myIntentService");
    }
    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        Bundle extras = intent.getExtras();
        String flag = extras.getString("Flag");
       switch (flag){
           case "one":
               Log.e(TAG,"do  task1");
               break;
           case "two":
               Log.e(TAG,"do  task2");
               break;
       }
    }
}

在这里插入图片描述

Handler机制常见问题

1. 一个线程可以有多少个Handler?
答:理论上可以有无数个,因为可以无限的new Handle,实际要受内存的限制。
2. Handler内存泄露原因?为什么其他的内部类没有说过有这个问题?
答:从生命周期来考虑这个问题。
	Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        super.handleMessage(msg);
        onclick(view); // ①
        MainActivity.this.onclick(view);}
};
private void onclick(View view) {
}

上述代码中①、②处的代码是等价的,说明 handler(非静态内部类)内部自动持有了外部的MainActivity类,从生命周期来说,如果handler不销毁这MainActivity就没法被销毁。
Handler 代码中:

public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
    MessageQueue queue = mQueue;
	........
   rn 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);
}

Msg.target = this; this就是handler,说明Message中持有了Handler的引用,如果给Message加上了延迟时间,就会导致Message持有的handler引用的生命周期变长,进而导致handeler持有的Activity的引用的生命周期变长,如果此时Activity finish掉了,由于handler中还有未处理的message,同时handler又持有了Activity实例的引用,这样GC垃圾回收机制就不会去回收这个Activity,进而导致activity泄露。
可以将Handler定义为静态内部类,因为静态内部类不会持有外部类的应用;如果需要使用外部类的成员,可以用WeakReference 包裹外部类的对象将外部类对象传入Handler。

至于其他内部类为什么没有没有内存泄露的问题:是因为其他内部类的生命周期维持着和外部类的生命周期一样。比如RecyclerView ---->Adapter—>ViewHolder。

3. 为什么主线程可以new Handler? 如果想要在子线程中new Handler 要做些什么准备?

答:

public Handler(Callback callback, boolean async) {
	.......
      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;
}

我们从Handler的构造方法中可以看出,在new Handler 之前需要有一个looper,否则会抛出异常。
在ActivityThread#main()方法中可以知道

ublic static void main(String[] args) {
	......
	Looper.prepareMainLooper();
	.......
	Looper.loop();
	.......
}

此时在主线程中已经创建了Looper,所以在主线程中可以直接进行new Handler() 的操作。因此在子线程中我们想要new Handler()就必要先要调用Looper.prepare()方法为子线程创建一个唯一的Looper,同时还要进行Looper.loop()操作。
注意:通过代码实践发现部分华为手机在子线程中不调用Looper.prepare()也能直接进行new Handler()操作,猜测华为系统对Android源码进行了改动,当子线程没有looper时,系统内部会创建一个looper。

4. 子线程中维护的Looper,消息队列无消息的时候的处理方案是什么?有什么用?
答:当消息队列中无消息的时候,应该让Looper退出loop()方法,那么如何让Looper退出loope()方法,进行下面的分析:<font color = "#ff000">nativePollOnce()、nativeWake()为底层方法。</font>
    Message next() {
        // Return here if the message loop has already quit and been disposed.
        // This can happen if the application tries to restart a looper after quit
        // which is not supported.
        final long ptr = mPtr;
        if (ptr == 0) {
            return null;
        }

        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 (false) Log.v("MessageQueue", "Returning message: " + msg);
                        return msg;
                    }
                } else {
                    // No more messages.
                    nextPollTimeoutMillis = -1;
                }

                // Process the quit message now that all pending messages have been handled.
                if (mQuitting) {
                    dispose();
                    return null;
                }

                // If first time idle, then get the number of idlers to run.
                // Idle handles only run if the queue is empty or if the first message
                // in the queue (possibly a barrier) is due to be handled in the future.
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

                if (mPendingIdleHandlers == null) {
                    mPendingIdleHandlers = new IdleHandler[Math.max(pendingIdleHandlerCount, 4)];
                }
                mPendingIdleHandlers = mIdleHandlers.toArray(mPendingIdleHandlers);
            }

            // Run the idle handlers.
            // We only ever reach this code block during the first iteration.
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
                try {
                    keep = idler.queueIdle();
                } catch (Throwable t) {
                    Log.wtf("MessageQueue", "IdleHandler threw exception", t);
                }

                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            // Reset the idle handler count to 0 so we do not run them again.
            pendingIdleHandlerCount = 0;

            // While calling an idle handler, a new message could have been delivered
            // so go back and look again for a pending message without waiting.
            nextPollTimeoutMillis = 0;
        }
    }

首先我们看看如果队列中无消息的时候会造成怎样的后果:
a.Message msg = mMessages;mMessages为空,所以拿到的msg也为空;
b.代码就会走到nextPollTimeoutMillis = -1;
c.就会执行到continue,继续下一次循环;

                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    // No idle handlers to run.  Loop and wait some more.
                    mBlocked = true;
                    continue;
                }

d.回到for循环体顶部,当执行到 nativePollOnce(ptr, nextPollTimeoutMillis);第2步中nextPollTimeoutMillis=-1,会导致一直阻塞在nativePollOnce里;
e.所以当子线程的消息队列中无消息时会在Looper.loop()方法中的Message msg = queue.next(); 处阻塞着。

为了退出这个阻塞,并让next()方法返回null啦,源码中有这样一段代码

                if (mQuitting) {
                    dispose();
                    return null;
                }

程序在什么地方会赋值mQuitting=true啦,继续追踪发现:MessageQueue#quit()

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

在上面的代码中 mQuitting = true,同时同时调用removeAllMessagesLocked()清理消息,并且nativeWake(mPtr)方法会使nativePollOnce(ptr, nextPollTimeoutMillis);跳出阻塞继续往下走,再次走到

                if (mQuitting) {
                    dispose();
                    return null;
                }

此时mQuitting=ture,则next就会返回null,从而跳出Looper.loop()循环。
继续寻找调用MessageQueue#quit()方法的地方,发现:Looper#quit()

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

因此当子线程消息队列中无消息的时候,应该让Looper退出loop()方法,退出的措施是调用Looper.quit();

上面的分析流程也适用于延时Message的阻塞问题。
nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
当nativePollOnce(ptr, nextPollTimeoutMillis)阻塞时间到达后就会继续往下执行。

5. MessageQueue 消息队列的阻塞问题?(分析流程与第4问一样)
答:一般的生产者-消费者模式中,经常会遇到入队列和出队列的阻塞问题,但是MessageQueue是有点特殊的生产者-消费者模式。即在MessageQueue.enqueue()时不对加入消息数量做限制,理论上可以无限加,实际受限于存储空间。为什么不设置加入限制啦?是因为系统的消息也是通过这个方法加入的。然后看看MessageQueue.next()会出现的阻塞:
①要取的第一个消息还没到执行时间  (到时间自动唤醒);
②消息队列中已经没有消息,无限等待 
//传入的timeoutMillis 如果为-1 表示无限等待,直到有事件发生为止;如果为0表示无需等待
private native void nativePollOnce(long ptr, int timeoutMillis);
Message next() {
	......
    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) {
		//消息队列有消息
                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.
		//消息队列没有消息了,则为-1 表示一直等待
                nextPollTimeoutMillis = -1;
            }

 // Process the quit message now that all pending messages have been handled.
            if (mQuitting) {
                dispose();
                return null;
            }
            ......
             if (pendingIdleHandlerCount <= 0) {
                // No idle handlers to run.  Loop and wait some more.
                mBlocked = true;
                continue;
             }
	......
    }
}

1)当消息未到执行时间的情况,会获取等待时间然后调用nativePollOnce(ptr, nextPollTimeoutMillis)进行等待,时间到达后自动唤醒;
2)2.当消息队列中没有消息时,nextPollTimeoutMillis = -1; 此时在调用
nativePollOnce(ptr, nextPollTimeoutMillis);则会一直等待,直到有事件发生为止,如果某一刻有新的消息加入则会调用enqueueMessage()方法中的nativeWake()方法唤醒。

6. 既然可以存在多个Handler往MessageQueue中添加数据(发送消息时各个Handler可能处于不同线程),那它如何确保线程安全的?

答:Snchronized:内置锁(上锁、解锁)都是由JVM完成。在MessageQueue中enqueueMessage()、next()、quit()中都有synchronized(this){}锁住当前类,因此在执行enqueueMessage()、next()、quit()时MessageQueue中的其他函数都处于等待状态。并且一个线程只有一个MessageQueue,在配合着MessageQueue 中的调用方法中添加了锁,因此能够保证线程的安全。

类锁

MessageQueue mq1 = new MessageQueue;
mq1.enqueueMessage();
MessageQueue mq2 = new MessageQueue;
mq2.enqueueMessage();
Synchronized(MessageQueue.this){}

如果正在调用mq1.enqueueMessage();则mq2.enqueueMessage();只能等待着mq1执行完后释放了锁后,才能执行。

对象锁:

MessageQueue mq1 = new MessageQueue;
mq1.enqueueMessage();
mq1.next();
MessageQueue mq2 = new MessageQueue;

mq2.enqueueMessage();

Synchronized(mq1){}

mq1.enqueueMessage()、mq2.enqueueMessage()的执行护不影响,但是mq1.next();只能等待着mq1执行完后释放了锁后,才能执行。

7.主线程中能执行Looper.quit()方法吗?

答:不能在主线程中执行quit()方法,会抛出异常:

void quit(boolean safe) {
    if (!mQuitAllowed) {
       throw new IllegalStateException("Main thread not allowed to quit.");
    }
	......
}
8.我们使用Message时应该如何创建它?

答:通过new Message()或者Message.obtain().(享元设计模式)内存复用。
为什么要obtain啦?因为在Looper.loop()中有回收的代码:

public static void loop() {
    ......
        msg.recycleUnchecked();
   
}

Message#recycleUnchecked()

void recycleUnchecked() {
    // Mark the message as in use while it remains in the recycled object pool.
    // Clear out all other details.
    flags = FLAG_IN_USE;
    what = 0;
    arg1 = 0;
    arg2 = 0;
    obj = null;
    replyTo = null;
    sendingUid = -1;
    when = 0;
    target = null;
    callback = null;
    data = null;

    synchronized (sPoolSync) {
        if (sPoolSize < MAX_POOL_SIZE) {
            next = sPool;
            sPool = this;
            sPoolSize++;
        }
    }
}

在Looper#loop()中会将处理了的message的内部数据全部置空,然后在sPool前加入该Message,这样回收的Message就维持了一个池子。那么为什么要这样处理啦?目的就是为了避免内存抖动,防止OOM. 原因是在一块完整的内存中通过new就会不断地去扣一块对应的内存,虽然使用后会释放但是会出现内存碎片,Android事务的处理都是基于message的传递 如果大量的消息全部都通过new, 则频繁创建的对象和频繁的GC回收对象会导致内存抖动(已用内存忽高忽低),而且GC回收后可能会产生内存碎片,如果这时其他线程需要申请大块内存但是又找不到就可能发生OOM。

9.Looper的死循环中Block(阻塞)为什么不会导致应用卡死?

答:Looper死循环和应用ANR毫不相关的两个问题。Looper循环是从MessageQueue中拿Message给程序处理;ANR是程序处理不能在有效时间内给出响应。它们是两个独立的过程Looper循环拿消息,至于拿不拿得到、要等多久能拿到,与程序处理而未响应(ANR)一点关系都没有。
例如:点击事件5s没处理:其实点击事件最终也会转换为Message, 如果处理这个Message的时间超过5s,就会使用handler发送一个超时的ANR的Message提醒。

以上是对Handler的学习和简单理解,如有错误之处还请谅解。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值