• Handler Looper 解析


    文章讲述Looper/MessageQueue/Handler/HandlerThread相关的技能和使用方法.

    什么是Looper?Looper有什么作用?

    Looper是用于给线程(Thread)添加消息队列(MessageQueue)的工具;让消息队列的处理处于循环状态,一旦接收到消息,会唤醒线程并执行消息处理方法.

    通常情况下,Activity和Service等系统组件不会使用到Looper,Framework层已经为其初始化好了线程(附带有消息队列,称为主线程或UI线程);主线程会一直运行,处理用户事件.

    如果我们需要新建一个线程,且这个线程要能够循环处理其他线程发过来的消息事件,或者需要长时间与其他线程进行交互,这时就需要使用到Looper给线程建立消息队列.

    Looper怎么用?

    Looper类中以下方法:

    比较常用的是以下方法:

    public static void prepare();
    public static Looper myLooper();
    public static void loop();
    public void quit();

    方法描述:

    1. prepare():在线程run()最开始,调用该方法;为线程初始化消息队列.

    2. myLooper():获取Looper对象引用;一定要在prepare()之后调用.

    3. loop():让线程的消息队列开始运行,接收消息;

    4. quit():退出消息循环.若线程中无其他操作,线程将终止运行.

        public class WorkThread extends Thread {
            // 工作线程的Handler实例
            private Handler mHandler;
            // 工作线程的Looper实例
            private Looper mLooper;
            public WorkThread() {
                LogUtil.d(TAG, "WorkThread::starting...");
                start();
            }
            public void run() {
                LogUtil.d(TAG, "WorkThread::runningF...");
                // 初始化消息队列
                Looper.prepare();
                // 获取Looper对象
                mLooper = Looper.myLooper();
                // 在工作线程创建Handler
                mHandler = new Handler(mLooper) {
                    @Override
                    public void handleMessage(android.os.Message msg) {
                        LogUtil.d(TAG, "handleMessage::starting...");
                        StringBuilder sb = new StringBuilder();
                        sb.append("it is my please to serve you, please be patient to wait!
    ");
                        // 子线程一直在循环执行
                        for (int i = 0; i < 100; i++) {
                            sb.append(".");
                            Message newMsg = Message.obtain();
                            newMsg.obj = sb.toString();
                            mMainHanlder.sendMessage(newMsg);
                            SystemClock.sleep(1000);
                        }
                        sb.append("
    your work is done");
                        Message otherMsg = Message.obtain();
                        otherMsg.obj = sb.toString();
                        mMainHanlder.sendMessage(otherMsg);
                    };
                };
                // 开始接收消息
                Looper.loop();
            }
            public void exit() {
                LogUtil.d(TAG, "exit::starting...");
                if (mLooper != null) {
                    // 退出消息循环
                    mLooper.quit();
                    mLooper = null;
                    LogUtil.d(TAG, "exit::mLooper is set to null");
                }
            }
            public void executeTask(String txt) {
                LogUtil.d(TAG, "executeTask::starting...");
                if (mLooper == null || mHandler == null) {
                    LogUtil.d(TAG, "executeTask::mLooper is null");
                    Message msg = Message.obtain();
                    msg.obj = "Sorry man, it is out of service";
                    // 使用主线程的Handler实例,向主线程发送Message
                    mMainHanlder.sendMessage(msg);
                    return;
                }
                LogUtil.d(TAG, "executeTask::mLooper is not null");
                Message msg = Message.obtain();
                msg.obj = txt;
                mHandler.sendMessage(msg);
            }
        }

    在工作线程中创建的Handler是属于该子线程(工作线程)的,而不是主线程.

    疑惑:

    Thread的状态和Looper的关系:未退出消息循环时,Thread是不会死亡的;一旦消息队列接收到消息,则会唤醒线程并处理消息.

    何时退出Thread?如上述所说,若没有消息队列,一旦run()执行结束,该Thread也就是结束.

    Handler是用于操作线程内部消息队列的类.即是用Handler来操作消息队列.如:给消息队列发送消息,和从消息队列中取出消息并处理.Handler作用:用于线程内部消息处理;用于线程间通讯(ITC-Inter Thread Communication).

    必须要指出的是,此处的Handler来自于:import android.os.Handler;

    Handler用于线程内部消息处理,即在将来定时执行某个动作,或者周期性执行动作.Handler用于操作线程内部的消息队列,可以用来线程间通信ITC,大大减少同步的烦恼,甚至不需要使用synchronized.

    Handler/Looper/MessageQueue是属于一个线程内部的数据,但提供给外部线程访问的接口,Handler就是公开给外部线程,与线程通讯的接口.MessageQueue是相对较底层的,较少直接使用;Looper和Handler就是专门用来操作底层MessageQueue的.

    Handler是用于操作一个线程内部的消息队列的,所以Handler必须依附于一个线程,而且只能是一个线程.也就是说:必须在一个线程内创建Handler,同时制定Handler的回调方法handlerMessage(Message msg).

    以上方法都是设置定时器,在指定的时间向Handler所在的MessageQueue发送消息.线程内部消息循环并不是并发处理(并非创建一个线程),而是在同一个线程中处理.

    并发执行或处理,既是多线程执行.

    正确创建Handler,需要让Handler与线程绑定.如果给Handler指定Looper对象,此时Handler便绑定到Looper对象所在的线程.Handler的消息处理回调方法会在这个线程执行;如果不指定Looper,则Handler绑定到创建此Handler的线程内,消息处理回调方法也在这个线程执行.

    如果要在一个线程中使用消息队列和Handler,Android API中有已经封装好的HanlderThread,在这个类中已经做好了Looper的初始化工作.

        private void initBackThread() {
            mIndexUpdateThread = new HandlerThread("check-message-coming");
            mIndexUpdateThread.start();
            // 使用mIndexUpdateThread创建Handler实例,即这个Handler就在mIndexUpdateThread线程中
            mIndexUpdateHandler = new Handler(mIndexUpdateThread.getLooper()) {
                @Override
                public void handleMessage(android.os.Message msg) {
                    int what = msg.what;
                    LogUtil.d(TAG, "handleMessage::msg.what=" + what
                            + "; Thread name=" + Thread.currentThread().getName());
                    checkforUpdate();
                    if (mIsUpdateInfo) {
                        // 实现循环更新
                        mIndexUpdateHandler.sendEmptyMessageDelayed(
                                MSG_UPDATE_INFO, 500);
                    }
                };
            };
        }
  • 相关阅读:
    关于java中的批注@SuppressWarnings
    Enumeration接口的用法
    java中的Properties类的操作
    编译java时出现(端口冲突)Address already in use: JVM_Bind<null>:1919
    jQuery画廊插件-GalleryView
    jQuery中用attr和prop获取checkbox的属性问题
    (报错记录)Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:2.3.2:
    JDK 和 JRE 的区别(转载)
    css属性之vertical-align详解
    this——笔记
  • 原文地址:https://www.cnblogs.com/CVstyle/p/6395759.html
Copyright © 2020-2023  润新知