安卓 Handler 机制学习

Posted 编程圈子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了安卓 Handler 机制学习相关的知识,希望对你有一定的参考价值。

安卓 Handler 机制学习

一、一些基本概念

1. Handler机制的目的:

  • 耗时操作在独立线程操作
  • 跨线程传递消息
  • 消息托管
  • 消息处理的异步

2. handler与thread

  • Handler 作用是实现异步消息处理,Thread是新线程
  • Handler 的调用者仍为同一线程
  • 安卓的UI操作要求必须在主线程执行,如果Handler里操作过于耗时,调用者会阻塞。安卓提供了几种方式在其它线程来操作UI,如: Activity.runOnUiThread(Runnable), View的Post 、AsnycTask类等,它们基本都采用了Handler。
  • Handler直接调用线程的run方法。

3. handler的message处理

handler里使用Looper来处理Message,消息的处理是会阻塞的。

二、Handler机制组成部分

1. message

在线程间传递的对象稍为消息。Message.obtain()可以用来创建对象,这时创建对象类似是一个对象池,减少每次都new对象的开销。
Message是一个单向列表。

2. Handler

handler是message的处理器,同时也负责消息的发送和移除。

  • 发送即时消息: handler.sendMessage(Message msg)
  • 发送延时消息: handler.sendMessageDelayed(Message msg, long time)
  • 处理消息:handleMessage(Message msg)
  • 移除还未处理消息: handler.removeMessages(int what)

3. MessageQueue 消息队列

每个线程会有一个MessageQueue,message按when排序存放在消息队列 。

4. Looper 循环器

一个死循环,用来循环取出MessageQueue里的Message,交给Handler处理。每个线程只会有一个Looper对象。
Looper对象会持有1个MessageQueue对象、1个Thread对象(表示当前线程)、1个sMainLooper(指向主线程中创建的Looper对象)、1个ThreadLocal对象(负责持有作为线程局部变量的Looper对象)。

所以Thread-Looper-MessageQueue 之间的关系是一对一的。

三、 Handler的工作流程

  1. 创建线程、运行线程 thread.start()
  2. 创建Looper、MessageQueue Looper.prepare();
  3. 启动Loop循环机制,监听MessageQueue Looper.loop();
  4. 发送Message handler.sendMessage(msg); 或 handler.post(runnable);
  5. Loop监听到新Message,取出处理。 Looper.loop()

四、Looper方法

1. 构造函数

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

构造Looper的时候会创建MessageQueue。

2. prepareMainLooper()

用来在主线程中创建Looper对象,一般APP框架调用这个方法,不能在其它线程调用。

定义


    public static void prepareMainLooper() 
        prepare(false);
        synchronized (Looper.class) 
            if (sMainLooper != null) 
                throw new IllegalStateException("The main Looper has already been prepared.");
            
            // myLooper 用来返回当前线程中持有的Looper对象
            sMainLooper = myLooper();
        
    

调用

public final class ActivityThread 
    public static final void main(String[] args) 
        ......
        Looper.prepareMainLooper();
        ......
        ActivityThread thread = new ActivityThread();
        thread.attach(false);

        if (sMainThreadHandler == null)     
            sMainThreadHandler = thread.getHandler();
        
        ......
        Looper.loop();
        ......
    

3. Looper.prepare()方法

静态方法用来创建MessageQueue,可以根据参数控制Looper是否可退出。这里使用ThreadLocal对象,这样不同线程可以保存不同的Looper。

public final class Looper
   static final ThreadLocal<Looper> sThreadLocal = new ThreadLocal<Looper>();
   final MessageQueue mQueue;
	public static void prepare()
        prepare(true);
    
    public 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));
	
	private Looper(boolean quitAllowed)
		mQueue = ne MessageQueue(quitAllowed);
	

4. myLooer()方法

用来获取当前线程中持有的线程局部Looper对象变量。

    public static @Nullable Looper myLooper() 
        return sThreadLocal.get();
    

5. Looper.loop()方法

无限循环从MessageQueue中取出 Message对象。如果消息不为空就发送给Handler的dispatchMessage方法处理,如果为空会等待。

public static void loop() 
    final Looper me = myLooper();  //获取TLS存储的Looper对象,获取当前线程的Looper 
    if (me == null) 
        throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
    
 
    final MessageQueue queue = me.mQueue;  //获取Looper对象中的消息队列
    ....

    for (;;)  //主线程开启无限循环模式
        Message msg = queue.next(); //获取队列中的下一条消息,可能会线程阻塞
        if (msg == null)  //没有消息,则退出循环,退出消息循环,那么你的程序也就可以退出了
            return;
        
        ....
        //分发Message,msg.target 是一个Handler对象,哪个Handler把这个Message发到队列里,
        //这个Message会持有这个Handler的引用,并放到自己的target变量中,这样就可以回调我们重写
        //的handler的handleMessage方法。
        msg.target.dispatchMessage(msg);
        ....
        ....
        msg.recycleUnchecked();  //将Message回收到消息池,下次要用的时候不需要重新创建,obtain()就可以了。
    

五、MessageQueue

MessageQueue是一种消息队列的数据结果,其中重要的方法是 enqueueMessage()和 next() 方法。

  • 创建主线程的时候,会默认创建一个Looper,同时创建一个MessageQueue,
  • 创建子线程时,不会自动创建Looper,也不会创建MessageQueue,在调用Looper.prepare()的时候才会创建该线程的MessageQueue。

1. enqueueMessage

enQueueMessage主要操作:

  • 插入消息到消息队列
  • 唤醒Looper中等待的线程
boolean enqueueMessage(Message msg, long when) 
        if (msg.target == null) //msg.target就是发送此消息的Handler
            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;//将延迟时间封装到msg内部
            Message p = mMessages;//消息队列的第一个元素
            boolean needWake;
            if (p == null || when == 0 || when < p.when) 
               //如果此队列中头部元素是null(空的队列,一般是第一次),
               //或者此消息不是延时的消息,则此消息需要被立即处理,
               //此时会将这个消息作为新的头部元素,并将此消息的next指向旧的头部元素,
               //然后判断如果Looper获取消息的线程如果是阻塞状态则唤醒它,让它立刻去拿消息处理.
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked;
             else 
                //如果此消息是延时的消息,则将其添加到队列中,
                //原理就是链表的添加新元素,按照when,也就是延迟的时间来插入的,
                //延迟的时间越长,越靠后,这样就得到一条有序的延时消息链表,取出消息的时候,延迟时间越小的,就被先获取了。
                //插入延时消息不需要唤醒Looper线程。
                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;
    

2. next()

  • next()里的 nativePollOnce(ptr, nextPollTimeoutMillis); 会阻塞消息,等待新消息到来。
  • 如果拿到消息还没时间,会听故事希望nextPollTimeoutMillis赋值,线程阻塞,等到时间自动唤醒
Message next() 
    
        final long ptr = mPtr;
        if (ptr == 0) 
           //从注释可以看出,只有looper被放弃的时候(调用了quit方法)才返回null,mPtr是MessageQueue的一个long型成员变量,关联的是一个在C++层的MessageQueue,阻塞操作就是通过底层的这个MessageQueue来操作的;当队列被放弃的时候其变为0。
            return null;
        
 
        int pendingIdleHandlerCount = -1; // -1 only during first iteration
        int nextPollTimeoutMillis = 0;
        for (;;) 
            if (nextPollTimeoutMillis != 0) 
                Binder.flushPendingCommands();
            
 
            //阻塞方法,主要是通过native层的epoll监听文件描述符的写入事件来实现的。
           //如果nextPollTimeoutMillis=-1,一直阻塞不会超时。
           //如果nextPollTimeoutMillis=0,不会阻塞,立即返回。
           //如果nextPollTimeoutMillis>0,最长阻塞nextPollTimeoutMillis毫秒(超时),如果期间有程序唤醒会立即返回。
            nativePollOnce(ptr, nextPollTimeoutMillis);
 
            synchronized (this) 
           
                final long now = SystemClock.uptimeMillis();
                Message prevMsg = null;
                Message msg = mMessages;
                if (msg != null && msg.target == null) 
                    //msg.target == null表示此消息为消息屏障(通过postSyncBarrier方法发送来的)
                    //如果发现了一个消息屏障,会循环找出第一个异步消息(如果有异步消息的话),所有同步消息都将忽略(平常发送的一般都是同步消息)
                    do 
                        prevMsg = msg;
                        msg = msg.next;
                     while (msg != null && !msg.isAsynchronous());
                
                if (msg != null) 
                    if (now < msg.when) 
                        // 如果消息此刻还没有到时间,设置一下阻塞时间nextPollTimeoutMillis,进入下次循环的时候会调用nativePollOnce(ptr, nextPollTimeoutMillis)进行阻塞;
                        nextPollTimeoutMillis = (int) Math.min(msg.when - now, Integer.MAX_VALUE);
                     else 
                        //正常取出消息
                        //设置mBlocked = false代表目前没有阻塞
                        mBlocked = false;
                        if (prevMsg != null) 
                            prevMsg.next = msg.next;
                         else 
                            mMessages = msg.next;
                        
                        msg.next = null;
                        msg.markInUse();
                        return msg;
                    
                 else 
                    //没有消息,会一直阻塞,直到被唤醒
                    nextPollTimeoutMillis = -1;
                
 
                if (mQuitting) 
                    dispose();
                    return null;
                
 
            //...
            //此处有省略IdleHandler相关代码
            //...
 
            pendingIdleHandlerCount = 0;
            nextPollTimeoutMillis = 0;
        
    
 

六、Handler

1. 构造函数

    public Handler(Callback callback) 
        this(callback, false);
    
    public Handler(Callback callback, boolean async) 
        
        //.....此处省略n行代码......
 
        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的时候可以传入Callback,也可以不传。

2. 发送消息: sendMessage(msg)

sendMessage(msg)和post(Runnable)都会调用到sendMessageDelayed(Message msg, long delayMillis),最终调用 sendMessageAtTime方法。

3. 发送消息:post(Runnable)

这个时候消息会赋上callback属性值。

// 源码
    public final boolean post(Runnable r)
    
       return  sendMessageDelayed(getPostMessage(r), 0);
    

使用方式:

mHandler.post(new Runnable() 
          @Override
           public void run() 
        //主线程UI更新
                  
);

4. sendMessageAtTime

    public boolean sendMessageAtTime(Message msg, long uptimeMillis) 
        // 这里把mQueue赋值给本地的Queue,传给enQueueMessage,
        // 而mQueue就是mLooper.mQueue
        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);
    

4. dispatchMessage

    public void dispatchMessage(Message msg) 
        if (msg.callback != null) 
            // 使用post(runnable)的时候
            handleCallback(msg);
         else 
        	// mCallback 是Handler的构造函数赋值
            if (mCallback != null) 
                if (mCallback.handleMessage(msg)) 
                    return;
                
            
            handleMessage(msg);
        
    

七、使用示例

1. 使用handler作延时处理

这里是向主线程添加延时消息。

       binding.fab.setOnClickListener(new View.OnClickListener() 
            @Override
            public void onClick(View view) 
                new Handler().postDelayed(new Runnable() 
                    @Override
                    public void run() 
                        Toast.makeText(MainActivity.this, "延迟处理", Toast.LENGTH_SHORT).show();
                    
                , 1000*5);

            
        );

2. 子线程更新UI

    private void update(String text) 
        TextView textView = findViewById(R.id.count);
        textView.setText(text);
    

    private final Handler handler = new Handler(Looper.myLooper()) 
        @Override
        public void handleMessage(android.os.Message msg) 
            if (msg.what == 1) 
                Bundle bundle = msg.getData();
                update(bundle.getString("text"));
            
        
    ;


    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        com.cn.test.databinding.ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());

        setSupportActionBar(binding.toolbar);

        binding.fab.setOnClickListener(view -> 
            Message message = new Message();
            Bundle bundle = new Bundle();
            bundle.putString("text", (new Date()).getTime() + "")安卓 Handler 机制学习

安卓 Handler 机制学习

什么是Handler的同步屏障机制?

Android :安卓学习笔记之 Handler机制 的简单理解和使用

深入源码分析Handler 消息机制 LooperMessageQueue 消息同步屏障IdleHandlerMessage 复用

(4.1.10.8)Android Handler之同步屏障机制(sync barrier)