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

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

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

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

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

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

具体解释:

点击按钮,之后开启一个子线程来模拟下载过程(线程休眠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机制实现的

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

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)

原文地址:https://www.cnblogs.com/kexing/p/11666803.html

时间: 2024-08-07 09:25:57

Android开发——实现子线程更新UI的相关文章

Android子线程更新UI主线程方法之Handler

背景: 我们开发应用程序的时候,处于线程安全的原因子线程通常是不能直接更新主线程(UI线程)中的UI元素的,那么在Android开发中有几种方法解决这个问题,其中方法之一就是利用Handler处理的. 下面说下有关Handler相关的知识. 多线程一些基础知识回顾:在介绍Handler类相关知识之前,我们先看看在Java中是如何创建多线程的方法有两种:通过继承Thread类,重写Run方法来实现通过继承接口Runnable实现多线程 具体两者的区别与实现,看看这篇文章中的介绍:http://de

子线程更新UI界面的2种方法

一.一般我们都会在子线程完成一些耗时的操作. 1.Android中消息机制: 2.知识点: Message:消息,其中包含了消息ID,消息处理对象以及处理的数据等,由MessageQueue统一列队,终由Handler处理.                      Handler:处理者,负责Message的发送及处理.使用Handler时,需要实现handleMessage(Message msg)方法来对特定的Message进行处理,例如更新UI等.                    

WPF Dispatcher.BeginInvoke子线程更新UI

原文:WPF Dispatcher.BeginInvoke子线程更新UI 在开发WPF应用时出现:"调用线程无法访问此对象,因为另一个线程拥有该对象." 是因为UI线程是WPF应用的主线程,若尝试子线程更新UI线程应使用Dispatcher.BeginInvoke()或者Invoke()方法. Dispatcher.BeginInvoke() //异步执行,不等待委托结束就更新 Dispatcher.Invoke()          //代表同步执行 Action()       

Python+PyQT5的子线程更新UI界面的实例《新手必学》

今天小编就为大家分享一篇Python+PyQT5的子线程更新UI界面的实例,具有很好的参考价值,希望对大家有所帮助.一起跟随小编过来看看吧子线程里是不能更新UI界面的,在移动端方面.Android的UI访问是没有加锁的,多个线程可以同时访问更新操作同一个UI控件.也就是说访问UI的时候,android系统当中的控件都不是线程安全的,这将导致在多线程模式下,当多个线程共同访问更新操作同一个UI控件时容易发生不可控的错误.所以Android中规定只能在UI线程中访问UI,相当于从另一个角度给Andr

Qt 子线程更新Ui

最近做练习,写一个Qt版的飞机大战,需要用子线程更新UI,发现Qt子线程不能更新Ui,否则程序会崩溃.在网上百度了下,说是需要在子线程自定义信号,然后在线程回调的run()函数里发射信号,主线程连接信号和槽,然后在槽函数里面更新UI.虽然最后发现这个办法对我写飞机大战没有啥帮助,但是感觉这个办法还是需要总结下来的.(最后我是用的定时器更新的UI界面) 废话不多说,下面是子线程更新UI的方法: 第一步:写一个线程类,继承自QThread 第二步:自定义信号 class CMyThread : pu

C#子线程更新UI控件的方法总结

http://blog.csdn.net/jqncc/article/details/16342121 在winform C/S程序中经常会在子线程中更新控件的情况,桌面程序UI线程是主线程,当试图从子线程直接修改控件属性时会出现“从不是创建控件的线程访问它”的异常提示. 跨线程更新UI控件的常用方法有两种: 1.使用控件自身的invoke/BeginInvoke方法 2.使用SynchronizationContext的Post/Send方法更新 1.使用控件自身的invoke/BeginIn

Android 子线程 更新 UI 界面 总结

package com.jrhcode.morethreadtest; import java.util.concurrent.Executors; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; import android.app.Activity; import android.view.Menu; import android.widget.TextView; import

Android 子线程更新UI 异常

众所周知,Android是不可以在子线程中直接更新UI的,需要借助Handler或者View.post(Runnable runnable)或者runOnUIThread(Runnable runnable)将更新的代码切入到主线程中去实现UI更新. 我们来试一下,在Activity的 onCreate中直接在新线程中去更新一个TextView的文本,结果发现,WHAT?竟然没有抛出异常,更新也成功了? 这是因为在onCreate中尚未完成View的绘制,此时TextView中有个变量mLayo

iOS开发- reloadData无效 (子线程更新UI错误)

今天在写一个聊天工具的时候遇到了一个问题. 注册的通知里面, 每当有其他用户发来消息的时候,  UITableView 就要重新更新 [objc] view plaincopy cell.textLabel.textColor = [UIColor redColor]; 使用红色标记cell名字来突出显示新消息.  (当然,, 这只是个demo, 效果比较渣.  正常情况应该是用户头像跳动或者显示新消息条数...) 可是我发现,  调用了这样的更新语句后, UITableVIew里面的数据并没有