欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android开发——实现子线程更新UI

程序员文章站 2022-03-20 22:05:28
Android中线程按功能分的话,可以分为两个,一个是主线程(UI线程),其他的都是子线程 主线程不能执行那些耗时过长的代码或任务(执行耗时过长的代码会出现应用未响应的提示),所以都是使用子线程来执行耗时过长的代码,比如说下载文件等任务 一般情况,子线程中执行过长的代码,都是需要进行更新UI操作。 ......

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

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

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

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

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

Android开发——实现子线程更新UI

具体解释:

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

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

/**
 * 模拟下载
 */
fun download(): string {
    thread.sleep(1000)
    return "this is data"
}

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

1.activity.runonuithread()

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

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

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

btn_start.setonclicklistener {
    thread {
        val data = download()
        runonuithread({
            //这里进行更新ui操作
            tv_show.text = data
        })
    }
}

java版

btn_start.setonclicklistener(new onclicklistener(){
    new thread(new runnable(){
        string data = download();
        runonuithread(new runnable(){
            @override
            public void run() {
                tv_show.settext(data);
            }
        })
    }).start();
});

2.view.post()

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

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

btn_start.setonclicklistener {
    thread {
        val data = download()
        //选择当前activity的view对象都可以
        tv_show.post { 
            tv_show.text = data 
        }
    }
}

3.view.postdelayed()

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

btn_start.setonclicklistener {
    thread {
        val data = download()
        tv_show.postdelayed({
            tv_show.text = data
        },2000)
    }
}

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

4.handler.post()

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

private val handler = handler()

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

btn_start.setonclicklistener {
    thread {
        val data = download()
        handler.post {
            tv_show.text = data
        }
    }
}

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,从下面的代码可以看到,抽象方法的参数变为了我们的泛型的类型

internal inner class downloadingtask : asynctask<string, downloadingitem, downloadeditem>() {

    override fun onpreexecute() {
        //一些初始化操作
    }

    override fun doinbackground(vararg params: string?): downloadeditem {
        //params是一个参数数组,如果创建downloadingtask对象只传入了一个参数,直接取下标为0的那个即可(需要转型)
        //耗时操作(如下载操作),获得进度数据
        
        //将新的进度数据传递到onprogressupdate方法,更新ui
        publishprogress(messageitem)
        
        //任务执行完毕,返回结果(回调onpostexecute方法)
    }

    override fun onprogressupdate(vararg values: downloadingitem?) {
        //这里使用最新的进度来进行相关ui的更新
        //values是一个downloadingitem数组,取末尾那个即为最新的进度数据
    }

    override fun onpostexecute(result: downloadeditem?) {
        //下载成功提示或者是其他更新ui的操作
    }
}

执行:

执行task的时候需要在主线程(ui线程调用)

downloadingtask().execute("参数")

批量下载:

//允许在同一时刻有5个任务正在执行,并且最多能够存储50个任务
private val exec = threadpoolexecutor(5, 50, 10, timeunit.seconds, linkedblockingqueue<runnable>())
downloadingtask().executeonexecutor(exec, url)

6.handler机制实现(核心)

其实,handler机制是子进程更新ui的核心

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

Android开发——实现子线程更新UI

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

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

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

1.重写handler类中的handlemessage方法

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

private val handler =object : handler(){
    override fun handlemessage(msg: message?) {
        if (msg.what == 1) {
            //来源为1,则
        }
    }
}

2.发送message

val msg = handler.obtainmessage()
//一般都是规定好一个int的常量,来表示what
msg.what = 1
//传递int数据
msg.arg1 = 20
handler.sendmessage(msg)