Handler学习

在android开发中handler是我们用到的最多的类之一,在与应用程序交互时,比如从网上下载的图片如何更新到UI界面上呢?这时就会用到Handler机制,那如果不用到handler机制,或者在子线程中直接将图片更新到我们的UI当中,整个android当中就会给我们抛出一个异常,告诉我们不能在一个非ui线程中去直接更新ui。

在最初的学习android中,我们只知道在子线程中简单的发送一个sendMessage(Message msg),在发送一个消息之后,它会自动回调我们这个handler的handleMessage(Message msg)方法,那么为什么一旦发送消息之后,会自动回调到handleMessage方法呢?还有我们都知道handler经常与Looper和Message Quee经常关联,那么他们三者之间的关系又是什么呢,还有等等诸多的问题可能会困扰初学者。

一、handler是什么?
注:线程分为主线程(主线程又叫UI线程,只能有一个主线程)和子线程(可以有多个)Handler只能在主线程里运行。

handler是android给我们提供用来更新UI的一套机制,也是一套消息处理的机制,我们可以发送消息,也可以通过它处理消息。

二、为什么要用handler?

Android在设计的时候,就封装了一套消息创建、传递、处理机制,如果不遵循这样的机制,就没有办法更新UI信息,就会抛出异常信息。

三、handler的用法。

首先先用代码尝试下在子线程中来更新UI,看一下具体的异常信息。
布局文件如下:





  


在子线程中来给TextView赋值操作:

private TextView textView;
@Override
protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.activity_handler01);
     textView = findViewById(R.id.id_textview);
     //开辟一个子线程
     new Thread(){
      @Override
      public void run() {
          super.run();
      try {
            Thread.sleep(1000);
            textView.setText("update text");//在子线程给TextView赋值
         }catch (Exception e){
           e.printStackTrace();
       }
    }
  }.start();
}

运行会发现程序没有更新TextView,会看到日志抛出了一个异常:

Handler学习_第1张图片
image.png
这个异常就是告诉我们程序在非U主线程(UI线程)中进行去更新UI了。android是不允许这种机制去更新界面的。这个异常是从android.view.ViewRootImpl的checkThread方法抛出的,ViewRootImpl是ViewRoot的实现类。
:可以看到我们是让程序sleep()了一秒后开始更新UI的,如果没有sleep直接在run方法中给textview赋值操作,可以赋值,不会抛出异常,是因为ViewRootImpl(ViewRootImpl是在WindowManagerGlobal的addView方法中创建的。)的创建在onResume方法回调之后,而我们一开篇是在onCreate方法中创建了子线程并访问UI,在那个时刻,ViewRootImpl是没有创建的,无法检测当前线程是否是UI线程,所以程序没有崩溃一样能跑起来,而之后修改了程序,让线程休眠了1秒后,程序就崩了。很明显1秒后ViewRootImpl已经创建了,可以执行checkThread方法检查当前线程。在onCreate方法中创建的子线程访问UI是一种极端的情况,这个不仔细分析源码是不知道的。)

通过上面的程序我们可以看出在子线程中是不允许更新UI的,这时候我们就要用到handler了,修改程序如下:

private TextView textView;
//创建handler对象
private Handler handler=new Handler();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler01);
textView = findViewById(R.id.id_textview);
new Thread(){
@Override
public void run() {
super.run();
try {
Thread.sleep(1000);
//利用handler发送一个消息
handler.post(new Runnable() {
@Override
public void run() {
textView.setText("update text");
}
});

}catch (Exception e){
e.printStackTrace();
}
}
}.start();
}

handler的基本用法:
handler.post(Runnable r)
handler.postDelayed(Runnable r, long delayMillis)
handler.sendMessage(Message msg)
handler.sendEmptyMessage(int what)

我们利用handler.postDelayed(),每隔一秒来更新ImageView的图片。

private ImageView imageView;
private Handler handler = new Handler();
private int images[] = {R.drawable.image1, R.drawable.image2, R.drawable.image3};
//创建一个索引
private int index;
private MyRunnable myRunnable=new MyRunnable();
//创建一个Runnable 并实现run方法
class MyRunnable implements Runnable{
@Override
public void run() {
index++;
index=index%3;
//设置图片
imageView.setImageResource(images[index]);
//在runnable中不断去轮询这个图片 每隔一秒去执行这个操作
handler.postDelayed(myRunnable,1000);
}
}

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_handler01);
imageView = findViewById(R.id.ivImage);
handler.postDelayed(myRunnable,1000);
}

其他用法就不一一举例说明了。我们在new handler()的时候发现handler的构造还有一个传递Callback的方法Handler(Callback callback),callback回调里有一个返回值为boolean类型handleMessage方法。这个方法和handler方法体里面的void类型的handleMessage是有一定关系的,当Handler.Callback里的handleMessage返回值是false的时候,msg会被传递到handler方法体里面的void类型的handleMessage中,如果返回true,则代表被拦截了,就不会让msg传递下去。

private Handler handler = new Handler(new Handler.Callback() {
     @Override
     public boolean handleMessage(Message msg) {
           //返回true后  消息将会被拦截不会传到下面的handleMessage里   返回false后不会被拦截
             return false;
      }
  }){
    @Override
     public void handleMessage(Message msg) {
     super.handleMessage(msg);
      }
};

handler不只是可以发送消息,还可以移除一个消息,用到的方式是 handler.removeCallbacks(Runnable r);传递的参数是对应的创建的Runnable对象。

四、handler的原理是什么?(Handler、Looper、MessageQueen、Message的关系 )。

Handler学习_第2张图片
image.png
Thread是最基础的,Looper和MessageQueue都构建在Thread之上,Handler又构建在Looper和MessageQueue之上。

Looper(轮询器):
负责读取MessageQueen中的消息,读到消息之后就把消息交给Handler去处理。消息队列MessageQueue只是存储Message的地方,真正让消息队列循环起来的是Looper,这就好比消息队列MessageQueue是个水车,那么Looper就是让水车转动起来的河水。Looper.Loop方法,就是一个死循环,不断的从MessageQuee取消息,如果有消息就处理,没有消息就阻塞。

MessageQuee:

最基础最底层的是Thread,每个线程内部都维护了一个消息队列——MessageQueue,消息队列MessageQueue,顾名思义,就是存放消息的队列。
Message:
Handler接收和处理的消息对象

Handler:
Handler是暴露给开发者最顶层的一个类,其构建在Thread、Looper与MessageQueue之上。
Handler具有多个构造函数,签名分别如下所示:

  1. publicHandler()
  2. publicHandler(Callbackcallback)
  3. publicHandler(Looperlooper)
  4. publicHandler(Looperlooper, Callbackcallback)

第1个和第2个构造函数都没有传递Looper,这两个构造函数都将通过调用Looper.myLooper()获取当前线程绑定的Looper对象,然后将该Looper对象保存到名为mLooper的成员字段中。

第3个和第4个构造函数传递了Looper对象,这两个构造函数会将该Looper保存到名为mLooper的成员字段中。

第2个和第4个构造函数还传递了Callback对象,Callback是Handler中的内部接口,需要实现其内部的handleMessage方法,Callback代码如下:

public interface Callback {

public boolean handleMessage(Message msg); 
 
}

Handler.Callback是用来处理Message的一种手段,如果没有传递该参数,那么就应该重写Handler的handleMessage方法,也就是说为了使得Handler能够处理Message,我们有两种办法:

  1. 向Hanlder的构造函数传入一个Handler.Callback对象,并实现Handler.Callback的handleMessage方法
  2. 无需向Hanlder的构造函数传入Handler.Callback对象,但是需要重写Handler本身的handleMessage方法
    也就是说无论哪种方式,我们都得通过某种方式实现handleMessage方法,这点与Java中对Thread的设计有异曲同工之处。

下面我们通过一张图来理解Thread、MessageQuee、looper以及Handler的之间的关系。


Handler学习_第3张图片
image.png

我们可以把传送带上的货物看做是一个个的Message,而承载这些货物的传送带就是装载Message的消息队列MessageQueue。传送带是靠发送机滚轮带动起来转动的,我们可以把发送机滚轮看做是Looper,而发动机的转动是需要电源的,我们可以把电源看做是线程Thread,所有的消息循环的一切操作都是基于某个线程的。一切准备就绪,我们只需要按下电源开关发动机就会转动起来,这个开关就是Looper的loop方法,当我们按下开关的时候,我们就相当于执行了Looper的loop方法,此时Looper就会驱动着消息队列循环起来。

那Hanlder在传送带模型中相当于什么呢?
我们可以将Handler看做是放入货物以及取走货物的管道:货物从一端顺着管道划入传送带,货物又从另一端顺着管道划出传送带。我们在传送带的一端放入货物的操作就相当于我们调用了Handler的sendMessageXXX、sendEmptyMessageXXX或postXXX方法,这就把Message对象放入到了消息队列MessageQueue中了。当货物从传送带的另一端顺着管道划出时,我们就相当于调用了Hanlder的dispatchMessage方法,在该方法中我们完成对Message的处理。

补充:Thread就是ActivityThread,默认整个应用程序是通过ActivityThread创建的,是Android应用的主线程(UI线程),说起ActivityThread,不得不提到Activity的创建、启动过程以及ActivityManagerService, 以下引用自罗升阳大师的博客:《Android应用程序的Activity启动过程简要介绍和学习计划》

Step 1. 无论是通过Launcher来启动Activity,还是通过Activity内部调用startActivity接口来启动新的Activity,都通过Binder进程间通信进入到ActivityManagerService进程中,并且调用ActivityManagerService.startActivity接口;

Step 2. ActivityManagerService调用ActivityStack.startActivityMayWait来做准备要启动的Activity的相关信息;

Step 3. ActivityStack通知ApplicationThread要进行Activity启动调度了,这里的ApplicationThread代表的是调用ActivityManagerService.startActivity接口的进程,对于通过点击应用程序图标的情景来说,这个进程就是Launcher了,而对于通过在Activity内部调用startActivity的情景来说,这个进程就是这个Activity所在的进程了;

Step 4. ApplicationThread不执行真正的启动操作,它通过调用ActivityManagerService.activityPaused接口进入到ActivityManagerService进程中,看看是否需要创建新的进程来启动Activity;

Step 5. 对于通过点击应用程序图标来启动Activity的情景来说,ActivityManagerService在这一步中,会调用startProcessLocked来创建一个新的进程,而对于通过在Activity内部调用startActivity来启动新的Activity来说,这一步是不需要执行的,因为新的Activity就在原来的Activity所在的进程中进行启动;

Step 6. ActivityManagerServic调用ApplicationThread.scheduleLaunchActivity接口,通知相应的进程执行启动Activity的操作;

Step 7. ApplicationThread把这个启动Activity的操作转发给ActivityThread,ActivityThread通过ClassLoader导入相应的Activity类,然后把它启动起来。

你可能感兴趣的:(Handler学习)