说明

Handler 是Android提供的一个用于处理消息与可运行对象的组件,它有两个功能:

  1. 将消息与可运行对象在安排在未来某个时间点运行
  2. 在其它线程上操作队列

为什么提供Handler机制

Android在启动后会有一个主线程,它不允许子线程去改变主UI线程的内容,为了防止一些耗时操作导致主UI线程无响应而闪退,是Android提供的一种保护机制。在现实中确实有一些耗时长的需求使用异步线程,比如调用三方接口,加载完后来刷新UI,这种需要就可以用Android提供的 Handler 机制来实现,可以认为 Handler 是一个与Android主进程的通道

常用方法

sendMessage

发送消息

handleMessage

接收消息

obtainMessage

从全局消息pool获取一个Message对象

post

提交可运行对象,后缀带AtTime表示在指定时间提交,后续带Delayed表示提交延迟运行

removeMessages

移除消息队列中等待中的消息

hasMessages

判断消息队列中是否有某个指定消息

实战

实现一个指定时间切换图片的功能,最终效果如下

页面

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/RelativeLayout1"
        android:gravity="center">

    <ImageView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/imgAnimation"
            android:layout_alignParentLeft="true"
            android:layout_alignParentTop="true" />

</RelativeLayout>

代码比较简单,就是一个图片,居中显示

代码逻辑

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
var imgAnimation: ImageView? = null
var imgstart = 0
val imgids = arrayOf(
    R.drawable.pic1,
    R.drawable.pic2,
    R.drawable.pic3,
    R.drawable.pic4,
    R.drawable.pic5
)

private fun testHandler() {

    setContentView(R.layout.handler_sample)
    imgAnimation = findViewById(R.id.imgAnimation)
    val handler = ImgAnimationHandler()
    Timer().schedule(
        object : TimerTask() {
            override fun run() {
                handler.sendEmptyMessage(101)
            }
        }, 0, 500
    )
}

class ImgAnimationHandler : Handler() {

    override fun handleMessage(msg: Message) {
        if (msg.what == 101) {
            imgAnimation?.setImageResource(imgids[imgstart++ % imgids.size])
        }
    }
}
  1. 消息处理:主要是新建了一个 ImgAnimationHandler ,它覆写了 handleMessage 方法来处理信息,当消息what值=101 时,从 imgids 中顺序选择一个 sourceId 来达到图片切换的效果
  2. 发送消息:主线程中创建一个Timer的schedule线程每隔 500ms 通过 ImgAnimationHandler 发送消息 (what=101)

在子线程中调用Handler

在子线程中调用Handler,需要设置指定 Looper 的Handler(在创建 Handler 时,官方不再建议使用默认的构造函数创建 Handler,而是推荐 传入 Looper 实例作为构建参数的构造函数)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
class LooperThread extends Thread {
       public Handler mHandler;

       public void run() {
           Looper.prepare();

           mHandler = new Handler(Looper.myLooper()) {
               public void handleMessage(Message msg) {
                   // process incoming messages here
               }
           };

           Looper.loop();
       }
   }

如上最佳实践

  1. 使用 Looper.prepare() 来初始化当前线程为一个looper
  2. 创建 Handler 引用 与当前线程关联的 looper
  3. 调用 Looper.loop() 执行当前线程的消息队列

使用 Looper.getMainLooper() 可以方便的将其它线程的任务提交到UI线程