Handler知识点

215 阅读10分钟

Handler

1.Handler是什么?

Handler是一个消息分发对象,进行异步消息处理。

三部分组成

处理器 类(Handler)

消息队列 类(MessageQueue)

循环器 类(Looper)

2.Handler的作用

在多线程场景中,将工作线程(子线程)需要跟新UI的操作信息 传递到 主线程,从而实现工作线程(子线程)跟新UI的操作,实现异步处理任务。

引申问题:为什么使用Handler?

因为Handler可以多线程并发跟新UI的同时保证了线程的安全

在Android中只允许主线成更新UI组件,实际场景中子线程也会有需要跟新ui组件的需求,这会造成线程的不安全性。

3.Handler的使用

sendMessage

 		private TextView showTextView;
    private MyHandler mMyhandler =new MyHandler();
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        showTextView = (TextView) findViewById(R.id.show_tv);
        createThread();

        //方法二:创建Handler的匿名内部类
        handler=new Handler(){
            @Override
            public void handleMessage(@NonNull Message msg) {
                super.handleMessage(msg);
                switch (msg.what){
                    case 1:
                        showTextView.setText("线程一已启动,更新ui");
                        break;
                    case 2:
                        showTextView.setText("线程二已启动,更新ui");
                        break;
                    default:
                        break;
                }
            }
        };
    }

    private void createThread() {
        new Thread(){
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Message message =Message.obtain();
                message.what=1;//设置标识符
                message.obj="存储的数据";
                mMyhandler.sendMessage(message);
            }
        }.start();//开启线程

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                Message messageTwo=Message.obtain();
                messageTwo.what=2;//设置标识符
                messageTwo.obj="存储的信息";
                mMyhandler.sendMessage(messageTwo);
            }
        }).start();
    }

    //方法一 创建Handler子类
    private class MyHandler extends Handler{
        @Override
        public void handleMessage(@NonNull Message msg) {
            super.handleMessage(msg);
            switch (msg.what){
                case 1:
                    showTextView.setText("线程一已启动,更新ui");
                    break;
                case 2:
                    showTextView.setText("线程二已启动,更新ui");
                    break;
                default:
                    break;
            }
        }
    }

post

private TextView showTextView;
    private Handler handler;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        showTextView = (TextView) findViewById(R.id.show_tv);
        createThread();
        //创建一个Handler
        handler = new Handler();
    }

    private void createThread() {
        new Thread(){
            @Override
            public void run() {
                super.run();
                try {
                    Thread.sleep(3000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                    showTextView.setText("三秒延迟跟新UI");
                    }
                });
            }
        }.start();//开启线程

        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(6000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                handler.post(new Runnable() {
                    @Override
                    public void run() {
                   showTextView.setText("六秒延迟更新UI");
                    }
                });
            }
        }).start();
    }

4.Handler的原码分析

alt Handler

使用步骤

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

  // 步骤2:创建消息对象
    Message msg = Message.obtain(); // 实例化消息对象
    msg.what = 1; // 消息标识
    msg.obj = "AA"; // 消息内容存放
  
  // 步骤3:在工作线程中 通过Handler发送消息到消息队列中
  // 多线程可采用AsyncTask、继承Thread类、实现Runnable
   mHandler.sendMessage(msg);

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

查看Handler的构造方法

/** 
  * 具体使用
  */
    private Handler mhandler = new  Handler(){
        // 通过复写handlerMessage()从而确定更新UI的操作
        @Override
        public void handleMessage(Message msg) {
                ...// 需执行的UI操作
            }
    };

/** 
  * 源码分析:Handler的构造方法
  * 作用:初始化Handler对象 & 绑定线程
  * 注:
  *   a. Handler需绑定 线程才能使用;绑定后,Handler的消息处理会在绑定的线程中执行
  *   b. 绑定方式 = 先指定Looper对象,从而绑定了 Looper对象所绑定的线程(因为Looper对象本已绑定了对应线程)
  *   c. 即:指定了Handler对象的 Looper对象 = 绑定到了Looper对象所在的线程
  */
  public Handler() {

            this(null, false);
            // ->>分析1

    }
/** 
  * 分析1:this(null, false) = Handler(null,false)
  */
  public Handler(Callback callback, boolean async) {

            ...// 仅贴出关键代码

            // 1. 指定Looper对象
                mLooper = Looper.myLooper();
                if (mLooper == null) {
                    throw new RuntimeException(
                        "Can't create handler inside thread that has not called Looper.prepare()");
                }
                // Looper.myLooper()作用:获取当前线程的Looper对象;若线程无Looper对象则抛出异常
                // 即 :若线程中无创建Looper对象,则也无法创建Handler对象
                // 故 若需在子线程中创建Handler对象,则需先创建Looper对象
                // 注:可通过Loop.getMainLooper()可以获得当前进程的主线程的Looper对象

            // 2. 绑定消息队列对象(MessageQueue)
                mQueue = mLooper.mQueue;
                // 获取该Looper对象中保存的消息队列对象(MessageQueue)
                // 至此,保证了handler对象 关联上 Looper对象中MessageQueue
    }

从上面可看出: 当创建Handler对象时,则通过 构造方法 自动关联当前线程的Looper对象 & 对应的消息队列对象(MessageQueue).

那么,线程的Looper对象和消息队列对象MessageQueue什么时候创建的?

/** 
  * 源码分析1:Looper.prepare()
  * 作用:为当前线程(子线程) 创建1个循环器对象(Looper),同时也生成了1个消息队列对象(MessageQueue)
  * 注:需在子线程中手动调用该方法
  */

		//设置Looper,只能调用一次。1个线程中只能对应1个Looper实例
    public static final void prepare() {
    
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        // 1. 判断sThreadLocal是否为null,否则抛出异常
        //即 Looper.prepare()方法不能被调用两次 = 1个线程中只能对应1个Looper实例
        // 注:sThreadLocal = 1个ThreadLocal对象,用于存储线程的变量

      	//ThreadLocal:代表了一个线程局部的变量,每条线程都只能看到自己的值,并不会意识到其它的线程中也存在该变量。
      	//在这里ThreadLocal的作用是保证了每个线程都有各自的Looper
        sThreadLocal.set(new Looper(true));
        // 2. 若为初次Looper.prepare(),则创建Looper对象 & 存放在ThreadLocal变量中
        // 注:Looper对象是存放在Thread线程里的
        // 源码分析Looper的构造方法->>分析a
    }

  /** 
    * 分析a:Looper的构造方法
    **/

        private Looper(boolean quitAllowed) {

            mQueue = new MessageQueue(quitAllowed);
            // 1. 创建1个消息队列对象(MessageQueue)
            // 即 当创建1个Looper实例时,会自动创建一个与之配对的消息队列对象(MessageQueue)

            mRun = true;
            mThread = Thread.currentThread();
        }

/** 
  * 源码分析2:Looper.prepareMainLooper()
  * 作用:为 主线程(UI线程) 创建1个循环器对象(Looper),同时也生成了1个消息队列对象(MessageQueue)
  * 注:该方法在主线程(UI线程)创建时自动调用,即 主线程的Looper对象自动生成,不需手动生成
  */
    // 在Android应用进程启动时,会默认创建1个主线程(ActivityThread,也叫UI线程)
    // 创建时,会自动调用ActivityThread的1个静态的main()方法 = 应用程序的入口
    // main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象

      /** 
        * 源码分析:main()
        **/
        public static void main(String[] args) {
            ... // 仅贴出关键代码

            Looper.prepareMainLooper(); 
            // 1. 为主线程创建1个Looper对象,同时生成1个消息队列对象(MessageQueue)
            // 方法逻辑类似Looper.prepare()
            // 注:prepare():为子线程中创建1个Looper对象
            
            
            ActivityThread thread = new ActivityThread(); 
            // 2. 创建主线程

            Looper.loop(); 
            // 3. 自动开启 消息循环 

        }

创建主线程时,会自动调用ActivityThread的1个静态的main();而main()内则会调用Looper.prepareMainLooper()为主线程生成1个Looper对象,同时也会生成其对应的MessageQueue对象

生成Looper & MessageQueue对象后,则会自动进入消息循环:Looper.loop()

/** 
  * 源码分析: Looper.loop()
  * 作用:消息循环,即从消息队列中获取消息、分发消息到Handler
  * 特别注意:
  *       a. 主线程的消息循环不允许退出,即无限循环
  *       b. 子线程的消息循环允许退出:调用消息队列MessageQueue的quit()
  */
  public static void loop() {
        
        ...// 仅贴出关键代码

        // 1. 获取当前Looper的消息队列
            final Looper me = myLooper();
            if (me == null) {
                throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
            }
            // myLooper()作用:返回sThreadLocal存储的Looper实例;若me为null 则抛出异常
            // 即loop()执行前必须执行prepare(),从而创建1个Looper实例
            
            final MessageQueue queue = me.mQueue;
            // 获取Looper实例中的消息队列对象(MessageQueue)

        // 2. 消息循环(通过for循环)
            for (;;) {
            
            // 2.1 从消息队列中取出消息
            Message msg = queue.next(); 
            if (msg == null) {
                return;
            }
            // next():取出消息队列里的消息
            // 若取出的消息为空,则线程阻塞
            // ->> 分析1 

            // 2.2 派发消息到对应的Handler
            msg.target.dispatchMessage(msg);
            // 把消息Message派发给消息对象msg的target属性
            // target属性实际是1个handler对象
            // ->>分析2

        // 3. 释放消息占据的资源
        msg.recycleUnchecked();
        }
}

/** 
  * 分析1:queue.next()
  * 定义:属于消息队列类(MessageQueue)中的方法
  * 作用:出队消息,即从 消息队列中 移出该消息
  */
  Message next() {

        ...// 仅贴出关键代码

        // 该参数用于确定消息队列中是否还有消息
        // 从而决定消息队列应处于出队消息状态 or 等待状态
        int nextPollTimeoutMillis = 0;

        for (;;) {
            if (nextPollTimeoutMillis != 0) {
                Binder.flushPendingCommands();
            }

        // nativePollOnce方法在native层,若是nextPollTimeoutMillis为-1,此时消息队列处于等待状态 
        nativePollOnce(ptr, nextPollTimeoutMillis);

        synchronized (this) {
     
            final long now = SystemClock.uptimeMillis();
            Message prevMsg = null;
            Message msg = mMessages;

            // 出队消息,即 从消息队列中取出消息:按创建Message对象的时间顺序
            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 (DEBUG) Log.v(TAG, "Returning message: " + msg);
                    msg.markInUse();
                    return msg;
                }
            } else {

                // 若 消息队列中已无消息,则将nextPollTimeoutMillis参数设为-1
                // 下次循环时,消息队列则处于等待状态
                nextPollTimeoutMillis = -1;
            }

            ......
        }
           .....
       }
}// 回到分析原处

/** 
  * 分析2:dispatchMessage(msg)
  * 定义:属于处理者类(Handler)中的方法
  * 作用:派发消息到对应的Handler实例 & 根据传入的msg作出对应的操作
  */
  public void dispatchMessage(Message msg) {

    // 1. 若msg.callback属性不为空,则代表使用了post(Runnable r)发送消息
    // 则执行handleCallback(msg),即回调Runnable对象里复写的run()
        if (msg.callback != null) {
            handleCallback(msg);
        } else {
            if (mCallback != null) {
                if (mCallback.handleMessage(msg)) {
                    return;
                }
            }

            // 2. 若msg.callback属性为空,则代表使用了sendMessage(Message msg)发送消息(即此处需讨论的)
            // 则执行handleMessage(msg),即回调复写的handleMessage(msg) ->> 分析3
            handleMessage(msg);

        }
    }

  /** 
   * 分析3:handleMessage(msg)
   * 注:该方法 = 空方法,在创建Handler实例时复写 = 自定义消息处理方式
   **/
   public void handleMessage(Message msg) {  
          ... // 创建Handler实例时复写
   } 

Handle

相关问题:

Handle可以在子线程创建吗?

通常Handler必须在主线程创建,因为Handler机制的主要作用是更新UI。当然也可以创建在子线程,那么就得自己手动去prepare()和loop()。

new Thread(){
        @Override
        public void run() {
            super.run();
            //创建当前线程的Looper,并绑定到ThreadLocal中
            Looper.prepare();
            //创建Handler,关联Looper和MessageQueue
            Handler handler = new Handler();
            //启动消息循环
            Looper.loop();
        }
    }.start();
   

引申问题:什么时线程安全?

线程安全不是指线程的安全,而是指内存的安全。 每个进程只能访问系统所分配给自己的一块内存,不能访问其他进程的内存。而进程中的所有线程可以访问内存中的堆内存。这就造成了一个安全隐患,如果某一线程要去休息了,回来发现堆内存自己的数据被人修改了,这就会出问题。因为公共区域人来人往,存放的东西如果没有安全措施,一定不安全。内存的情况也是这样的。

Handler的静态引用?

未找到相关答案

为什么不new Message()而是Message.obtain()?

Message内部维护了一个Message池,用于管理Message对象,使用obtain直接从池子中获取对象,而new需要消耗内存

/**
     * Return a new Message instance from the global pool. Allows us to
     * avoid allocating new objects in many cases.
     */
    public static Message obtain() {
        synchronized (sPoolSync) {
            if (sPool != null) {
                Message m = sPool;
                sPool = m.next;
                m.next = null;
                m.flags = 0; // clear in-use flag
                sPoolSize--;
                return m;
            }
        }
        return new Message();
    }

Todo:Android中为什么主线程不会因为Looper.loop()里的死循环卡死??

未完成

同一个Looper是如何区分不同的Handler的?

 private boolean enqueueMessage(@NonNull MessageQueue queue, @NonNull Message msg,
            long uptimeMillis) {
        msg.target = this;	//当前的Handler会把自己赋值给msg.target对象,这样 msg.target.dispatch()就会分发不同的消息
        msg.workSourceUid = ThreadLocalWorkSource.getUid();

        if (mAsynchronous) {
            msg.setAsynchronous(true);
        }
        return queue.enqueueMessage(msg, uptimeMillis);
    }

参考文章:

Android Developer Document Handler Handler开发文档

手记 Handler详解 以提问的方式

简书 Android Handler:手把手带你深入分析 Handler机制源码

简书 Android Handler 使用详解

简书 Android Handler之原理解析

一个线程可以有几个Looper?几个Handler?从Looper.prepare()来看看关于Looper的一些问题