【大树云微课堂】Android 中 Handler 类的介绍和使用

在了解 Handler 前,首先需要知道 Android 中,当程序启动时,Android 系统会启动一条线程,该线程也被称作 UI 线程,主要用于进行界面 UI 的操作。而又因为其不是线程安全的,所以 UI 线程有以下要求 : 只允许在 UI 线程中对界面 UI 进行操作且不允许在 UI 线程中进行耗时操作。这就导致了在进行操作时,必须启动一条新的线程,而想在新线程中,对 UI 进行操作,则需要借助到 Handler 类的消息传递机制来实现了

 

Handler 的作用

其作用主要有两个:进程间的消息发送和处理。借助这两个功能,可以在新线程中进行耗时操作,比如网络请求数据,在请求结束时,通过 Handler 类来向主线程发送消息;主线程则可以通过 Handler 来处理消息,根据消息来对界面 UI 进行操作。

Handler 的使用

按照正常的方法,创建类只需要调用它的构造函数即可,那么创建一个 Handler 类的代码应该如下

private val handler: Handler = Handler()


在创建完成后,就可以使用 Handler 的方法来发送消息了。sendMessage 方法接收的是一个 Message 参数,所以直接调用 Message 方法创建,创建之后就调用 sendMessage 方法即可。

val msg = Message()

// 设置 msg 的参数,可用于识别消息

msg.what= 10000

handler.sendMessage(msg)

那么在发送完信息之后,应该有个地方对 Message 消息进行处理,这时候需要用到 Handler 类的 handlerMessage 方法。
但是在 Handler 类源码中进行查看,可以看到该方法并没有进行实现,需要调用者主动去实现它,所以我们在创建 Handler 类时就不能通过直接调用构造函数的方式创建,而是通过继承并重写 handlerMessage 方法来创建一个 Handler 的子类。代码如下

private val handler: Handler = object : Handler() {

    override fun handleMessage(msg: Message?) {

        println(msg?.what)

    }

}


上述方法采用了 object 来创建一个 Handler 的匿名内部类,并重写了 handleMessage 方法,能够将消息的参数打印至控制台中。

这样,在调用 sendMessage 方法发送的消息,就能够在这里进行处理。

虽然上述 Handler 类的创建方法能够完成消息的发送处理的功能,但是此时编译器会提示该 Handler 可能会存在内存泄漏问题,原因是因为创建了一个内部 Handler 类,内存泄漏的具体原因在下面章节会讲解。既然内部类会出现问题,那么就直接写成嵌套类即可。


class MainActivity:BaseActivity(){

 override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

}

class MyHandler:Handler(){

        override fun handleMessage(msg: Message?) {

            println(msg?.what)

        }

    }

}

上述写法在 kotlin 语法下是不会提示内存泄漏的,可以正常使用。而在 Java 的写法下,还是会提醒可能会出现内存泄漏问题,这是因为在 Java 中,MyHandler 类仍然是内部类,而在 kotlin 中则是嵌套类。

嵌套类和内部类区别: 嵌套类只是单纯的将一个类写在另一个类的内部,两者没有关系,而内部类则是可以调用外部类的成员。

除了上述的创建方式外,还推荐另一种创建方式,更加方便快捷,并且在 Java 和 kotlin 语法中都适用。

// 实现 Handler.Callback 接口

private val handlerCallback = object:Handler.Callback{

        override fun handleMessage(msg: Message?): Boolean {

            println(msg?.what)

            return false

        }

    }

private val handler:Handler = Handler(handlerCallback)

该方法在调用 Handler 类的构造函数时,传入了 Handler.Callback 这个接口,该接口是 Handler 类所提供的,其代码如下

public interface Callback {

    public boolean handleMessage(Message msg);

}

该接口的作用时用来重写 Handler 的 handleMessage 方法,从而避免通过继承 Handler 子类来重写。在传入该接口的实现后,在处理消息时,就会调用该接口的方法。

所以 Handler 简单使用流程如下

class MainActivity : BaseActivity() {

    // 接口

    private val handlerCallback = object:Handler.Callback{

        override fun handleMessage(msg: Message?): Boolean {

            println(msg?.what)

            return false

        }

    }

    // Handler

    private val handler:Handler = Handler(handlerCallback)

      // 生命周期 onCreate

    override fun onCreate(savedInstanceState: Bundle?) {

        super.onCreate(savedInstanceState)

        setContentView(R.layout.activity_main)

        // 发送消息

        val msg = Message()

        msg.what = 1000

        handler.sendMessage(msg)

    }

}

上述代码中,会在控制台输出 1000


不同线程中使用

在文章最开始有提到,Handler 的主要作用是发送消息和处理消息,而且是在不同的线程间。上述的例子都只是在主线程中进行的消息的发送和处理。

虽然发送/处理消息都是一样的,但需要特别注意不同线程间使用会出现的问题。

对上述代码进行修改,在一个新的线程中进行消息的发送

// 接口

private val handlerCallback = object:Handler.Callback{

    override fun handleMessage(msg: Message?): Boolean {

        println("在 ${Thread.currentThread()} 进行处理")

        println(msg?.what)

        return false

    }

}

// Handler

private val handler:Handler = Handler(handlerCallback)

// 生命周期 onCreate

override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    // 新线程中发送消息

    Thread(Runnable {

        println("在 ${Thread.currentThread()} 进行发送")

        val msg = Message()

        msg.what = 1000

        handler.sendMessage(msg)

    }).start()

}

控制台输出如图所示,这表明了 Handler 确实能够做到在新线程中向主线程中发送消息(main 为主线程,Thread-2 为新线程)。

那么如果是主线程向新线程中发送消息会怎么样呢。首先我们先要在新线程中创建一个 Handler 类给主线程使用,修改上述代码如下

private val handlerCallback = object:Handler.Callback{

    override fun handleMessage(msg: Message?): Boolean {

        println("在 ${Thread.currentThread()} 进行处理")

        println(msg?.what)

        return false

    }

}

private lateinit var handler:Handler

override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    // 新线程中创建 Handler 类

    Thread(Runnable {

        handler = Handler(handlerCallback)

    }).start()

    // 线程停止 2s,保证 handler 已经创建完了

    Thread.sleep(2000)

    println("在 ${Thread.currentThread()} 进行发送")

    val msg = Message()

    msg.what = 1000

    handler.sendMessage(msg)

}


在执行上述代码时,系统抛出了异常,异常信息为 Can't create handler inside thread that has not called Looper.prepare(),这句话的意思是说不能够在一个没有调用 Looper.prepare() 的线程中创建 Handler 类。

那这个 Looper 类是什么呢,这个在下面章节再讲解,这里先给出能够正确执行的代码

private val handlerCallback = object:Handler.Callback{

    override fun handleMessage(msg: Message?): Boolean {

        println("在 ${Thread.currentThread()} 进行处理")

        println(msg?.what)

        return false

    }

}

private lateinit var handler:Handler

override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    // 新线程中创建 Handler 类

    Thread(Runnable {

        // 新增 Looper 方法

        Looper.prepare()

        handler = Handler(handlerCallback)

        // 新增 Looper 方法

        Looper.loop()

    }).start()

    // 线程停止 2s,保证 handler 已经创建完了

    Thread.sleep(2000)

    println("在 ${Thread.currentThread()} 进行发送")

    val msg = Message()

    msg.what = 1000

    handler.sendMessage(msg)

}


控制台输出结果如图,新增两个 Looper 的方法后,就能够正确的得到结果

Handler 的工作原理

首先需要知道的是使用 Handler 完成线程间的通信,会涉及到 4 个类,包括有 Handler,Message,Looper,MessageQueue。

  • Handler 的作用就是发送和处理消息
  • Message 则是消息的载体类。
  • Looper 则是一个线程的消息循环类,在其源码的注释有写到:
    该类用于为线程运行消息循环。默认情况下,线程没有与他们关联的消息循环,要创建一个消息循环,请现在运行循环的线程中调用 Looper.prepare() 方法,然后调用 Looper.loop() 方法来循环处理消息。
  • MessageQueue 则是一个消息队列类,用来管理改线程下的所有 Message 消息

Handler 每次发送的消息都会被发送到消息队列中存储。而 Looper 则会循环遍历消息队列中的消息,并交给对应的 Handler 来处理。这样就能够完成了 Handler 的消息发送和处理。

在了解完基本的工作原理后,回顾一下上述章节所提到的异常: Can't create handler inside thread that has not called Looper.prepare(), 这个异常是在 Handler 的构造函数中抛出的

// Handler 构造函数

mLooper = Looper.myLooper();

if (mLooper == null) {

    throw new RuntimeException(

        "Can't create handler inside thread " + Thread.currentThread()

                + " that has not called Looper.prepare()");

}
 

也就是说在 Looper.myLooper() 中获得了空值,该方法会获得该线程下的 Looper,如果该线程没有关联的 Looper,则会返回空值,从而报错。所有将 Looper 和线程关联起来的方法就是 Looper.prepare()。

需要注意的是每一个线程中都只有一个 Looper 和 MessageQueue,其中 MessageQueue 由 Looper 创建。也就是说当前线程只能调用一次 Looper.prepare() 方法,否则会报错。


private static void prepare(boolean quitAllowed) {

    // 不为空说明已经关联过了

    if (sThreadLocal.get() != null) {

        throw new RuntimeException("Only one Looper may be created per thread");

    }

    sThreadLocal.set(new Looper(quitAllowed));

}
 

如果只是按照异常提示加上 Looper.prepare() 方法而不加上Looper.loop() 方法,虽然不会报错,但 Handler 也不会进行消息的处理。如下所示:


override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    setContentView(R.layout.activity_main)

    // 新线程中创建 Handler 类

    Thread(Runnable {

        // 调用 Looper.prepare 方法

        Looper.prepare()

        handler = Handler(handlerCallback)       

        // 不调用该方法

        // Looper.loop()

    }).start()

    // 线程停止 2s,保证 handler 已经创建完了

    Thread.sleep(2000)

    println("在 ${Thread.currentThread()} 进行发送")

    val msg = Message()

    msg.what = 1000

    handler.sendMessage(msg)

}


控制台输出如上图所示,Handler 只发送了消息,但是却没有对消息进行处理。那是因为消息存储在消息队列中,但是没有 Looper 进行分发,Handler 无法获得该消息。

所以才需要调用 Looper.loop()方法,让 Looper 循环遍历消息队列中的消息进行分发。而在主线程使用 Handler 时,不需要执行 Looper 的方法,那是因为主线程在创建的过程中,就已经执行了这两步方法。


更多大樹雲資源,請參照:

★博文内容均由个人提供,与平台无关,如有违法或侵权,请与网站管理员联系。

★博文作者未开放评论功能