Handler基本使用

简介: Handler主要用于消息的处理,是Android SDK来处理异步消息的核心类,子线程与主线程通过Handler来进行通信,子线程可以通过Handler来通知主线程进行UI更新。

第一节

使用Handler实现异步更新UI
1.创建Handler

Handler handler=new Handler(){
    @Override
    public void handleMessage(Message msg){
       super.handleMessage(msg);
         //收到消息后,将会调用此方法
         //可以在这里进行UI更新操作
       }
   };

2.然后只要在子线程发送一条空消息即可

new Thread(new Runnable(){
        @Override
        public void run(){
           handler.sendEmptyMessage(1);
        }
   }).start();

哈哈哈,然后就实现了异步更新UI,是不是觉得很简单啊!但其实本质上还是在主线程更新UI,而Handler的作用就是去通知主线程去
更新UI,因为Android是单线程模式的,所以我们不能在子线程更新UI,只能通过Handler去通知主线程更新UI。

那么问题来了,如果我们在多个地方发送了消息,而且每一个地方又要进行不同的UI更新操作,那么我们又要怎么区分呢?哈哈哈…其实这个我们完全不用担心,因为其实Handler早就为我们考虑到了,不信,我们再往回看看!

从这里,我们可以看到,这里还携带了一个参数“1”而这个1就是这条消息的标识,也可以理解为这条消息的id,通过这个我们就能区分消息了!

handler.sendEmptyMessage(1);

然后…创建Handler就变成了这样

Handler handler=new Handler(){
    @Override
    public void handleMessage(Message msg){
       super.handleMessage(msg);
          switch(msg.what){
             case 1:
              {
                 //收到标识为1的消息后,将会调用这里
                 //可以在这里进行UI更新操作
              }
          }
       }
   };

扩展: 如果你认为Handler只能发送空消息,那么你就大错特错了,因为Handler还支持发送一个Message对象,不过呢!在看代码之前,还是先看看这个吧!

参数 解析
arg1 参数是一个int类型,可以传递一个int类型的消息
arg2 同arg1一样
obj 这个就比较强了,参数是一个Object类型,可以传递一个Object类型的消息。
what 一条消息的标识,也可以理解为一条消息的id,主要用于区分消息。

发送一个Message对象
Message对象是可以带参数的,参数已经解析过了,这里就不做讲解了,哈哈哈…

Message message =new Message();
message.arg1=1;
message.arg2=2;
message.what=3;
message.obj="测试";
handler.sendMessage(message);

哈哈哈,怎么样呀!是不是感觉瞬间高大了很多呢,有没有感觉到啊?哈哈哈…然后Handler的代码就变成了这样。

Handler handler=new Handler(){
    @Override
    public void handleMessage(Message msg)
    {
       super.handleMessage(msg);
          switch (msg.what){
             case 1:
              {
                 //收到标识为1的消息后,将会调用这里
                 //可以在这里进行UI更新操作	
              }
             break;
             case 3:
              {
             //收到标识为3的消息后,将会调用这里
                Log.d("arg1", msg.arg1 + "");
                Log.d("arg2", msg.arg2 + "");
                Log.d("obj", msg.obj + "");
              }
          }
       }
   };

运行一下。

Message优化:官方建议我们将

Message msg =new Message();

改为

Message msg = handler.obtainMessage();

或者

Message msg = Message.obtain();

优点: 这样的话,可以由系统负责message对象的创建与销毁

继续深入学习

第二节

细心的朋友可能会注意到Handler对象还有一个post的方法,用于发送一个Runnable对象,我们需要这个Runnable对象中,重写run()方法。

这里要注意的是:使用post发送Runnable对象 虽然看起来像是在子线程中执行,但其实它是在主线程中执行的!由于我们每次与子线程打交道时,都会与Runnable接触,很容易就会误以为Runnable就是子线程,但其实能创建子线程的只有一个,那就是Thread,这下你应该明白了吧!这个完全是可以通过Log输出当前线程,进行验证的,不信你可以试试。

因为使用post发送的消息不会被Handler对象接收,所以这里需要注意一下,不要把它们两个搞混了,所有的操作都只要在Runnable对象里写好就行了。

一般用法:

new Thread(new Runnable(){
     @Override
     public void run()
     {
        handler.post(new Runnable()
        {
          @Override
           public void run()
            {
               //UI更新操作
               //这里的所有操作都会在主线程中执行
            }
        });
      }
   }).start();

当然,你也可以这样写

private class myRunnable implements Runnable{
    @Override
    public void run()
    {
       // UI更新操作
       // 这里的所有操作都会在主线程中执行
    }
 }

然后…

myRunnable myRunnable=new myRunnable();
handler.post(myRunnable);

当然,你也可以在创建Handler时做好相关操作,然后你只要在你想要调用的地方发出一条消息即可。

嗯呐…Hadler的基本用法,就这些了,现在赶紧去试试吧!哈哈哈…

继续深入学习

第三节:

HandlerThread

如果我们想要在Handler对象中执行一些比较耗时的操作的话, 我们一般会想到的是直接在把它丢到子线程中去执行,所以直接在Handler中新建一个子线程不就完事了吗?我想,大多数人的反应大概都是这个样子的吧!

Handler Handler=new Handler(){
    @Override
    public void handleMessage(Message msg){
       super.handleMessage(msg);
       new Thread(new Runnable(){
          @Override
          public void run(){
             //在这里执行耗时操作
          }
       }).start();
    }
 };

嗯呐,这样确实实现了异步执行,但是如果这个Handler对象接收到的消息很多呢?那么又会怎样呢?

为了让效果更加明显,我在Handler对象中写了一条日志输出,输出的内容是当前的线程,并且向它发送了三条消息,运行结果如下:

同样的,我又在Looper线程中做了同样的操作,运行结果如下:

分析:

现象 结论
普通的Handler对象创建了三条线程 每接收到一条消息,就会创建一条新的线程,这个方法对线程的开销很大,所以不推荐使用。
Looper线程只有一条线程 无论接收到多少条消息,它都只会创建一条线程

那么现在你应该知道Looper线程的作用了吧!

注意:这里主要是想告诉大家什么是Looper线程,并不是让大家去写。官方已经封装好了Looper线程,名叫HandlerThread,这个…我们后面再讲。

不过在学习Looper线程之前呢,还是先了解一下这个吧!

词义 解释
Looper 一个线程只能有一个Looper,一个Looper只能有一个MessageQueue,Looper负责消息的循环,它会一直从MessageQueue里面取出 Message,并交给相应的 Handler 进行处理。
MessageQueue 消息队列,用于存放Handler发送来的消息
Message 消息体,用于装载需要发送的对象,存放于MessageQueue中
Handler 这个…我想…已经不用介绍了吧!

Looper线程

如果我们想要在子线程中使用Handler的话,我们还需要在线程里面创建一个Looper对象,才能够使消息能够进行循环。

Looper线程其实就是一个拥有Looper的子线程

那么问题来了,为什么我们在主线程中使用的Handler时,不用创建Looper对象呢?因为主线程默认是创建Looper对象的,所以就不需要我们再去创建了。

创建Looper线程

具体代码如下:

private class myThread extends Thread{
    public Handler Handler;
    @Override
    public void run()
    {
       Looper.prepare();
       Handler = new Handler(){
          @Override
          public void handleMessage(Message msg)				{
             super.handleMessage(msg);
             Log.d("测试2", Thread.currentThread() + "");
             //异步任务
             //可执行一些比较耗时的操作
             //不能执行UI更新操作
             }
          };
          Looper.loop();
       }	
   }

使用方法与上面有些不一样
具体请看代码:

例示:向子线程发送一条消息

myThread myThread=new myThread();
myThread.start();
try
{
myThread.sleep(500);
}
catch (InterruptedException e)
myThread.Handler.sendEmptyMessage(0);

运行一下。

通过对比,我们可以很清楚的看出,它是在子线程里面执行的,所以不能执行UI更新操作,可用于一些比较耗时的操作

测试1,是我在主线程里的Handler对象里面写的,输出的是当前的线程,仅仅只是为了做一下对比。

Handler handler=new Handler(){
   @Override
   public void handleMessage(Message msg)
   {
      super.handleMessage(msg);
      Log.d("测试1", Thread.currentThread() + "");
      //UI更新操作
      }
   };
handler.sendEmptyMessage(0);

继续学习Looper线程

在了解完上面的东西后。我们现在在主线程中创建一个Handler对象,并且为它指定一个Looper对象,就把它指定为Looper线程中的Looper对象吧!

具体执行步骤如下:

private class myThread extends Thread{
    public Looper looper;
    @Override
    public void run()
    {
       looper.prepare();
       looper=looper.myLooper();
       looper.loop();
      }
   }

这里解释一下,上面的
Looper.myLooper();
用于获取当前线程的Looper对象

记得在Activity的onDestroy中写上
myThread.looper.quit();
用于终止Looper线程的Looper循环

然后在主线程创建Handler对象时指定这个Looper对象

myThread myThread=new myThread();
myThread.start();
Handler handler=new Handler(myThread.looper){
    @Override
    public void handleMessage(Message msg)
    {
       super.handleMessage(msg);
       Log.d("嘿嘿",Thread.currentThread()+"");
       //异步任务
       //可执行一些比较耗时的操作
       //不能执行UI更新操作
   }
 };
 handler.sendEmptyMessage(1);

然后。。。

程序崩溃了

这是为什么呢?
通过程序运行日志,我们可以看到
myThread.looper

这里出现了空值问题,因为当我们的程序创建Handler对象时,子线程中的Looper对象还没有来得及创建,所以才导致了程序崩溃,那么我们又应该去怎么解决呢?

解决方法:

在启动Looper线程后,先让主线程先休眠500ms,再去创建Handler对象。这样就解决了空值问题。

myThread myThread=new myThread();
myThread.start();
try
{
Thread.sleep(500);
}catch (InterruptedException e){}
Handler handler=new Handler(myThread.looper){
    @Override
    public void handleMessage(Message msg){
       super.handleMessage(msg);
       //异步任务
       //可执行一些比较耗时的操作
       //不能执行UI更新操作
    }
 };
 Message msg= Message.obtain();
 handler.sendMessage(msg);

或者

myThread myThread=new myThread();
myThread.start();
try{
myThread.sleep(500);
}
catch (InterruptedException e){}
Handler handler=new Handler(myThread.looper){
    @Override
    public void handleMessage(Message msg){
       super.handleMessage(msg);
       //异步任务
       //可执行一些比较耗时的操作
       //不能执行UI更新操作
    }
 };
 Message msg= Message.obtain();
 handler.sendMessage(msg);

这时,我们在Handler对象里输出一条日志,内容为当前线程,我们会看到它输出的是子线程,而不是主线程

所以 ,我们的写在主线程的Handler也己经成功实现了异步执行,然后就可以在里面执行一些比较耗时的操作了,因为它是在子线程中执行的,所以我们不能执行UI更新操作

其实这里和之前的在子线程写一个Handler对象是一样的,只不过是分开写了罢了。

Google官方不推荐我们去自己自定义Looper线程,推荐我们直接使用己经封装好的Looper线程HandlerThread,它能为我们解决多线程并发问题,而且简单,易用。

HandlerThread是什么?
相信你现在应该已经知道了HandlerThread是什么了,HandlerThread其实就是Googler官方封装的Looper线程,可执行一些比较耗时的操作,同样,这个也不能执行UI更新操作,那么我们到底要怎么使用HandlerThread呢?请看下面:

HandlerThread使用方法:

HandlerThread HandlerThread=new HandlerThread("这个随便写");
HandlerThread.start();
Handler handler=new Handler(HandlerThread.getLooper()){
   @Override
   public void handleMessage(Message msg){
      super.handleMessage(msg);
      Log.d("嘿嘿",Thread.currentThread()+"");
      //异步任务
      //可执行一些比较耗时的操作
      //不能执行UI更新操作
      }
 };
 handler.sendEmptyMessage(1);

运行一下:

怎么样?学会了吗?使用HandlerThread还是很简单的。

写博客真是不容易啊!实在是心累呀!不过也加深了我对Handler的理解,所以不亏…哈哈哈。

本文到这里就结束了

希望大家能够喜欢。

猜你喜欢

转载自blog.csdn.net/weixin_45263548/article/details/92783103