Handle 源码分析与学习

前言

Android基于JVM部分规范定制了自己的DavikArt虚拟机,Android会调用ActivityThreadmain开始一个app的启动.
大多数UI应用为了避免内存共享导致的多线程问题,都采用了消息队列维护一个Main线程进行单独控制视图,如win32,Android,Java Swing.

ActivityThread.main 便存在一个消息循环的初始化。

class ActivityThread{
 	
	public static void main(String[] args) {
		//构造一个主线程对应的Looper实例,存放在Looper.sMainLooper中
	 	Looper.prepareMainLooper();
	 	//...
	 	//创建一个Handler对象实例
	 	 if (sMainThreadHandler == null) {
            sMainThreadHandler = thread.getHandler();
        }
        //开启事件循环
        Looper.loop();
	}
	//handler实例
	 final H mH = new H();
	 final Handler getHandler() {
        return mH;
    }
    static volatile Handler sMainThreadHandler;
}
复制代码

Looper分析1

为避免一次性分析所有代码所带来的烦恼这chapter只解析部分

Looper.prepareMainLooper分析

class Looper{
 	//用于存放线程对应Looper实例
  static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();

	 @Deprecated
    public static void prepareMainLooper() {
        //构造了一个main线程实例
        prepare(false);
        synchronized (Looper.class) {
        	//保存mainLooper实例
            sMainLooper = myLooper();
        }
    }
  //获取当前线程对应的Looper实例  	
  public static @Nullable Looper myLooper() {
        return sThreadLocal.get();
    }

   public static void prepare() {
        prepare(true);
    }
    
    //构造lopper实例放入一个ThteadLocal中
    private static void prepare(boolean quitAllowed) {
        sThreadLocal.set(new Looper(quitAllowed));
    }
    //构造一个消息队列
	 private Looper(boolean quitAllowed) {
        mQueue = new MessageQueue(quitAllowed);
        mThread = Thread.currentThread();
    }
}
复制代码

从上面我们可以得到Looper.prepare构造对应调用线程的Looper实例,然后将实例放入
一个ThreadLocal中。

Looper.loop分析

class Looper{
	 public static void loop() {
      	//得到当前线程对应实例
        final Looper me = myLooper();
       
		//得到Looper对应MessageQueue
        final MessageQueue queue = me.mQueue;

        boolean slowDeliveryDetected = false;

        for (;;) {
        	//取出消息队列
            Message msg = queue.next(); // might block
            //没有消息那么就退出死循环
            if (msg == null) {
                return;
            }

            // Looper中你可以自定义一个日志器实例,然后你就可以统计
            //主线程是否耗时了哦
            final Printer logging = me.mLogging;
            if (logging != null) {
                logging.println(">>>>> Dispatching to " + msg.target + " " 
                        msg.callback + ": " + msg.what);
            }
			  //分配消息给回调
           	  msg.target.dispatchMessage(msg);
                if (observer != null) {
                    observer.messageDispatched(token, msg);
                }

             //可以自定义一个日志打印
            if (logging != null) {
                logging.println("<<<<< Finished to " + msg.target + " " + msg.callback);
            }

        }
    }

}
复制代码

我们可以大概知道looperloop函数会从MessageQueue不断取出Message对象,然后进行分发.

一个可以用于统计主线程耗时的技巧

class MainActivity : AppCompatActivity() {
    class MyPrinter : Printer {
        var startTime = System.currentTimeMillis()
        override fun println(x: String) {
            //事件分发处理函数开始
            if (x.contains(">>>>> Dispatching to")) {
                startTime = System.currentTimeMillis()
            }
            //事件分发处理函数开始
            if (x.contains("<<<<< Finished to ")) {
                val endTime = System.currentTimeMillis()
                //某次主线程处理耗时为duration ms
                val duration = endTime - startTime    
            }
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)
        //得到主线程的Looper然后挂载自己的打印
        var myLooper = Looper.getMainLooper();
        myLooper.setMessageLogging(MyPrinter())
    }
}
复制代码

MessageQueue 分析1

从上面的我得知Looper会不断调用MessageQueuenext

class MessageQueue{

	//一个用于标识状态的类,这个标志位被native代码所使用,
	//所以java
	private long mPtr; // used by native code
	
    Message next() {
        // Return here if the message loop has already quit and been disposed.
      	//如果标志位为0那么证明消息循环已经结束那么返回null即可
        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命令到内核,这里不需要关心
                Binder.flushPendingCommands();
            }
			//调用linux的epoll函数进行休眠
			//epoll是linux的下的api,这里不需要深究只需要认为object.wait 和object.notify的作用即可
			//当有信息要处理或者休眠到期或被内核唤醒
            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) {
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                    } else {
                        // 链表断链后返回
                        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;
                }

               //...略
        }
    }

}
复制代码

Message作为队列节点的数据机构,我们看下他的属性:


public final class Message implements Parcelable {
   	//用户多定义的消息标识号
    public int what;
    /**
	 *  目标消息要送达的时间。这个时间是基于{@link SystemClock#uptimeMillis}.
     */
    @UnsupportedAppUsage
    @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE)
    public long when;
	
	//所对应的handler如果这个为空那么标识这个消息作为同步凭证
    @UnsupportedAppUsage
    /*package*/ Handler target;
    
	//如果你希望直接回调自定义的函数那么赋值这个即可
    @UnsupportedAppUsage
    /*package*/ Runnable callback;

    // 链表的下一个节点
    @UnsupportedAppUsage
    /*package*/ Message next;
}
复制代码

其内部的链表结构大致如下:
在这里插入图片描述
其中特别要注意的是 链表是按照优先级排序的,when越小越在前面.(when表示要执行的时间)

我们看下MessageQueue入队的操作函数

class MessageQueue{
    //标识消息队列在调用next函数的时候被阻塞在pollOnce()传入非零的超时参数
    private boolean mBlocked;
   final long ptr = mPtr; //当mPtr为0时退出消息循环,在构造MessageQueue的时候会初始化一个非0数值
	//when 表示此消息要送达的时间
    boolean enqueueMessage(Message msg, long when) {
    	//同步锁 防止多线程并发放入
        synchronized (this) {
        
            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 {
            //插入消息在队列中,在多数情况我们不需要唤醒队列,除非有一个同步屏障在头队列头且消息是一个最早的异步消息,这里可以简单理解为了快速响应异步消息
            	//同步屏障后面在细讲
                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.
            //因为之前判断过当前消息队列没有退出( mQuitting is false),所以我们假设
            //mPtr!=0
            if (needWake) {
           	   //mPtr==0标识已经退出
           	   //nativeWake使用linux底层的epool唤醒队列,并且使用mPtr告知唤醒的消息队列状态
                nativeWake(mPtr);
            }
        }
        return true;
    }
}
复制代码

Handler分析

为了构建一个和其他线程通信的桥梁,Android提供Handler,方便我们使用Looper完成线程通信和切换.

直接看如下案例(网上轮子过多不在介绍):

class MainActivity : AppCompatActivity() {
    private class MyHandler(activity: MainActivity) : Handler() {
        private val mTarget: WeakReference<MainActivity>
        override fun handleMessage(msg: Message) {
            super.handleMessage(msg)
        }
        init {
            mTarget = WeakReference<MainActivity>(activity)
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        //子线程向主线程通信
//         val myHandler = MyHandler(this)
//        val thread = Thread(Runnable {
//            myHandler.sendEmptyMessage(1)
//        })

        //主线程向子线程通信
        var myHandler: MyHandler? = null
        val thread = Thread(Runnable {
            myHandler = MyHandler(this)
            Looper.prepare()
            Looper.loop()
        }).start()
        myHandler?.sendEmptyMessage(0);
        //请在activity销毁或者不需要的退出,我见到好多大佬都忘了哦
        myHandler?.looper?.quit()
    }
    fun onDestry(){
    	
    }
}
复制代码

构造函数


public interface Callback {
    public boolean handleMessage(Message msg);
}

class Handler{
  //callback:当消息对象Message没有设置特有回调时,就先会先用Handler.CallBack,然后再根据返回值为false再去调用Handler自身的handleMessage函数
  //async:是否允许开启同步屏障 后文在讲叙
  public Handler(Callback callback, boolean async) 
}
复制代码

我们知道消息分发的时候会调用下面的代码:

class Looper{
	 public static void loop() {
			  //分配消息给回调
			  //msg是Message , target是Handler
           	  msg.target.dispatchMessage(msg);
        }
    }
}
复制代码

所以我们消息最终分发到了Handler

class Handler{
 public void dispatchMessage(Message msg) {
 		//如果Message有指定回调那么直接调用
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
        	//构造函数传入callback如果设置了根据返回值确定是否继续调用自己的handleMessage
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }
            //这个一般开发者都会重写的函数
            handleMessage(msg);
        }
    }
    //调用Message定义的回调然后直接返回
     private static void handleCallback(Message message) {
        message.callback.run();
    }
}
复制代码

所以得到以下的回调链条:
在这里插入图片描述

同步屏障

Android仅能在主线程更新ui,为了流程度我们一般会在16ms重绘一次屏幕,但是如果此时事件循环中有无数的事件队列怎么办?这将导致UI绘制延迟 ,严重影响用户体验问题。

在这里插入图片描述
考虑到这种情况Android,把消息分为两种,同步消息和异步消息,默认情况我们的消息都是同步消息。

只需要调用Message.setAsynchronous即可成为异步消息.

class Message{
  public boolean isAsynchronous() {
        return (flags & FLAG_ASYNCHRONOUS) != 0;
    }
    public void setAsynchronous(boolean async) {
        if (async) {
            flags |= FLAG_ASYNCHRONOUS;
        } else {
            flags &= ~FLAG_ASYNCHRONOUS;
        }
    }
}
复制代码

成为异步消息后,还需要插入同步屏障,这时所有的同步的消息都不会执行只会优先执行异步消息。

我们回过头看下MessageQueue的异步处理部分

class MessageQueue{
  Message next() {
      
       //...略
                //如果当前的头节点是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());
                }
               

               //...略
        }
    }
}
复制代码

示意图:
在这里插入图片描述
我们看下Android系统下如何利用这个机制进行刷新ui

class ViewRootImpl{
	//这个函数开始便利根view致性各种绘制操作
  void scheduleTraversals() {
        if (!mTraversalScheduled) {
            mTraversalScheduled = true;
            //放入一个屏障
            mTraversalBarrier = mHandler.getLooper().postSyncBarrier();
            mChoreographer.postCallback(
                    Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);
            if (!mUnbufferedInputDispatch) {
                scheduleConsumeBatchedInput();
            }
            notifyRendererOfFramePending();
        }
    }
}
复制代码

具体插入屏障代码,由于只是简单的链表操作不在解释

class Looper{
 public int postSyncBarrier() {
        return mQueue.enqueueSyncBarrier(SystemClock.uptimeMillis());
    }
}

class MessageQueue(){
  int enqueueSyncBarrier(long when) {
        synchronized (this) {
            final int token = mNextBarrierToken++;
            final Message msg = Message.obtain();
            msg.markInUse();
            msg.when = when;
            msg.arg1 = token;

            Message prev = null;
            Message p = mMessages;
            if (when != 0) {
                while (p != null && p.when <= when) {
                    prev = p;
                    p = p.next;
                }
            }
            if (prev != null) { // invariant: p == prev.next
                msg.next = p;
                prev.next = msg;
            } else {
                msg.next = p;
                mMessages = msg;
            }
            return token;
        }
    }
	//移除
 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);
            }
        }
    }
}    
复制代码

IdleHandler

MessageQueue我们重新看下这个类的next函数

//MessageQueue.java
Class MessageQueue{
    private IdleHandler[] mPendingIdleHandlers;

  Message next() {
 
        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) {
               
               
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) {
 					//如果有消息就分发 然后返回
 				}

             //如果没有消息那么就调用IdleHandler集合中的对象
            for (int i = 0; i < pendingIdleHandlerCount; i++) {
                final IdleHandler idler = mPendingIdleHandlers[i];
                mPendingIdleHandlers[i] = null; // release the reference to the handler

                boolean keep = false;
        		//调用函数
               keep = idler.queueIdle();
               
				//如果函数返回false,那么会将其移除
                if (!keep) {
                    synchronized (this) {
                        mIdleHandlers.remove(idler);
                    }
                }
            }

          
            pendingIdleHandlerCount = 0;

            nextPollTimeoutMillis = 0;
        }
    }
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享