Handler详解

幻昼 2020年07月16日 325次浏览

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

同步屏障刷新屏幕流程

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,执行空闲操作。