经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 移动开发 » Android » 查看文章
Android开发——实现子线程更新UI - Stars-one
来源:cnblogs  作者:Stars-one  时间:2019/10/14 9:40:27  对本文有异议

Android中线程按功能分的话,可以分为两个,一个是主线程(UI线程),其他的都是子线程

主线程不能执行那些耗时过长的代码或任务(执行耗时过长的代码会出现应用未响应的提示),所以都是使用子线程来执行耗时过长的代码,比如说下载文件等任务

一般情况,子线程中执行过长的代码,都是需要进行更新UI操作。

但是Android中,为了防止安全,是不允许在子线程更新UI的,但是我们可以使用到Android官方给予的API来实现子线程更新UI的操作(本质上,这些API也是切换回了主线程来进行更新UI)

例子:点击一个按钮,过了1s后完成了下载任务,返回了数据,此数据会显示在界面上

具体解释:

点击按钮,之后开启一个子线程来模拟下载过程(线程休眠1s),之后任务执行完毕会返回数据(一个String),使用返回的数据更新UI

新建一个方法,用来模拟下载任务

  1. /**
  2. * 模拟下载
  3. */
  4. fun download(): String {
  5. Thread.sleep(1000)
  6. return "this is data"
  7. }

下面的使用6种方式和上面的模拟下载任务的方法,来实现上面例子的效果

1.Activity.runOnUiThread()

runOnUiThread是Activity中的方法,只有当前对象是Activity,就可以直接使用,如果当前的对象不是Activity,需要找到Activity对象,才能执行此方法

runOnUiThread方法的参数为一个Runnable接口,我使用的kotlin,所以有很多东西都是省略了

设置按钮的点击事件,点击按钮开启一个线程

  1. btn_start.setOnClickListener {
  2. thread {
  3. val data = download()
  4. runOnUiThread({
  5. //这里进行更新UI操作
  6. tv_show.text = data
  7. })
  8. }
  9. }

Java版

  1. btn_start.setOnClickListener(new OnClickListener(){
  2. new Thread(new Runnable(){
  3. String data = download();
  4. runOnUiThread(new Runnable(){
  5. @Override
  6. public void run() {
  7. tv_show.setText(data);
  8. }
  9. })
  10. }).start();
  11. });

2.View.post()

post方法是View对象的方法,参数也是接收一个runnable接口

这里我选用的view对象是需要进行更新textview的本身,当然也可以选用其他的View对象,只要是在当前Activity的对象都可以

  1. btn_start.setOnClickListener {
  2. thread {
  3. val data = download()
  4. //选择当前Activity的View对象都可以
  5. tv_show.post {
  6. tv_show.text = data
  7. }
  8. }
  9. }

3.View.PostDelayed()

此方法和上面的post方法类似,只是多一个参数,用来实现延迟更新UI的效果

  1. btn_start.setOnClickListener {
  2. thread {
  3. val data = download()
  4. tv_show.postDelayed({
  5. tv_show.text = data
  6. },2000)
  7. }
  8. }

上面的代码实现的效果是点击按钮之后,过了3s后才会看到界面发生改变

4.Handler.post()

new一个Handler对象(全局变量)

  1. private val handler = Handler()

使用post方法更新UI,此post方法和之前的post方法一样,参数都是为Runnable接口

  1. btn_start.setOnClickListener {
  2. thread {
  3. val data = download()
  4. handler.post {
  5. tv_show.text = data
  6. }
  7. }
  8. }

5.AsyncTask(推荐)

说明

AsyncTask是一个抽象类,必须创建一个子类类继承它

这里介绍一下关于AsyncTask的三个泛型参数和几个方法

泛型参数可以为任意类型,为空的话使用Void

参数 说明
params 参数泛型,doInBackground方法的参数
progress 进度泛型,onProgressUpdate方法的参数
result 结果泛型,onPostExecute方法的参数

抽象方法说明:

方法名 说明
onPreExectute() 此方法中,常常进行初始化操作,如进度条显示
doInBackground(Params...) 此方法必须实现,
onProgressUpdate(Progress...) 进行更新UI的操作
publishProgress(Progress...) 在doInBackground方法中调用,调用此方法后会回调执行onProgressUpdate方法进行更新UI
onPostExcute(Result) 任务结束之后进行更新UI

简单来说,如果子类继承了AsyncTask,它的抽象方法的参数都会变成泛型对应的类型

例子

下面的代码是取自我的APP,简单地说明一下AsyncTask<String, DownloadingItem, DownloadedItem>

我传入的是3个泛型参数分别为StringDownloadingItemDownloadedItem,分别对应的paramsprogressresult泛型

这里我是根据自己的需要而两个类DownloadingItemDownloadedItem,从下面的代码可以看到,抽象方法的参数变为了我们的泛型的类型

  1. internal inner class DownloadingTask : AsyncTask<String, DownloadingItem, DownloadedItem>() {
  2. override fun onPreExecute() {
  3. //一些初始化操作
  4. }
  5. override fun doInBackground(vararg params: String?): DownloadedItem {
  6. //params是一个参数数组,如果创建DownloadingTask对象只传入了一个参数,直接取下标为0的那个即可(需要转型)
  7. //耗时操作(如下载操作),获得进度数据
  8. //将新的进度数据传递到onProgressUpdate方法,更新UI
  9. publishProgress(messageItem)
  10. //任务执行完毕,返回结果(回调onPostExecute方法)
  11. }
  12. override fun onProgressUpdate(vararg values: DownloadingItem?) {
  13. //这里使用最新的进度来进行相关UI的更新
  14. //values是一个DownloadingItem数组,取末尾那个即为最新的进度数据
  15. }
  16. override fun onPostExecute(result: DownloadedItem?) {
  17. //下载成功提示或者是其他更新UI的操作
  18. }
  19. }

执行:

执行Task的时候需要在主线程(UI线程调用)

  1. DownloadingTask().execute("参数")

批量下载:

  1. //允许在同一时刻有5个任务正在执行,并且最多能够存储50个任务
  2. private val exec = ThreadPoolExecutor(5, 50, 10, TimeUnit.SECONDS, LinkedBlockingQueue<Runnable>())
  3. DownloadingTask().executeOnExecutor(exec, url)

6.Handler机制实现(核心)

其实,Handler机制是子进程更新UI的核心

我们上面的五种实现子进程更新UI的方式,都是基于Handler机制实现的

具体机制本文就不多说了,网上有许多的机制说明,这里就只讲一下实现的步骤

Message中有几个属性,whatarg1arg2,这三个都是接收一个Int,所以,传递数据不是很友好,这里就不准备实现之前的例子效果了

what表示来源,arg1和arg2用来传递Int数据

1.重写Handler类中的handleMessage方法

一般都是规定好一个Int的常量,来表示what

  1. private val handler =object : Handler(){
  2. override fun handleMessage(msg: Message?) {
  3. if (msg.what == 1) {
  4. //来源为1,则
  5. }
  6. }
  7. }

2.发送Message

  1. val msg = handler.obtainMessage()
  2. //一般都是规定好一个Int的常量,来表示what
  3. msg.what = 1
  4. //传递Int数据
  5. msg.arg1 = 20
  6. handler.sendMessage(msg)

原文链接:http://www.cnblogs.com/kexing/p/11666803.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号