Android进阶——强大的异步不可或缺的组件Handler详解(一)

引言

从一开始开通博客就计划一周至少写两篇博客,把自己的以前记在本地下的知识点和经验,全部搬到自己的博客里,无论是复杂难易程度只要是自己想写的就搬到这里来,好好总结下形成自己的技术体系,因为有些东西不用就会一时之间想不起来了。ORZ…好了进入正题,对于Java开发来说Thread比较简单,直接匿名创建重写run方法,调用start方法执行即可。或者从Runnable接口继承,但对于Android来说在底层UI操作部分代码没有设计成为线程安全类型,所以需要引入一些同步的机制来实现线程间的通信使其刷新。Handler就是在这样的设计背景下应运而生。

一Handler概述

Handler跟多线程,消息队列联系很紧密,其主要的功能就是发送和处理Message,也可以分发Runnable对象。 每个Handler实例,都会绑定到创建他的线程中(默认是位于主线程,但是我们可以通过Looper对象来指定到其他线程)。

二 Handler的作用

  • 执行计划任务,比如在预定的实现某些任务,模拟定时器等等。
  • 线程之间的互相通信,在app启动的时候,会创建一个主线程(即UI线程),主线程创建了之后还会创建一个消息队列来处理各种消息,当我们创建了子线程时,可以在子线程中拿到主线程创建的Handler对象,当我们需要通信时就可以,通过该对象像主线程的消息队列发送消息,主线程接收到消息之后就会处理。
  • 确保操作始终在某个特定的线程中运行。比如说当我们访问数据库并加载数据时,除了初始化要加载外,每当我们收到数据改变的消息通知时也需要重新加载,为了保证数据的有效性,减少不必要的查询操作。

三 Handler机制重要的四种角色参与者的关系

一个消息循环的机制,简单来理解就是往消息队列里添加数据,然后异步的等待消息的返回。当消息队列为空的时候就会挂起线程,等待新的消息的加入。在线程里产生一个Looper对象,从字面上不难理解,可以看成在线程里一直循环运行,不断地检索MessageQueue,不断地取出消息,得到消息之后发送到Handler,这样Handler通过回调handleMessage方法就能处理消息了

  • Looper:Android的线程分为有消息循环的线程和没有消息循环的线程,有消息循环的线程一般都会有一个Looper。我们的主线程(UI线程)就是一个消息循环的线程。Looper的线程,不管是UI线程还是子线程,只要你有Looper,我就可以往你的消息队列里面添加东西,并做相应的处理。一句话每一个消息循环线程都可以产生一个Looper对象,用于管理线程里的MessageQueue
  • Handler:你可以构造Handler对象来于Looper沟通以便Push新的消息到MessageQueue 里或者接收Looper(从MessageQueue中取出)发送过来的消息
  • MessageQueue: 用于存放线程放入的消息
  • 线程:产生Looper和MessageQueue

四得到Handler对象

  • 默认的构造方法Handler() 把当前Handler与当前线程和当前线程的消息队列绑定,这也是为什么默认的Handler一定运行在主线程中
  • 可以触发回调Handler(Handler.Callback callback)
  • Handler(Looper looper) 把Handler绑定到Looper所属的线程和消息队列中
  • Handler(Looper looper, Handler.Callback callback)

五 Handler常用API

  • 获取Looper对象: Looper getLooper()
  • 处理系统的消息:void dispatchMessage(Message msg) 一般不宜修改
  • 处理消息:void handleMessage(Message msg) 必须实现
  • 从消息队列中得到新的Message对象:Message obtainMessage()及重载
  • 发送Runnable对象:boolean post(Runnable r) 及重载形式
  • 发送消息:boolean sendEmptyMessage()及重载
  • 发送自定义Message:boolean sendMessage(Message msg)
  • removeCallbacks(Runnable r) 、removeCallbacksAndMessages(Object token) 、removeMessages(int what) 等移除系列,移除了之后就不会去执行handleMessage(Message msg)

六 Handler的简单应用

1同一个HandlerThread里可以有多个Handler

package cmo.learn.mutiplethread;

import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.widget.Button;

public class HandlerActivity extends Activity {
    private Handler mainHandler1;
    private Handler mainHandler2;
    Button mBtn1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler);
        // 在主线程中初始化Handler
        mainHandler1 = new Handler() {

            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.d("HandlerTest", "主线程的Handler1对象接收到消息" + msg.what);
            }

        };

        mainHandler2 = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                Log.d("HandlerTest", "主线程的Handler2对象接收到消息" + msg.what);
            }
        };
    }

    public void testHandler(View view) {
        switch (view.getId()) {
        case R.id.btn_handler01:
            new Thread(new Runnable() {

                @Override
                public void run() {
                    for (int i = 0; i < 10000; i++) {
                        i++;
                    }
                    //mainHandler1.sendEmptyMessage(1);
                    mainHandler2.sendEmptyMessage(1);
                }

            }).start();
            break;
        case R.id.btn_handler02:
            break;
        case R.id.btn_handler03:
            break;
        default:
            break;
        }
    }
}

运行结果:

当使用mainHandler1.sendEmptyMessage(1)发送消息时,mainHandler1接收到消息并处理

当使用mainHandler2.sendEmptyMessage(1)发送消息时,mainHandler2接收到消息并处理

2 Handler创建到任意指定线程中

2.1 直接在在新的线程中创建Handler和发送消息,这是不允许的

/*运行时报java.lang.RuntimeException: Can‘t create handler inside thread that has not called Looper.prepare()异常*/
new Thread(new Runnable(){

                @Override
                public void run() {
                    for (int i = 0; i < 1000; i++) {
                        i++;
                    }
                    Handler handler=new Handler();//Handler放到当前新线程中
                    handler.sendEmptyMessage(3);
                }
            }).start();

2.2 继承HandlerThread封装自己的HandlerThread,并且在HandlerThread.onLooperPrepared()方法中调用new Handler创建Handler对象并完成发送消息和回调处理

    private class MyHandlerThread extends HandlerThread{

        public MyHandlerThread(String name) {
            super(name);
        }

        @Override
        protected void onLooperPrepared() {
            super.onLooperPrepared();
            for (int i = 0; i < 1000; i++) {
                i++;
            }
            Looper loop=this.getLooper();
            new Handler(loop){

                @Override
                public void handleMessage(Message msg) {
                    super.handleMessage(msg);
                    Log.d("HandlerTest", Thread.currentThread().getName()+"线程的Handler对象接收到消息" + msg.what);
                }

            }.sendEmptyMessage(3);
        }
    }
        public void testHandler(View view) {
        switch (view.getId()) {
        case R.id.btn_handler01:
            //在新的线程中创建Handler和发送消息
            new Thread(new Runnable(){

                @Override
                public void run() {
                    for (int i = 0; i < 1000; i++) {
                        i++;
                    }
                    Handler handler=new Handler();//Handler放到当前新线程中
                    handler.sendEmptyMessage(3);
                }

            }).start();

            break;
        case R.id.btn_handler02:
            MyHandlerThread myThread=new MyHandlerThread("子线程");//调用
            myThread.start();
            break;
        default:
            break;
        }
    }

运行结果

3 利用Handler(Looper loop)指定Handler运行的线程

3.1 使用Looper.getMainLooper()

case R.id.btn_handler02:
            /*MyHandlerThread myThread=new MyHandlerThread("子线程");
            myThread.start();*/
            //使用MainLooper来指定
            new Thread(new Runnable(){

                @Override
                public void run() {
                    Handler handler;

                    handler=new Handler(Looper.getMainLooper()){

                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.d("HandlerTest", Thread.currentThread().getName()+"的Handler对象接收到消息" + msg.what);
                        }
                    };
                    handler.sendEmptyMessage(4);
                }
            }).start();

运行结果:

3.2 使用Looper.prepare()和Looper.loop()

//使用Looper.prepare()和Looper.loop()来指定运行的线程
            new Thread(new Runnable(){

                @Override
                public void run() {
                    Handler loophandler;
                    Looper.prepare();//必须执行
                    loophandler=new Handler(){

                        @Override
                        public void handleMessage(Message msg) {
                            super.handleMessage(msg);
                            Log.d("HandlerTest", Thread.currentThread().getName()+"的Handler对象接收到消息" + msg.what);
                        }

                    };
                    loophandler.sendEmptyMessage(5);
                    Looper.loop();
                }

            }).start();

运行结果:

通过以上两个小实验,我们可以知道,通过指定Looper对象,我们就可以控制Handler运行在指定的线程。

4 同一个Handler中不同的消息不可能同时执行,也不会交替执行

首先封装我们自己的一个Handler来处理两条消息

    private class ProcessMessageHandler extends Handler{

        public ProcessMessageHandler(Looper looper) {
            super(looper);
        }

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            Log.d("HandlerTest", "Start:ProcessMessageHandler对象接收到第一条消息" + msg.what);
            try{
                Thread.sleep(5000);//阻塞5秒,是为了避免UI被阻塞,把Handler放到了另外的线程
            }catch(Exception e){
                e.printStackTrace();
            }
            Log.d("HandlerTest", "End :ProcessMessageHandler对象接收到第二条消息" + msg.what);
        }
    }

模拟同时发送两条信息

HandlerThread threadProcess=new HandlerThread("Process");
            threadProcess.start();//启动HandlerThread
            Handler handlerProcess=new ProcessMessageHandler(threadProcess.getLooper());
            handlerProcess.sendEmptyMessage(6);//发送两条消息,每条消息都执行6s
            handlerProcess.sendEmptyMessage(7);

运行结果:

两条消息之间是不可能出现交替处理的,只有我们处理完一条消息之后,才会再从MessageQueue中读取下一条消息。

5 对于我们正在执行的Message,我们能否Remove掉?

5.1 Remove掉一个还没有开始执行的Message

首先还是先启动一条HandlerThread

private Handler workMsgHandler;
HandlerThread workingMsgThread=new HandlerThread("WorkingMsg");
        workingMsgThread.start();
        workMsgHandler=new ProcessMessageHandler(workingMsgThread.getLooper());

发送消息

workMsgHandler.sendEmptyMessage(10);//发送消息
workMsgHandler.sendEmptyMessage(12);//发送消息

执行Remove:

workMsgHandler.removeMessages(12);//5S前移除消息what为12

运行结果对比:

未移除what为12的消息:

移除了what为12的消息:

5.2Remove掉一个正在执行的Message

首先还是先启动一条HandlerThread

private Handler workMsgHandler;
HandlerThread workingMsgThread=new HandlerThread("WorkingMsg");
        workingMsgThread.start();
        workMsgHandler=new ProcessMessageHandler(workingMsgThread.getLooper());

发送消息之后

workMsgHandler.sendEmptyMessage(10);//发送消息

马上执行移除消息

workMsgHandler.removeMessages(10);//5S前移除消息what为10

运行结果:

很明显无法Remove掉正在执行的Message,如果呗Remove掉的话“End……”就不会被打印

5.3小结

一旦Message已经被执行,在执行的过程中不会被Remove掉;能Remove的只有未被执行的Message即Remove只能移除消息队列中的Message。

6同一个HandlerThread中使用多个Handler的MessageQueue不会混淆

7 利用弱引用去封装自己的Handler

虽然在Android中已经不推荐使用 WeakReference(防止内存泄漏,要保证内存被虚拟机回收,即弱引用关联的对象在JVM进行垃圾回收时总会被回收),甚至在高级版本中据说已经完全删除了弱引用,但是个人觉得若是用得恰当对自己开发的代码质量的提高有显著的效果。反之,则坑人坑己。any way,慎用为妙。

通过弱引用得到Activity的对象,进而在封装的Handler中使用Activity的方法交互

static class MyHandler extends Handler{
    private final WeakReference<Activity> mAct;//声明弱引用
    //声明构造方法供外部调用构MyHandler对象
    public MyHandler(MainActivity act){
        mAct=new  WeakReference<Activity>(act);
    }
    @Ovrride
    public void handleMessage(Message msg){
        //在这里获取MainActivity的引用并强转,调用相关方法showMessage
        ((MainActivity)mAct.get()).showMessage();
    }
}

使用自己封装的Handler发送消息

private MyHandler handler=new MyHandler();

总结

Handler实例与消息处理是相关联的,发送和接收都要求匹配,只能依附在Handler线程或其子类中,所以Handler的所有操作都是在一个线程中。推荐一个GC在线源码阅读网站

时间: 2024-10-04 15:55:54

Android进阶——强大的异步不可或缺的组件Handler详解(一)的相关文章

Android 基础总结:( 十四)Handler详解(上)

Handler的定义: 主要接受子线程发送的数据, 并用此数据配合主线程更新UI. 解释: 当应用程序启动时,Android首先会开启一个主线程 (也就是UI线程) , 主线程为管理界面中的UI控件,进行事件分发,比如说,你要是点击一个 Button ,Android会分发事件到Button上,来响应你的操作. 如果此时需要一个耗时的操作,例如:联网读取数据,或者读取本地较大的一个文件的时候,你不能把这些操作放在主线程中,如果你放在主线程中的话,界面会出现假死现象,如果5秒钟还没有完成的话,会收

Android 基础总结:( 十五)Handler详解(下)

Android GWES之Android消息系统 Looper,Handler,View 我们要理解Android的消息系统,Looper,Handle,View等概念还是需要从消息系统的基本原理及其构造这个源头开始.从这个源头,我们才能很清楚的看到Android设计者设计消息系统之意图及其设计的技术路线. 1.消息系统的基本原理 从一般的系统设计来讲,一个消息循环系统的建立需要有以下几个要素: 消息队列 发送消息 消息读取 消息分发 消息循环线程 首先来研究一下消息驱动的基本模型,我使用如下的

Android多线程----异步消息处理机制之Handler详解

关于Android的多线程知识,请参考本人之前的一篇博客:Android 多线程----AsyncTask异步任务详解 在Android当中,提供了异步消息处理机制的两种方式来解决线程之间的通信问题,一种是今天要讲的Handler的机制,还有一种就是之前讲过的 AsyncTask 机制. 一.handler的引入: 我们都知道,Android UI是线程不安全的,如果在子线程中尝试进行UI操作,程序就有可能会崩溃.相信大家在日常的工作当中都会经常遇到这个问题,解决的方案应该也是早已烂熟于心,即创

Android四大组件--Activity详解

Android四大组件--Activity详解 分类: android android应用android开发 本文的主要内容包括1.activity的建立.配置和使用:2.activity的跳转和传值:3.startActivityForResult:4.activity的生命周期. 1.activity的建立.配置和使用 Activity是一个应用中的组件,它为用户提供一个可视的界面,方便用户操作,比如说拔打电话.照相.发邮件或者是浏览地图等.每个activity会提供一个可视的窗口,一般情况

Android开发四大组件--Activity详解

Android开发四大组件--Activity详解 - Android开发教程 Android开发的四大组件在开发中应用中是必不可少的,下面就来详解下四大组件之一Activity,总结自网络.Activty的生命周期的也就是它所在进程的生命周期. 一个Activity的启动顺序: onCreate()——>onStart()——>onResume() 当另一个Activity启动时: 第一个Activity onPause()——>第二个Activity onCreate()——>

Android官方架构组件:Lifecycle详解&amp;迪士尼彩乐园网站架设原理分析

我们先将重要的这些类挑选出来: LifecycleObserver接口( Lifecycle观察者):实现该接口的类,通过注解的方式,可以通过被LifecycleOwner类的addObserver(LifecycleObserver o)方法注册,被注册后,LifecycleObserver便可以观察到LifecycleOwner的生命周期事件. LifecycleOwner接口(Lifecycle持有者):实现该接口的类持有生命周期(Lifecycle对象),该接口的生命周期(Lifecyc

Android官方架构组件:Lifecycle详解&amp;迪士尼彩乐园平台搭建原理分析

在过去的谷歌IO大会上,Google官方向我们推出了 Android Architecture Components,其中谈到Android组件处理生命周期的问题,向我们介绍了 Handling Lifecycles. 同时,如何利用 android.arch.lifecycle 包提供的类来控制数据.监听器等的 lifecycle.同时,LiveData 与 ViewModel 的 lifecycle 也依赖于 Lifecycle 框架. 经过公司内部的技术交流小组的探讨后,不少小伙伴觉得这个

Android官方架构组件:Lifecycle详解&amp;迪士尼彩乐园定制开发原理分析

Lifecycle 是一个类,它持有关于组件(如 Activity 或 Fragment)生命周期状态的信息,并且允许其他对象观察此状态. 我们只需要2步: 1.Prestener继承LifecycleObserver接口public interface IPresenter extends LifecycleObserver { @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) void onCreate(@NotNull LifecycleOwner

Android基础入门教程——2.3.2 EditText(输入框)详解

Android基础入门教程--2.3.2 EditText(输入框)详解 标签(空格分隔): Android基础入门教程 本节引言: 上一节中我们学习了第一个 UI控件TextView(文本框),文中给出了很多实际开发中可能遇到的一些需求 的解决方法,应该会为你的开发带来便利,在本节中,我们来学习第二个很常用的控件EditText(输入框): 和TextView非常类似,最大的区别是:EditText可以接受用户输入!和前面一样,我们不一个个讲属性, 只讲实际应用,要扣属性可以自己查看API文档