【源码记录】Handler机制

一、Looper


1、sThreadLocal

    //【分析点1】
    //为了实现线程隔离而使用ThreadLocal
    static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

2、Looper

私有构造函数,外部初始化Looper只能通过prepare方法

    //【分析点2】
    //构造函数是内部私有的,也就是说外部是不可调用构造函数的
    //这就决定了每个Looper只会对应一个MessageQueue
    private Looper(boolean quitAllowed) {
        //创建MessageQueue
        mQueue = new MessageQueue(quitAllowed);
        //当前线程
        mThread = Thread.currentThread();
    }

3、prepare

普通线程构造Looper的方式

    //这个才是真正的初始化方法
    private static void prepare(boolean quitAllowed) {
        //【分析点3】
        //ThreadLocal的get方法获取线程的方式:
        // Thread t = Thread.currentThread();
        //由此可见取的是当前线程内的ThreadLocalMap
        //ThreadLocal就是通过这种方式来实现线程隔离
        if (sThreadLocal.get() != null) {
            //所以这里如果不为空,就说明这个Looper被绑定了
            //抛出错误
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //将Looper存进ThreadLocal
        sThreadLocal.set(new Looper(quitAllowed));
    }

4、prepareMainLooper

主线程构造Looper的方式,区别在于quitAllowed参数为false

    //【分析点4】
    //这是给主线程调用的初始化方法
    //主线程和普通线程Looper的最大差别就是
    //主线程的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();
        }
    }

5、loop(重要)

Looper关键方法

    public static void loop() {
        final Looper me = myLooper();
        
        ...
		
		//【分析点5】
        //通过for循环从MessageQueue的next方法里面取消息
        //可以看到这是一个死循环,也就是Android程序为什么不像普通Java程序那样运行完就退出的原因
        for (;;) {
        	//【分析点6】
            //调用MessageQueue的next方法
            //这里要留意的是,我们平常说的阻塞并不是在
            //Looper的loop方法里面,而是在queue的next方法里面,
            //这个后续会讲到
            Message msg = queue.next(); 
            if (msg == null) {
                return;
            }

            //【分析点7】
            //这是给BlockCanary之类性能检查工具用来检测卡顿的
            //原理就是通过在dispatch前后做打印Log,从而得到
            //处理一个消息的时间
            //与此类似的性能检测方案还有监听Choreographer的帧率
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " +
                        msg.callback + ": " + msg.what);
            }

            ...
            
            
            try {
	            //【分析点8】
	            //分发方法
	            //这个target其实就是在Handler的enqueueMessage里面
	            //Handler将自己传给Message
	            //这里也是Handler会内存泄漏的原因
	            //通过Handler.dispatch方法
	            //从而调用到Runnable.run / Callback.handleMessage / handleMessage
                msg.target.dispatchMessage(msg);
                
            } catch (Exception exception) {
                ...
            } finally {
                ...
            }
            
			//对应【分析点7】
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

            
            //【分析点8】
            //我们用Message通常都是通过obtain方法
            //就是因为Meesage内部持有一个长度为50的消息缓存池(其实是单链表)
            //这里调用回收方法
            msg.recycleUnchecked();
        }
    }



二、MessageQueue


1、native method

C++层面的方法

	//【分析点9】
	//这些都是c++层面的方法,我通过AndroidXRef找到了源码
    private native static long nativeInit();
    private native void nativePollOnce(long ptr, int timeoutMillis); 
    private native static void nativeWake(long ptr);
    private native static void nativeDestroy(long ptr);
    private native static boolean nativeIsPolling(long ptr);
    private native static void nativeSetFileDescriptorEvents(long ptr, int fd, int events);

1.1、nativeInit

创建mWakeEventFd文件描述符,并通过epoll_ctl方法监听文件描述符

	
	//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp	

	static jlong android_os_MessageQueue_nativeInit(JNIEnv* env, jclass clazz) {
		//在这里我们可以看到,NativeMessageQueue是Message在cpp层的具体实现
		//接着我们看看NativeMessageQueue的构造函数
	    NativeMessageQueue* nativeMessageQueue = new NativeMessageQueue();
	    ...
	    nativeMessageQueue->incStrong(env);
	    return reinterpret_cast<jlong>(nativeMessageQueue);
	}

	//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp

	NativeMessageQueue::NativeMessageQueue() :
	        mPollEnv(NULL), mPollObj(NULL), mExceptionObj(NULL) {
	    //可以看到,不止是MessageQueue,Looper也是有对应实现的
	    //继续看Looper的实现
	    mLooper = Looper::getForThread();
	    if (mLooper == NULL) {
	        mLooper = new Looper(false);
	        Looper::setForThread(mLooper);
	    }
	}

	//源码位置:/system/core/libutils/Looper.cpp
	
	Looper::Looper(bool allowNonCallbacks) :
	        mAllowNonCallbacks(allowNonCallbacks), mSendingMessage(false),
	        mPolling(false), mEpollFd(-1), mEpollRebuildRequired(false),
	        mNextRequestSeq(0), mResponseIndex(0), mNextMessageUptime(LLONG_MAX) {
	    //【分析点10】
	    //调用eventfd返回一个文件描述符,专门用于事件通知。

		...
	    //进入rebuildEpollLocked
	    rebuildEpollLocked();
	}

	//源码位置:/system/core/libutils/Looper.cpp
	
	void Looper::rebuildEpollLocked() {
		//【分析点11】
		//终于看到epoll了
		//这里做了:
		//1、通过epoll_create创建 epoll 对象
		//2、调用epoll_ctl将mWakeEventFd设置为要监听的文件描述符
	    mEpollFd = epoll_create(EPOLL_SIZE_HINT);

		...	   

	    int result = epoll_ctl(mEpollFd, EPOLL_CTL_ADD, mWakeEventFd, & eventItem); 
	    ...
	}


1.2、nativePollOnce

通过epoll_wait进入休眠状态,同时释放CPU

	
	//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp	
	
	static void android_os_MessageQueue_nativePollOnce(JNIEnv* env, jobject obj,
	        jlong ptr, jint timeoutMillis) {
	    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
	    //主要还是通过调用nativeMessageQueue的方法
	    nativeMessageQueue->pollOnce(env, obj, timeoutMillis);
	}

	//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp	

	void NativeMessageQueue::pollOnce(JNIEnv* env, jobject pollObj, int timeoutMillis) {
	    //最终还是调用Looper的pollOnce
	    mLooper->pollOnce(timeoutMillis);
	    ...
	}

	//源码位置:/system/core/libutils/Looper.cpp
	
	int Looper::pollOnce(int timeoutMillis, int* outFd, int* outEvents, void** outData) {
    for (;;) {
        ...
        //调用pollInner
        result = pollInner(timeoutMillis);
    }

	//源码位置:/system/core/libutils/Looper.cpp

	int Looper::pollInner(int timeoutMillis) {
	    ...
	    //【分析点12】
	    //终于调用epoll的方法epoll_wait
	    //让出CPU,并让线程进入休眠
	    int eventCount = epoll_wait(mEpollFd, eventItems, EPOLL_MAX_EVENTS, timeoutMillis);
	    ...
    }
}

1.3、nativeWake

通过对mWakeEventFd文件描述符发起写操作,从而唤醒线程


	//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp	

	static void android_os_MessageQueue_nativeWake(JNIEnv* env, jclass clazz, jlong ptr) {
	    NativeMessageQueue* nativeMessageQueue = reinterpret_cast<NativeMessageQueue*>(ptr);
	    //一样的套路,最终都是调用到Looper的方法
	    nativeMessageQueue->wake();
	}

	//源码位置:/frameworks/base/core/jni/android_os_MessageQueue.cpp

	void NativeMessageQueue::wake() {
	    mLooper->wake();
	}

	//源码位置:/system/core/libutils/Looper.cpp

	void Looper::wake() {
	    uint64_t inc = 1;
	    //【分析点13】
	    //通过对nativeInit创建的mWakeEventFd发起一个写操作
	    //从而唤醒nativePollOnce中调用epoll_wait方法的线程
	    ssize_t nWrite = TEMP_FAILURE_RETRY(write(mWakeEventFd, &inc, sizeof(uint64_t)));
	    ...
	}

2、MessageQueue

MessageQueue构造函数

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

3、next(重要)

真正阻塞的方法,主要做了几件事情:

  • 判断是否同步屏障消息,如果是,则找出最近的一条异步消息
  • 如果消息还没到时间执行,则设置nextPollTimeoutMillis,让下次进入next方法时阻塞
  • 如果已经可以执行了,就直接return这条消息
  • 如果没有消息,进入永久阻塞,直到被唤醒
  • 处理闲时任务
    Message next() {
		//【分析点14】
		//next方法主要有几大逻辑:
		//1、判断是否同步屏障消息,如果是,则找出最近的一条异步消息
		//2、如果消息还没到时间执行,则设置nextPollTimeoutMillis,让下次进入next方法时阻塞
		//如果已经可以执行了,就直接return这条消息
		//3、如果没有消息,进入永久阻塞,直到被唤醒
		//4、处理闲时任务

        ...

        int nextPollTimeoutMillis = 0;
        //【分析点15】
        //跟Looper的loop一样,这里也是一个无限循环
        for (;;) {
            
            //【分析点16】
            //1、这里表明所有的消息都已经处理完了,在等待新的消息进入,如果
            //nextPollTimeoutMillis参数为-1,则代表无限期的阻塞,直到被唤醒
            //2、这里也就是真正阻塞消息的地方
            //nativePollOnce是一个IO多路复用机制
            //IO多路复用可以简单地理解为用一个线程去监视多个文件句柄(Linux下一切皆文件)
            //只有这个文件句柄就绪,才会被唤醒,否则这个文件句柄会交出CPU并阻塞程序
            nativePollOnce(ptr, nextPollTimeoutMillis);

			
            synchronized (this) {
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                //【分析点17】
                //这里判断通过target是否为空,判断是否同步屏障消息,
                //如果是,通过while循环找到下一条 异步 消息
                if (msg != null && msg.target == null) {
                    
                    do {
                        prevMsg = msg;
                        msg = msg.next;
                    } while (msg != null && !msg.isAsynchronous());
                }
                if (msg != null) {
                    //【分析点18】
                    //如果还没到时间执行这条消息,则进入有时间
                    //的阻塞(也就是等待)
                    if (now < msg.when) {
                        nextPollTimeoutMillis = 
                        		(int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                    	//如果没有设置延时,那就将msg return出去
                        mBlocked = false;
                        if (prevMsg != null) {
                            prevMsg.next = msg.next;
                        } else {
                            mMessages = msg.next;
                        }
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    }
                } else {
                    //【分析点19】
                    //msg == null,也就是说已经没有消息了
                    //进入永久阻塞,直到被nativeWake唤醒
                    nextPollTimeoutMillis = -1;
                }

                //【分析点20】
                //调用quit的时候,mQuitting才会为true
                //这里如果返回null,也就是next返回null,会导致
                //Looper结束loop
                //所以Looper的关闭流程分两个:
                //1、调用Looper.quit / Looper.quitSafely,导致next的时候MessageQueue返回一个
                //null消息
                //2、Looper.loop发现msg==null,就会return,退出loop方法
                if (mQuitting) {
                    dispose();
                    return null;
                }

				//【分析点21】
				//处理闲时任务
                //如果等待执行中的闲时任务没有了,那就取出闲时
                //任务handler中的任务
                if (pendingIdleHandlerCount < 0
                        && (mMessages == null || now < mMessages.when)) {
                    pendingIdleHandlerCount = mIdleHandlers.size();
                }
                if (pendingIdleHandlerCount <= 0) {
                    mBlocked = true;
                    continue;
                }

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

            //遍历所有闲时任务
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; 

                boolean keep = false;
                try {
                    //【分析点22】
                    //这里要注意,执行闲时任务的时候,因为这是在主线程,所以不能太
                    //耗时,避免影响到下一条消息
                    keep = idler.queueIdle();
                }

                if (!keep) {
                    synchronized (this) {
                        //每执行完一条,就删除一条
                        mIdleHandlers.remove(idler);
                    }
                }
            }

            pendingIdleHandlerCount = 0;

            //阻塞时间改为0,即不阻塞
            //但是事实上阻不阻塞,重新进入方法后会再决定
            nextPollTimeoutMillis = 0;
        }
    }

4、quit

退出方法,主要分安全退出非安全退出
退出流程:将mQuitting设置为true,等到Looper下次调用next的时候,next方法发现mQuittingtrue,就会给Looper返回一个null的信息,Looper接收到null消息就会调用return退出for循环

    void quit(boolean safe) {
    	//【分析点18】
    	//主线程是不允许退出的
        if (!mQuitAllowed) {
            throw new IllegalStateException("Main thread not allowed to quit.");
        }

        synchronized (this) {
            if (mQuitting) {
                return;
            }
            mQuitting = true;

			//【分析点19】
            //安全与非安全的区别就在于
            //安全:先清除还没到执行时间的消息,会等当前正在要取出
            //的消息执行完
            //非安全:一律清除

            if (safe) {
                //安全退出
                removeAllFutureMessagesLocked();
            } else {
                //非安全退出
                removeAllMessagesLocked();
            }

            nativeWake(mPtr);
        }
    }

5、removeAllFutureMessagesLocked

安全退出,只会清除那些还没到执行时间的消息

    private void removeAllFutureMessagesLocked() {
        final long now = SystemClock.uptimeMillis();
        //【分析点20】
        //1、因为在enqueueMessage的时候已经将所有Message按照when字段进行排序了
        //所以如果头节点也还没到执行时间,那说明后续的消息也未到执行时间
        //所以直接清除所有消息
        //2、如果第一个本该执行了,那就从第二个开始,依旧是将尚未可执行的消息置空
        //然后回收Message
        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);
            }
        }
    }

6、removeAllMessagesLocked

非安全退出,不管消息是否已经到可以执行的时间

    private void removeAllMessagesLocked() {
        Message p = mMessages;
        //【分析点21】
        //遍历整个链表,将消息置空并回收
        //不论这个消息是否可立刻执行
        while (p != null) {
            Message n = p.next;
            p.recycleUnchecked();
            p = n;
        }
        mMessages = null;
    }

7、enqueueMessage(重要)

消息入队方法,在这里根据msg.when对消息进行排序
注:同步屏障并不通过这个方法入队

    boolean enqueueMessage(Message msg, long when) {
        //【分析点22】
        //这个方法将同步和异步消息按照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) {
            ...

            msg.markInUse();
            msg.when = when;
            Message p = mMessages;
            boolean needWake;
            //【分析点23】
            //(1)消息头为空,说明消息队列已经没消息了,正处于阻塞状态
            //(2)不是延时消息
            //(3)是延时,但是比队列头的消息还早执行
            //则将这条消息放在消息队列的第一个
            //如果阻塞状态,则唤醒MessageQueue
            if (p == null || when == 0 || when < p.when) {
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
            } else {
            	//【分析点24】
            	//进入这里即代表这是一条普通的延时任务
                //则遍历链表,根据时间顺序来插入
                //唤醒条件:已经阻塞 & 消息链表头部节点是一个同步屏障节点 & 新入队的消息为异步消息
                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; 
                prev.next = msg;
            }

            //如果需要唤醒,则调用nativeWake
            if (needWake) {
                nativeWake(mPtr);
            }
        }
        return true;
    }

8、postSyncBarrier

发送同步屏障消息

    private int postSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            //【分析点25】
            //Message主要通过构造函数和obtain方法获取
            //系统更推荐我们通过obtain方法获取,因为这是从缓存链表里面取出来的
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            
            //【分析点26】
            //这部分的逻辑相当于普通消息在enqueueMessage里被排序一样
            //就算有多个同步屏障消息A、B、C、D,那它们也是根据when字段排序的
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { 
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }

9、removeMessages

删除指定消息,需同时满足三个条件:

  • 通过指定Handler添加
  • what字段相同
  • obj字段要么是null、要么等于我们通过setData设置进来的值

注意:如果what字段传0,有可能会将所有post系列的任务都删了,因为post的消息都是通过getPostMessage得到的,这个方法直接通过obtain获取消息,而obtain获取都是通过recycleUnchecked处理过的、what字段被设置成0的消息,这也算是post和send系列的区别之一吧

    void removeMessages(Handler h, int what, Object object) {
        if (h == null) {
            return;
        }

        synchronized (this) {
            //链表头
            Message p = mMessages;
            //【分析点27】
            //(1)指定handler内
            //(2)what相同
            //(3)object为null或者object与消息的obj相等
            //遍历所有满足条件的消息,回收掉
            while (p != null && p.target == h && p.what == what
                   && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            //上面从链表头开始,找出所有满足条件的消息,然后回收掉
            //当某条消息的next不满足条件
            //进入这个循环
            //遍历这条信息后面所有符合条件的消息,并回收
            while (p != null) {
                Message n = p.next;
                if (n != null) {
                    if (n.target == h && n.what == what
                        && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

10、removeCallbacksAndMessages

删除同时满足这两个条件的消息:

  • 通过指定Handler添加
  • obj字段要么是null、要么等于我们通过setData设置进来的值

实现逻辑和removeMeesge完全一样,只是这个方法不需要指定what字段

    void removeCallbacksAndMessages(Handler h, Object object) {
        if (h == null) {
            return;
        }
        synchronized (this) {
            Message p = mMessages;
			//注意这个&&符号,同时满足条件才会remove
			//同时注意||,说明object可以是null也可以是具体值,并不妨碍删除消息
            while (p != null && p.target == h
                    && (object == null || p.obj == object)) {
                Message n = p.next;
                mMessages = n;
                p.recycleUnchecked();
                p = n;
            }

            while (p != null) {
                Message n = p.next;
                if (n != null) {
                	//这里也一样,同时满足条件
                    if (n.target == h && (object == null || n.obj == object)) {
                        Message nn = n.next;
                        n.recycleUnchecked();
                        p.next = nn;
                        continue;
                    }
                }
                p = n;
            }
        }
    }

11、getPostMessage

post系列方法创建msg的方式,这里要注意创建出来的msg.what都会是0

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



三、Handler


1、CallBack

内部类,Handler在被Looper调用dispatchMessage方法后有三种处理方式:

  • 调用msg.callback.run,也就是我们通过post进来的Runnable
  • 调用内部类CallBack.handleMessage
  • 调用重写方法handleMessage
    public interface Callback {
        boolean handleMessage(@NonNull Message msg);
    }

2、dispatchMessage

Looper.loop中调用的分发方法

    public void dispatchMessage(@NonNull Message msg) {
        //不管是post还是send,最终在Looper层面,都是调用
        //msg.target.dispatchMessage
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                //这是设置了Handler.CallBack的情况
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //这是重写方法
            handleMessage(msg);
        }
    }

3、createAsync

主要作用是使所有通过这个Handler发送的Message,都会被设置为FLAG_ASYNCHRONOUS异步消息(默认是同步消息),在搭配消息屏障使用的情况下,会被优先调用。

    public static Handler createAsync(@NonNull Looper looper) {
        return new Handler(looper, null, true);
    }

4、obtainMessage

返回一个从缓存链表里面取出来的Message

    public final Message obtainMessage(){
        return Message.obtain(this);
    }

5、post

可以看到这一系列方法的msg是通过getPostMessage创建出来的

    public final boolean post(@NonNull Runnable r) {
       return sendMessageDelayed(getPostMessage(r), 0);
    }

6、postAtTime

post一个在某个具体时间执行的消息,其中时间是通过SystemClock.uptimeMillis() + uptimeMills计算出来的

关于SystemClock.uptimeMillisSystem.currentTimeMillis

  • SystemClock.uptimeMillis 是指从开机到现在的毫秒数(手机睡眠的时间不包括在内);
  • System.currentTimeMillis是指从1970年1月1日 UTC到现在的毫秒数;

其中System.currentTimeMillis是可以通过System.setCurrentTimeMillis修改的,所以大部分系统源码都不会用这个方法计算时间

    public final boolean postAtTime(@NonNull Runnable r, long uptimeMillis) {
        return sendMessageAtTime(getPostMessage(r), uptimeMillis);
    }

7、postDelayed

基本同postAtTime

    public final boolean postDelayed(@NonNull Runnable r, long delayMillis) {
        return sendMessageDelayed(getPostMessage(r), delayMillis);
    }

8、postAtFrontOfQueue(重要)

Handler实现“插队”主要有以下几种方法:

  • postAtFrontOfQueue,实际实现也是通过sendMessageAtFrontOfQueue
  • sendMessageAtFrontOfQueue,具体实现就是将when字段设置成0,其它消息的when字段都是当前时间或者当前时间往后的,设置为0就可以确保在enqueueMessage里面会被排到链表的头部
  • postSyncBarrier:发送同步屏障消息,然后再发送异步消息
    public final boolean postAtFrontOfQueue(@NonNull Runnable r) {
        return sendMessageAtFrontOfQueue(getPostMessage(r));
    }

9、runWithScissors(不常见)

这是一个被添加了@hide注解的方法,也就是说这个方法是不允许被应用层调用的
这个方法用于:在当前A线程向B线程发送一个Runnable,等待B线程执行完之后A线程再继续执行(有点像Thread.join)

    public final boolean runWithScissors(@NonNull Runnable r, long timeout) {
        if (r == null) {
            throw new IllegalArgumentException("runnable must not be null");
        }
        //timeout可以等于0但是不能小于0
        if (timeout < 0) {
            throw new IllegalArgumentException("timeout must be non-negative");
        }

        //【分析点28】
        //如果是同一个线程,直接执行就可以了
        if (Looper.myLooper() == mLooper) {
            r.run();
            return true;
        }

        //【分析点29】
        //如果不在同一线程
        //将Runnable封装成BlockingRunnable,调用postAndWait
        //关于BlockingRunnable后续会讲到
        BlockingRunnable br = new BlockingRunnable(r);
        return br.postAndWait(this, timeout);
    }

10、removeCallbacks

调用MessageQueueremoveMessage方法

    public final void removeCallbacks(@NonNull Runnable r) {
        mQueue.removeMessages(this, r, null);
    }

11、removeCallbacksAndMessages

同上

    public final void removeCallbacksAndMessages(@Nullable Object token) {
        mQueue.removeCallbacksAndMessages(this, token);
    }

12、sendMessage

send系列方法,与post系列不同的点:

  • 参数不同send是自定义msg的,而postmsg是通过getPostMessage得到的,所以如果removeMessage(0)的话,会将post进去的所有消息清空回收掉
  • 实现不同send要么在继承handler类时重写handleMessage,要么创建Handler的内部类CallBack,然后在这两个地方去处理我们需要在Handler所处线程要做的事情;而post可以直接通过匿名内部类,实现起来方便很多

但实际上的实现是一样的,都是通过调用enqueueMessage入队消息

    public final boolean sendMessage(@NonNull Message msg) {
        return sendMessageDelayed(msg, 0);
    }

13、sendEmptyMessageDelayed

与本方法类似的方法还有:

  • sendMessage:接受一个Message对象参数
  • sendMessageDelayed:除了Message,还有delayed字段
  • sendMessageAtTime:其他方法都是调用该方法,不像其它方法的when字段是在SystemClock.uptimeMillis()基础上计算出来,sendMessageAtTimewhen字段直接就是用方法参数的时间
  • sendEmptyMessage:接受一个msg.what参数
  • sendEmptyMessageDelayed:同上
  • sendEmptyMessageAtTime:同上
  • sendMessageAtFrontOfQueue:这是“插队”方法
    public final boolean sendEmptyMessageDelayed(int what, long delayMillis) {
        Message msg = Message.obtain();
        msg.what = what;
        return sendMessageDelayed(msg, delayMillis);
    }

14、sendMessageAtFrontOfQueue

实现插队的方法之一,实现原理如postAtFrontOfQueue所说

    public final boolean sendMessageAtFrontOfQueue(@NonNull Message msg) {
        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, 0);
    }

15、executeOrSendMessage

针对那些对所处线程没有要求的任务

    public final boolean executeOrSendMessage(@NonNull Message msg) {
        //如果同一线程
        //直接dispatch->handleMessage
        if (mLooper == Looper.myLooper()) {
            dispatchMessage(msg);
            return true;
        }
        //否则发送消息
        return sendMessage(msg);
    }

16、enqueueMessage(重要)

除了同步屏障外,其他方法都是通过这个方法进行消息入队
而且Handler造成内存泄漏的原因也在于此:msg.target = this
在这里将Handler自己赋值给Messagetarget字段,这样在分发的时候才能找得到对应的对象
也就是说如果msg是一个延时很久的任务,那可能持有HandlerActivity已经被销毁了,但Handler对象还在Message中等待被处理,这样就会导致这个对象不能被回收,导致Activity无法在GC标记阶段被标记为可回收对象,从而导致内存泄漏
(顺便说一句,BroadcastReceiver也类似于这样处理,将ReceiverDispatcher作为参数传给AMS,但它用了弱引用,不像Handler这里用了强引用)

    private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        //这里会将自己作为Message的target字段
        //所以如果delayed太久的话,很有可能会引起内存泄漏
        msg.target = this;
        msg.workSourceUid = ThreadLocalWorkSource.getUid();
        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

17、BlockingRunnable(重要)

runWithScissors中使用
实现原理:通过postAndWait将消息发送出去,同时进入wait状态,等待另一个线程执行完run方法然后调用notifyAll唤醒它
问题:(A线程发送给B线程)

  • 如果超时了,只是A线程这边不再继续阻塞,但实际上这个Runnable是已经发送出去进入MessageQueue了,也就是说它始终是会执行的,那这样就会出现消息回调在不可预测的时间点
  • 如果timeout为0,在将Runnable发出去之后,调用quit方法,quit是会清空消息队列的,也就是A永远都无法被唤醒,如果此时A还持有其他锁,那就可能会造成死锁
    private static final class BlockingRunnable implements Runnable {
        private final Runnable mTask;
        private boolean mDone;

        public BlockingRunnable(Runnable task) {
            mTask = task;
        }


        //这里也就是下面:handler.post(this)运行的代码
        //运行的线程在对面线程,如果成功了,则唤醒在等待的锁
        @Override
        public void run() {
            try {
                mTask.run();
            } finally {
                synchronized (this) {
                    mDone = true;
                    notifyAll();
                }
            }
        }


        public boolean postAndWait(Handler handler, long timeout) {
            //首先用handler将任务发出去,然后进入锁方法阻塞
            //也就是等待对面线程完成任务或者超时
            //如果mq正在退出,返回false
            if (!handler.post(this)) {
                return false;
            }

            //如果超时了,直接返回false
            //还没超时,则继续wait
            synchronized (this) {
                if (timeout > 0) {
                    final long expirationTime = SystemClock.uptimeMillis() + timeout;
                    while (!mDone) {
                        long delay = expirationTime - SystemClock.uptimeMillis();
                        if (delay <= 0) {
                            return false; // timeout
                        }
                        try {
                            wait(delay);
                        } catch (InterruptedException ex) {
                        }
                    }
                } else {
                    while (!mDone) {
                        try {
                            wait();
                        } catch (InterruptedException ex) {
                        }
                    }
                }
            }
            return true;
        }
    }



四、Message

1、变量

    //消息标签
    public int what;
    //携带参数1  整型
    public int arg1;
    //携带参数2  整型
    public int arg2;
    //携带对象
    public Object obj;
    //这个是用于Messenger跨进程通信
    public Messenger replyTo;
    //延迟时间
    public long when;

    //用于加锁的对象
    public static final Object sPoolSync = new Object();
    //指向回收的Message,其实缓存池是一个链表
    private static Message sPool;
    //当前消息池数量
    private static int sPoolSize = 0;
    //缓存池大小
    private static final int MAX_POOL_SIZE = 50;


    //这个Runnable就是postxxx传进来的Runnable
    //这个Runnable的含义是:
    //我打算在Handler创建的那个线程(例如主线程)做什么
    //那我就将代码写在这里面,最终被执行是在handler的dispatchMessage
    /*package*/ Runnable callback;

2、obtain()

从缓存池中取消息,如果没有的话才通过new创建

    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; 
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

3、obtain(Message orig)

根据传入的Message去构造从缓存池取出的消息

    public static Message obtain(Message orig) {
        Message m = obtain();
        m.what = orig.what;
        m.arg1 = orig.arg1;
        m.arg2 = orig.arg2;
        m.obj = orig.obj;
        m.replyTo = orig.replyTo;
        m.sendingUid = orig.sendingUid;
        m.workSourceUid = orig.workSourceUid;
        if (orig.data != null) {
            m.data = new Bundle(orig.data);
        }
        m.target = orig.target;
        m.callback = orig.callback;

        return m;
    }

4、obtain(Handler h)

指定Messagetarget字段

    //指定目标target
    public static Message obtain(Handler h) {
        Message m = obtain();
        m.target = h;
        return m;
    }

5、recycleUnchecked

将消息回收至缓存池,可以看到这里是将what置为0,所以才会出现我们在分析post时候所说的

    void recycleUnchecked() {
        flags = FLAG_IN_USE;
        what = 0;
        arg1 = 0;
        arg2 = 0;
        obj = null;
        replyTo = null;
        sendingUid = UID_NONE;
        workSourceUid = UID_NONE;
        when = 0;
        target = null;
        callback = null;
        data = null;

        synchronized (sPoolSync) {
            //只要复用池里面的数量还没超过最大复用数
            //就将消息存在链表
            //按照新的存在头的位置
            if (sPoolSize < MAX_POOL_SIZE) {
                next = sPool;
                sPool = this;
                sPoolSize++;
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值