Android系统规定只能在主线程更新UI,当子线程有更新UI的需求时,可以通过线程间通信,将信息传递给主线程,让主线程进行UI更新。
Handler
Handler是实现线程间通信的主要手段,Message是信息的载体。子线程通过Handler将Message发送到消息队列MessageQueue,主线程的Looper循环访问消息队列,发现一有消息就取出进行处理。这样便实现了异步消息机制。
Handler作用:异步消息处理,实现线程间通信
使用方法及原理
-
继承Handler,重写
handleMessage(Message)方法inner class MyHandler : Handler(Looper.getMainLooper()) { override fun handleMessage(msg: Message) { when (msg.what) { WHAT_SAY_HELLO -> { tvContent.setText("Hello,what is your name!") } WHAT_SAY_HI -> tvContent.setText("Hi,my name is mmc.") else -> {} } super.handleMessage(msg) } } -
当子线程需要更新UI时,创建Message,通过Handler实例发送消息
private val mHandler: MyHandler by lazy { MyHandler() } thread { Thread.sleep(500) mHandler.run { //sendMessage(obtainMessage(WHAT_SAY_HI)) obtainMessage(WHAT_SAY_HI).sendToTarget() } } -
Message被推送到MessageQueue队尾,Looper循环遍历该队列,取出待处理的消息分发给Handler,Handler调用
handleMessage(Message)方法进行处理。
注意:
实现Handler时,需要显示传入一个Looper,一般传入主线程的Looper。如果调用了无参的
Handler(),将隐式传入当前线程的Looper,如果当前线程没有Looper,该Handler将无法接收消息,导致异常。获取Message时,可以直接new一个实例,也可以通过
Handler.obtain()从全局消息池返回一个Message。后者省去了创建实例的开销,更加高效。可直接调用Message.sendToTarget()发送消息。
Handler.sendMessage(Message)将消息发送到消息队列,实现异步消息机制;
Handler.handleMessage(Message)直接调用了Hanler的handleMessage(Message)方法,不通过线程池,不能实现异步消息;
Handler.dispatchMessage(Message),间接调用了handleMessage(Message),类似Handler.handleMessage(Message)。
AsyncTask
如果觉得Handler使用起来麻烦,也可以直接使用线程的AsyncTask,它的底层就是Handler。
-
使用方法:实现AsyncTask,指定三个泛型参数类型,第一个泛型参数为创建实例时传入的参数,第二个为执行后台任务过程中通知前台线程更新UI的数据类型,第三个参数为后台任务执行完成的返回值类型。
private inner class MyAsyncTask() : AsyncTask<Int, Int, String>() { //后台任务开始前准备动作 override fun onPreExecute() { btn.run { setText("Start loading...") isEnabled = false } } //执行后台任务 override fun doInBackground(vararg params: Int?): String { var progress = params?.get(0) ?: 0 while (progress < 100) { Thread.sleep(100) publishProgress(progress++) //通知前台线程更新UI } return "Loading done!" } //接收后台任务的消息更新UI override fun onProgressUpdate(vararg values: Int?) { values?.get(0)?.let { progress.progress = it } } //后台任务执行完成更新UI override fun onPostExecute(result: String?) { progress.progress = 100 btn.run { setText(result) } } } -
执行AsyncTask,注意一个AsyncTask实例只能执行一次,并且AsyncTask只能在主线程创建。
myAsyncTask.execute(5)