一、为什么要使用Handlers?

因为,我们当我们的主线程队列,如果处理一个消息超过5秒,android 就会抛出一个 ANP(无响应)的消息;所以,我们需要把一些要处理比较长的消息,放在一个单独线程里面处理,把处理以后的结果,返回给主线程运行,就需要用的Handler来进行线程建的通信。

Message对象封装了所有的消息,而这些消息的操作需要Handler(消息处理类)类完成。什么是handler?handler起到了处理MQ上的消息的作用(只处理由自己发出的消息),即通知MQ它要执行一个任务(sendMessage),并在loop到自己的时候执行该任务(handleMessage),整个过程是异步的handler创建时会关联一个looper,默认的构造方法将关联当前线程的looper,不过这也是可以set的

二、消息类:Message类

 android.os.Message的主要功能是进行消息的封装,同时可以指定消息的操作形式,Message类定义的变量和常用方法如下:

(1)public int what:变量,用于定义此Message属于何种操作
 (2)public Object obj:变量,用于定义此Message传递的信息数据,通过它传递信息
 (3)public int arg1:变量,传递一些整型数据时使用
 (4)public int arg2:变量,传递一些整型数据时使用
 (5)public Handler getTarget():普通方法,取得操作此消息的Handler对象。

在整个消息处理机制中,message又叫task,封装了任务携带的信息和处理该任务的handler。message的用法比较简单,但是有这么几点需要注意:
(1)尽管Message有public的默认构造方法,但是你应该通过Message.obtain()来从消息池中获得空消息对象,以节省资源。
(2)如果你的message只需要携带简单的int信息,请优先使用Message.arg1和Message.arg2来传递信息,这比用Bundle更省内存
(3)使用message.what来标识信息,以便用不同方式处理message。
(4)使用setData()存放Bundle对象。

三、消息通道:Looper

在使用Handler处理Message时,需要Looper(通道)来完成。在一个Activity中,系统会自动帮用户启动Looper对象,而在一个用户自定义的类中,则需要用户手工调用Looper类中的方法,然后才可以正常启动Looper对象。Looper的字面意思是“循环者”,它被设计用来使一个普通线程变成Looper线程。所谓Looper线程就是循环工作的线程。在程序开发中(尤其是GUI开发中),我们经常会需要一个线程不断循环,一旦有新任务则执行,执行完继续等待下一个任务,这就是Looper线程。使用Looper类创建Looper线程很简单:

public class LooperThread extends Thread {
    @Override
    public void run() {
        // 将当前线程初始化为Looper线程
        Looper.prepare();
         
        // ...其他处理,如实例化handler
         
        // 开始循环处理消息队列
        Looper.loop();
    }
}

通过上面两行核心代码,你的线程就升级为Looper线程了!那么这两行代码都做了些什么呢?
1)Looper.prepare():创建Loop而对象。
    现在你的线程中有一个Looper对象,它的内部维护了一个消息队列MQ。注意,一个Thread只能有一个Looper对象;prepare()背后的工作方式一目了然,其核心就是将looper对象定义为ThreadLocal。
2)Looper.loop():循环获取MQ中的消息,并发送给相应Handler对象。
    调用loop方法后,Looper线程就开始真正工作了,它不断从自己的MQ中取出队头的消息(也叫任务)执行。
3)Looper类其他方法:
    Looper.myLooper()得到当前线程looper对象
    getThread()得到looper对象所属线程
    quit()方法结束looper循环
4)综上,Looper有以下几个要点:
    每个线程有且只能有一个Looper对象,它是一个ThreadLocal
    Looper内部有一个消息队列,loop()方法调用后线程开始不断从队列中取出消息执行
    Looper使一个线程变成Looper线程。

四、 消息操作类:Handler类

Handler创建的时候肯定会在一个线程当中(主线程或者子线程),并且创建一个Looper实例与此线程绑定(无论是系统帮我们创建或者通过prepare自己绑定),在Looper中维护一个消息队列,然后looper循环的从消息队列中读取消息执行(在消息队列所在线程执行)。这就是整个Handler的运行机制了。

1、通过Handler有很多种发送消息的方式:

post(Runnable)
 postAtTime(Runnable, long)
 postDelayed(Runnable, long)
 sendEmptyMessage(int)
 sendMessage(Message)
 sendMessageAtTime(Message, long)
 sendMessageDelayed(Message, long)

其实无论是通过post的方式或者send的方式,最后都是通过(但其实post发出的Runnable对象最后都被封装成message对象了)public final boolean sendMessageDelayed(Message msg, long delayMillis)发出的

2、Handler拥有下面两个重要的特点:
1)handler可以在任意线程发送消息,这些消息会被添加到关联的MQ上,见源码:
2)消息的处理是通过核心方法dispatchMessage(Message msg)与钩子方法handleMessage(Message msg)完成的,handler是在它关联的looper线程中处理消息的。
这就解决了android最经典的不能在其他非主线程中更新UI的问题。

3、使用

public class DownloadActivity extends AppCompatActivity implements View.OnClickListener {
    private TextView downloadTV;
    private Button downloadBtn;
    private Handler handler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            downloadTV.setText("下载完成");
        }
    };

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

        bind();
    }

    private void bind() {
        downloadBtn=findViewById(R.id.download_btn);
        downloadTV=findViewById(R.id.download_tv);
        downloadBtn.setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.download_btn:
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        try {
                            Thread.sleep(5000);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                       handler.sendEmptyMessage(1);
                    }
                }).start();
                break;
        }
    }
}