我不是罗大锤我不是罗大锤

我不是罗大锤

我不是罗大锤我不是罗大锤

我不是罗大锤

首页首页
分类分类
标签标签
友情链接友情
日记日记
开发中
博客仍在开发中。
Powered byNola
Android 多线程Android 多线程

Android 多线程

&Android

允许评论

3 年前

耗时操作需要放在子线程中运行,否则会导致主线程被阻塞,从而影响用户对软件的正常使用。

一、线程基本用法

Kotlin 中使用线程方法和 Java 类似,可以选择继承 Thread 类或实现 Runnable 接口来实现线程,而Kotlin 还给我们提供了一种更加简单的开启线程的方法,写法如下:

thread {
    // 编写具体逻辑
}

thread 是 Kotlin 一个内置顶层函数,只需要在 Lambda 中编写逻辑即可,start() 也不需要。

二、在子线程中更新 UI——异步消息处理机制

class MainActivity : AppCompatActivity() {
    // 消息标记
    val updateText = 1
    
    val handler = object : Handler() {
        override fun handleMessage(msg: Message) {
            // 在这里进行UI操作,这里是在主线程中
            when(msg) {
                updateText -> {}
            }
        }
    }
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        thread {
            val msg = Message()
            msg.what = updateText
            // 将Message对象发送出去
            handler.sendMessage(msg)
        }
    }
}

Android中的异步消息处理主要由 4 个部分组成,Message、Handler、MessageQueue、Looper。

  1. Message Message是在线程之前传递的消息,内部可以携带少量信息并在线程间传递,除了what字段,还可以使用arg1和arg2字段来携带一些整形数据,obj字段携带一个Object对象。
  2. Handler 处理者,主要用于发送和处理消息,发送消息一般用sendMessage()或post(),发出的消息经过一系列辗转处理后最终传递到Handler的handleMessage()方法中。
  3. MessageQueue 消息队列,用于存放所有通过Handler发送的消息,消息会一直存在于消息队列中等待被处理,每个线程中只有一个MessageQueue对象。
  4. Looper 每个线程中的MessageQueue管家,调用Looper的loop()方法后,就会进入一个无限循环中,每当在MessageQueue中发现一条消息时,就将它取出并传递到Handler的handleMessage()方法中,每个线程中只有一个Looper对象。

Android多线程编程_1.jpg

三、在子线程中更新 UI——AsyncTask

AsyncTask 可能即将废弃。为了更方便在子线程中对 UI 进行操作,Android 还提供了 AsyncTask 等一些好用的工具,借助 AsyncTask 即使对异步消息处理机制完全不了解,也可以十分简单的从子线程切换到主线程,AsyncTask 实现原理也是基于异步消息处理机制的,Android 帮我们做了很好的封装。

AsyncTask 是一个抽象类,在继承时可以为 AsyncTask 指定 3 个泛型参数,这三个参数用途如下:

  1. Params 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
  2. Progress 后台执行任务时,如需在界面显示当前进度,使用这里指定的泛型作为进度单位。
  3. Result 任务执行完毕后,如需对结果进行返回,则使用这里指定的泛型作为返回值类型。 一个比较完整的自定义 AsyncTask 可以写成如下形式:
class DownloadTask : AsyncTask<Unit, Int, Boolean>() {
    override fun onPreExecute() {
        // 显示进度对话框
        progressDialog.show()
    }
    
    override fun doInBackground(vararg params: Unit?) = try {
        while (true) {
            // 虚构方法
            val downloadPercent = doDownload()
            // 调用onProgressUpdate()方法并传入参数
            publishProgress(downloadPercent)
            if (downloadPercent >= 100) {
                break
            }
        }
        true
    } catch (e: Exception) {
        false
    }
    
    override fun onProgressUpdate(vararg values: Int?) {
        // 在这里更新下载进度,这里可以操作UI
        progressDialog.setMessage("Download ${values[0]}%")
    }
    
    override fun onPostExecute(result: Boolean) {
        // 关闭进度对话框
        progressDialog.dismiss()
        if(result) {
            ...
        } else {
            ...
        }
    }
}

// 启动任务
fun main() {
    // execute()可以传参,会到doInBackground()中
    DownloadTask().execute()
}

4 个重写方法说明:

  1. onPreExecute() 这个方法会在后台任务开始执行之前调用,用于进行一些界面上的初始化操作,比如显示一个进度条对话框。
  2. doInBackgroung(Params…) 方法中所有代码都会在子线程中进行,应该在这里去处理所有的耗时任务,任务一旦完成就可以通过return语句将执行结果返回,如果AsyncTask第三个泛型参数指定Unit,就可以不返回任务执行结果。注意这个方法中不可以进行UI操作,如需更新UI参数,可以调用publishProgress(Progress…)方法来完成。
  3. onProgressUpdate(Progress…) 在后台任务中调用publishProgress()方法后,onPregressUpdate()方法很快就会被调用,该方法中参数就是在后台任务中传递过来的,在这个方法里可以对UI进行操作,利用参数中的数值对界面元素进行相应的更新。
  4. onPostExecute(Result) 当后台任务执行完毕并通过return语句进行返回时,这个方法很快就会被调用,返回的数据会作为参数传递到此方法中,可以利用返回的数据进行一些UI操作比如说提醒任务执行的结果,以及关闭进度条对话框等。

四、在子线程中更新 UI——runOnUiThread()

runOnUiThread() 方法对异步消息处理机制进行了一层封装,它背后的工作原理二中的 Handler、Message 消息机制是一样的,借助这个方法就可以在线程中将数据更新到界面上:

private fun showResponse(response: String) {
    runOnUiThread {
        // 在这里进行UI操作,将结果显示到界面上行
    }
}
目录
一、线程基本用法
二、在子线程中更新 UI——异步消息处理机制
三、在子线程中更新 UI——AsyncTask
四、在子线程中更新 UI——runOnUiThread()
暂无评论