kotlin的多线程编程

线程的基本用法

定义一个线程,只需要新建一个类,继承自Thread,然后重写父类的run方法:

class MyThread : Thread() {
    override fun run() {
        //具体逻辑
    }
}

如何启动这个线程?只需要创建MyThread的实例,然后调用他的start方法即可,这样run方法中的代码就会在子线程中运行:

MyThread().start()

但是使用继承,耦合性相对也会比较高,所以我们会更多地使用runnable接口来定义一个线程:

class MyThread : Runnable {
    override fun run() {
        //具体逻辑
    }
}

而是用了这种写法,启动线程的方法也会发生改变:

val myThread = MyThread()
Thread(myThread).start()

我们创建的myThread是MyThread实例,它是一个实现了Runnable接口的对象,所以我们可以直接把他传入Thread的构造函数中。

接着我们再调用Thread的start方法,run方法中的内容就开始在子线程中运行了。

如果我们不想专门定义一个类去实现runnable接口,那可以使用lambda,这种更为常见:

Thread {
    //具体逻辑
}.start()

kotlin中给我们提供了更简单的启动方式:

thread {
    //具体逻辑
}

在子线程中更新ui:异步消息处理机制Handler

Android的ui也是线程不安全的,如果想要更新ui,必须在主线程中进行,否则会出现异常。

但是有的时候,我们必须在子线程中执行一些耗时任务,然后根据这些任务的执行结果来更新ui。

对此,Android提供了一套异步消息处理机制。它主要由4个部分组成:

Message 线程之间传递的消息
Handler 用于发送和处理消息
MessageQueue 消息队列,存放所有通过Handler发送的消息。每个线程中只会有一个MessageQueue。
Looper 每个线程中MessageQueue的管家,调用Looper的loop方法后,会进入一个无限循环中。每当发现MessageQueue中存在一个Message时,就会将它取出并传入到Handler中。每个线程只有一个Looper。

了解了异步消息处理机制后,我们把整一个处理流程走一遍。

  • 在主线程中创建一个Handler对象,重写handlerMessage方法
  • 当子线程需要进行ui操作时,创建一个message对象,并通过handler发送出去
  • handler发送的消息会传入到MessageQueue中等待被处理,而Looper会一直尝试从MessageQueue获取消息
  • 由于在handler的构造函数中,我们传入了Looper.getMainLooper方法,因此此时我们在第一步重写的handlerMessage方法会在主线程中运行,此时我们可以放心的进行ui操作

kotlin的多线程编程_第1张图片

而一条message经过图示中的处理后,也就从子线程进入了主线程,从不能更新ui变成了可以。

AsyncTask 

为了更方便在子线程操作ui,android提供了AsyncTask,即使对异步消息处理机制完全不了解,也可以十分简单的使用。

首先AsyncTask是一个抽象类,所以我们想要使用他,就必须用一个类去继承他。在继承时我们可以为AsyncTask提供3个泛型参数:

Params 执行AsyncTask时需要传入的参数,可用于在后台任务中使用
Progress
在后台任务执行时,如果需要在界面上显示当前的进度,则使用这里指定的泛型作为进度单位
Result
当任务执行完毕后,如果需要对结果进行返回,则使用这里指定的泛型作为返回值类型

因此,一个自定义AsyncTask可以写成如下形式:

class DownloadTask : AsyncTask() {
    ...
}

此处我们传入了3个泛型参数,第一个指定为Unit,表示在执行AsyncTask的时候不需要传入参数给后台任务;第二个指定为Int,表示使用整型来作为进度展示单位;第三个指定为Boolean,表示使用它来反馈执行结果。

目前我们自定义的DownloadTask还是一个空任务,我们需要重写AsyncTask的方法,经常需要重写的有4个:

onPreExecute() 在后台任务开始执行之前调用,用于进行一些初始化操作
doInBackground(Params...)

这个方法中的所有代码都会在子线程中运行,因此我们把所有耗时任务都放在此处。

任务一旦完成,return回执行结果。如果AsyncTask的第三个参数是Unit,可以不返回执行结果。

因为是在此执行子线程操作,所以不能进行ui操作,如果需要更新ui,可以调用publishProgress(Progress...)方法来完成

onProgressUpdate(Progress...)

当在后台任务中调用了调用publishProgress(Progress...)方法,方法将会很快被调用,而该方法中携带的参数,就是从后台任务中传递过来的。

我们在这个方法中可以进行ui操作,利用参数中的值就可以对ui更新。

onPostExecute(Result) 当后台任务执行完毕并return时,该方法很快会被调用。返回的数据会传入该方法中,可以利用这些数据进行ui操作。

你可能感兴趣的:(kotlin,开发语言,android)