Handler详解

围巾🧣 2020年07月16日 497次浏览

Handler 机制简介

  • 定义
    一套 Android 消息传递机制 / 异步通信机制

  • 作用

    在多线程的应用场景中,将工作线程中需更新UI的操作信息 传递到 UI主线程,从而实现 工作线程对UI的更新处理,最终实现异步消息的处理

    image.png

  • 为什么要用 Handler消息传递机制
    答:多个线程并发更新UI的同时 保证线程安全。具体描述如下

    image.png

时序图

图片如下

image.png

使用

方式1:使用 Handler.sendMessage()

新建Handler子类(内部类)、匿名 Handler子类

	 /**
     * 方式1:新建Handler子类(内部类)
     */

    // 步骤1:自定义Handler子类(继承Handler类) & 复写handleMessage()方法
    class MyHandler extends Handler {

        // 通过复写handlerMessage() 从而确定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
            //...// 需执行的UI操作

        }
    }

    // 步骤2:在主线程中创建Handler实例
    private Handler myHandler = new MyHandler();

    void useTest() {
        // 步骤3:创建所需的消息对象
        Message msg = Message.obtain(); // 实例化消息对象
        msg.what = 1; // 消息标识
        msg.obj = "AA"; // 消息内容存放

        // 步骤4:在工作线程中 通过Handler发送消息到消息队列中
        // 可通过sendMessage() / post()
        // 多线程可采用AsyncTask、继承Thread类、实现Runnable
        myHandler.sendMessage(msg);

        // 步骤5:开启工作线程(同时启动了Handler)
        // 多线程可采用AsyncTask、继承Thread类、实现Runnable
    }



	/**
     * 方式2:匿名内部类
     */
    // 步骤1:在主线程中 通过匿名内部类 创建Handler类对象
    private Handler myHandler = new Handler() {
        // 通过复写handlerMessage()从而确定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
            //...// 需执行的UI操作
        }
    };


    void useTest() {
        // 步骤2:创建所需的消息对象
        Message msg = Message.obtain(); // 实例化消息对象
        msg.what = 1; // 消息标识
        msg.obj = "AA"; // 消息内容存放

        // 步骤3:在工作线程中 通过Handler发送消息到消息队列中
        // 可通过sendMessage() / post()
        // 多线程可采用AsyncTask、继承Thread类、实现Runnable
        myHandler.sendMessage(msg);

        // 步骤4:开启工作线程(同时启动了Handler)
        // 多线程可采用AsyncTask、继承Thread类、实现Runnable
    }

方式2:使用Handler.post()

    // 步骤1:在主线程中创建Handler实例
    private Handler mhandler = new mHandler();

    // 步骤2:在工作线程中 发送消息到消息队列中 & 指定操作UI内容
    // 需传入1个Runnable对象
    mHandler.post(new Runnable() {
        @Override
        public void run() {
                ... // 需执行的UI操作 
        }

    });

    // 步骤3:开启工作线程(同时启动了Handler)
    // 多线程可采用AsyncTask、继承Thread类、实现Runnable

工作流程

image.png

使用实例

public class MainActivity extends AppCompatActivity {

    public TextView mTextView;
    public Handler mHandler;

    // 步骤1:(自定义)新创建Handler子类(继承Handler类) & 复写handleMessage()方法
    class Mhandler extends Handler {

        // 通过复写handlerMessage() 从而确定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
            // 根据不同线程发送过来的消息,执行不同的UI操作
            // 根据 Message对象的what属性 标识不同的消息
            switch (msg.what) {
                case 1:
                    mTextView.setText("执行了线程1的UI操作");
                    break;
                case 2:
                    mTextView.setText("执行了线程2的UI操作");
                    break;
            }
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextView = (TextView) findViewById(R.id.show);

        // 步骤2:在主线程中创建Handler实例
        mHandler = new Mhandler();

        // 采用继承Thread类实现多线程演示
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // 步骤3:创建所需的消息对象
                Message msg = Message.obtain();
                msg.what = 1; // 消息标识
                msg.obj = "A"; // 消息内存存放

                // 步骤4:在工作线程中 通过Handler发送消息到消息队列中
                mHandler.sendMessage(msg);
            }
        }.start();
        // 步骤5:开启工作线程(同时启动了Handler)

        // 此处用2个工作线程展示
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 通过sendMessage()发送
                // a. 定义要发送的消息
                Message msg = Message.obtain();
                msg.what = 2; //消息的标识
                msg.obj = "B"; // 消息的存放
                // b. 通过Handler发送消息到其绑定的消息队列
                mHandler.sendMessage(msg);
            }
        }.start();


    }
}
public class MainActivity extends AppCompatActivity {

    public TextView mTextView;
    public Handler mHandler;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        mTextView = (TextView) findViewById(R.id.show);

        // 步骤1:在主线程中 通过匿名内部类 创建Handler类对象
        mHandler = new Handler() {
            // 通过复写handlerMessage()从而确定更新UI的操作
            @Override
            public void handleMessage(Message msg) {
                // 根据不同线程发送过来的消息,执行不同的UI操作
                switch (msg.what) {
                    case 1:
                        mTextView.setText("执行了线程1的UI操作");
                        break;
                    case 2:
                        mTextView.setText("执行了线程2的UI操作");
                        break;
                }
            }
        };
        // 采用继承Thread类实现多线程演示
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 步骤3:创建所需的消息对象
                Message msg = Message.obtain();
                msg.what = 1; // 消息标识
                msg.obj = "A"; // 消息内存存放

                // 步骤4:在工作线程中 通过Handler发送消息到消息队列中
                mHandler.sendMessage(msg);
            }
        }.start();
        // 步骤5:开启工作线程(同时启动了Handler)

        // 此处用2个工作线程展示
        new Thread() {
            @Override
            public void run() {
                try {
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                // 通过sendMessage()发送
                // a. 定义要发送的消息
                Message msg = Message.obtain();
                msg.what = 2; //消息的标识
                msg.obj = "B"; // 消息的存放
                // b. 通过Handler发送消息到其绑定的消息队列
                mHandler.sendMessage(msg);
            }
        }.start();
    }
}

时间优先队列插入

MessageQueue 插入时会根绝 Handler 执行时间去排序,执行早的在队列前面

观察 enqueueMessage 方法: 根据执行时间放到队列合适的位置

    // note jin: 把消息放到 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; // p 为队列头
            boolean needWake;
            if (p == null || when == 0 || when < p.when) {
                // note jin: when < p.when ,比第一个执行时间早,新的消息队列头
                msg.next = p;
                mMessages = msg;
                needWake = mBlocked; //  唤醒事件队列如果在阻塞
            } else {
                // note jin: 插入队列中间。 通常,我们不必唤醒事件队列 除非队列的顶端有屏障还有该消息是队列中最早的异步消息。
                needWake = mBlocked && p.target == null && msg.isAsynchronous();
                Message prev;
                for (;;) {
                    // note jin: 根据执行时间找到消息队列的合适位置
                    prev = p;
                    p = p.next;
                    if (p == null || when < p.when) {
                        // 到最后 or 该消息执行时间比p 指向的当前消息早
                        break;
                    }
                    if (needWake && p.isAsynchronous()) {
                        needWake = false;
                    }
                }
                // note jin: 插入队列
                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;
    }

同步屏障刷新屏幕流程

在Android中,Handler是用于发送和处理消息的工具。同步屏障(Synchronization Barrier)在Handler中用于控制消息的顺序执行,确保在队列中的消息在特定条件下按照预期的顺序被处理。

在Handler中,消息是按照FIFO(先进先出)的顺序被处理的。然而,在某些情况下,可能希望确保某个消息处理完成后才能继续处理后续的消息,这时同步屏障就派上用场了。

具体来说,同步屏障通常与 Handler.sendMessageAtFrontOfQueue() 方法一起使用。当调用这个方法时,可以传入一个 SyncBarrier 对象作为消息,这个对象会被插入到消息队列的头部,并且在队列中的所有消息被处理完成之前,它所代表的同步屏障将会一直阻止队列中的后续消息被处理。

使用同步屏障的典型场景是在消息队列中插入一个同步屏障,确保在UI更新或者其他重要操作完成之后才能继续处理后续的消息,以避免并发执行带来的问题。

在使用同步屏障时,需要注意以下几点:

  • 同步屏障会阻塞消息队列中同步屏障之后的消息,直到它被处理完成。
  • 同步屏障本身不会执行任何操作,它只是一个标记,用于控制消息的执行顺序。
  • 需要谨慎使用同步屏障,避免死锁等问题。

Handler-barrier

HandlerThread

HandlerThread是Thread的子类,严格意义上来说就是一个线程,只是它在自己的线程里面帮我们创建了Looper
HandlerThread 存在的意义如下:

1)方便使用:a. 方便初始化,b,方便获取线程looper
2)保证了线程安全

我们一般在Thread里面 线程Looper进行初始化的代码里面,必须要对Looper.prepare(),同时要调用Loop.loop()

场景

在另一个线程初始化子线程的handler的时候,我们无法将子线程的looper传值给Handler,因为 looper 可能还没有初始化

    public void run() {
        mTid = Process.myTid();
        Looper.prepare();
        synchronized (this) {
            mLooper = Looper.myLooper();
            notifyAll(); //此时唤醒其他等待的锁
        }
        Process.setThreadPriority(mPriority);
        onLooperPrepared();
        Looper.loop();
        mTid = -1;
    }

它的优点就在于它的多线程操作,可以帮我们保证使用Thread的handler时一定是安全的。

idleHandler

这是一种在只有当消息队列没有消息时或者是队列中的消息还没有到执行时间时才会执行的 IdleHandler,它存放在 mPendingIdleHandlers 队列中。

IdleHandler 是一个回调接口,当线程中的消息队列将要阻塞等待消息的时候,就会回调该接口。

在 MessageQueue 的 next 方法里,如没有消息时,就取该 Handler,执行空闲操作。

一般来说,IdleHandler 通常用于执行一些不需要立即执行、耗时较长但又不需要实时性的任务,以避免占用主线程的资源。

下面是一个简单的示例,展示了如何使用 IdleHandler

import android.os.Bundle;
import android.os.Looper;
import android.os.MessageQueue;

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 获取主线程的 Looper
        Looper looper = Looper.getMainLooper();

        // 注册 IdleHandler
        MessageQueue.IdleHandler idleHandler = new MessageQueue.IdleHandler() {
            @Override
            public boolean queueIdle() {
                // 当主线程空闲时执行的任务
                // 这里可以添加需要执行的任务逻辑
                return true; // 返回 true 表示继续监听空闲状态,false 表示注销 IdleHandler
            }
        };

        // 将 IdleHandler 注册到主线程的消息队列中
        looper.getQueue().addIdleHandler(idleHandler);
    }
}

通过 looper.getQueue().addIdleHandler(idleHandler)IdleHandler 注册到主线程的消息队列中。

需要注意的是,由于 queueIdle() 方法会在主线程空闲时被频繁调用,因此要确保在这里执行的任务是轻量级的,不会影响到主线程的性能和响应性。

Message 处理回调

在每次 Looper 取消息时,Android 提供了一种机制可以让开发者设置回调,以便在消息处理前后执行一些自定义逻辑。这通常是通过设置 LooperPrinter 来实现的。

Printer 是一个接口,定义了一个方法 println(String x)Printer 的作用是帮助开发者在 Looper 每次处理消息时,能够插入一些自定义的逻辑,例如日志记录或性能监控。

设置回调的方法

可以使用 Looper.setMessageLogging 来设置一个 Printer,这个 Printerprintln 方法会在每次 Looper 处理消息时被调用。

示例代码

public class LooperCallbackExample {

    public static void main(String[] args) {
        // 创建一个新线程来运行 Looper
        Thread thread = new Thread(new Runnable() {
            @Override
            public void run() {
                // 准备 Looper
                Looper.prepare();

                // 设置一个 Printer 回调
                Looper.myLooper().setMessageLogging(new Printer() {
                    @Override
                    public void println(String x) {
                        // 这里的代码将在每次 Looper 处理消息时被调用
                        Log.d("LooperCallback", "Looper processing message: " + x);
                    }
                });

                // 创建一个 Handler 并发送消息
                Handler handler = new Handler();
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                        Log.d("LooperCallback", "Handler is running");
                    }
                });

                // 开始 Looper 循环
                Looper.loop();
            }
        });

        thread.start();
    }
}