Android开发线程间的交互之异步任务(AsyncTask)

2024-08-28 16:58

本文主要是介绍Android开发线程间的交互之异步任务(AsyncTask),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、AsyncTask简介

AsyncTask是对Handler与线程池的封装。更新用户界面的操作还是在主线程中完成的,但是由于AsyncTask内部包含一个Handler,所以可以发送消息给主线程让它更新UI。另外,AsyncTask内还包含了一个线程池。使用线程池的主要原因是避免不必要的创建及销毁线程的开销。

二、AsnycTask参数及方法介绍

AsyncTask是一个抽象类,我们在使用时需要定义一个它的派生类并重写相关方法。

class MyAnyscTask extends AsyncTask<Params,Progress, Result> {}

参数的含义如下:

  • Params:doInBackground方法的参数类型;
  • Progress:AsyncTask所执行的后台任务的进度类型;
  • Result:后台任务的返回结果类型。
    class MyAnyscTask extends AsyncTask<Void,Void,Void>{/*** 此方法中定义要执行的后台任务,在这个方法中可以调用 * publishProgress来更新任务进度 * (publishProgress内部会调用onProgressUpdate方法)* @param params* @return*/@Overrideprotected Void doInBackground(Void... params) {return null;}/*** 此方法会在后台任务执行前被调用,用于进行一些准备工作*/@Overrideprotected void onPreExecute() {super.onPreExecute();}/*** 后台任务执行完毕后,此方法会被调用,参数即为后台任务的返回结果*/@Overrideprotected void onPostExecute(Void aVoid) {super.onPostExecute(aVoid);}/*** 由publishProgress内部调用,表示任务进度更新*/@Overrideprotected void onProgressUpdate(Void... values) {super.onProgressUpdate(values);}}

注:AsyncTask的方法中除了doInBackground是运行在子线程中做耗时操作外,其他几个都是运行在主线程中

三、AsnycTask使用

前面介绍的AsyncTask的参数以及方法,下面就使用AsyncTask来模拟一个下载的例子。

layout布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:orientation="vertical"android:layout_width="match_parent"android:layout_height="match_parent"><ProgressBarandroid:id="@+id/test_ansyctask_download_pb"style="?android:attr/progressBarStyleHorizontal"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"/><Buttonandroid:id="@+id/test_ansyctask_download_btn"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_margin="10dp"android:text="开始下载" />
</LinearLayout>

Activity

//下载进度条
mProgressBar =(ProgressBar)findViewById(R.id.test_ansyctask_download_progressBar);
//开始下载按钮
mBtn = (Button)findViewById(R.id.test_ansyctask_download_btn);
//为按钮添加点击事件
mBtn.setOnClickListener(this);

在onClick中调用AnyscTask.execute()执行异步任务

@Override
public void onClick(View v) {myAnyscTask = new MyAnyscTask();//执行异步任务myAnyscTask.execute();
}

自定义AnyscTask

class MyAnyscTask extends AsyncTask<Void,Integer,Void> {@Overrideprotected void onPreExecute() {super.onPreExecute();}@Overrideprotected Void doInBackground(Void... params) {//该方法运行在子线程,用于耗时操作。模拟http下载 1s更新一次进度for(int i = 0; i < 100; i ++){publishProgress(i);try{Thread.sleep(1000);}catch (Exception e){e.printStackTrace();}}return null;}@Overrideprotected void onPostExecute(Void aVoid) {super.onPostExecute(aVoid);}@Overrideprotected void onProgressUpdate(Integer... values) {super.onProgressUpdate(values);//更新进度条mProgressBar.setProgress(values[0]);}}

这样一个简单的异步任务例子就完成了。

四、AsnycTask取消

运行上面的例子没有什么问题,但是当进度条开始加载一段时间后(没有加载完成)退出当前activity然后再次进入,点击开始按钮就会发现需要等一会儿进度条才会再次执行。这是因为AsyncTask内包含了一个线程池。需要等到上一次任务结束才会执行当前任务。为了让AsnycTask马上取消正在执行的任务,我们需要在停止的方法里面调用

//判断myAnyscTask不为空且正在运行
if(myAnyscTask!=null&&myAnyscTask.getStatus()==AsyncTask.Status.RUNNING){//Asynctask设置cancelled仅仅只是通知Asynctask设置一个取消标志,Asynctask并不能马上退出myAnyscTask.cancel(true);
}

然后在doInBackground中退出正在执行的任务

@Override
protected Void doInBackground(Void... params) {for(int i = 0; i < 100; i ++){//判断当myAnyscTask.isCancelled()标记为取消时,退出耗时操作if(myAnyscTask.isCancelled()){break;}publishProgress(i);try{Thread.sleep(1000);}catch (Exception e){e.printStackTrace();}}return null;
}

五、AsyncTask机制原理

在了解AsyncTask的内部原理的时候需要了解ArrayDeque和FutureTask类

ArrayDeque

数据结构的物理实现方式主要还是两种:链表和数组

Java中提供了具体实现队列的类ArrayDeque(循环队列,依赖于可变数组来实现的)

ArrayDeque不是线程安全的。

ArrayDeque不可以存取null元素,因为系统根据某个位置是否为null来判断元素的存在。 当作为栈使用时,性能比Stack好;当作为队列使用时,性能比LinkedList好。

方法:

//向队列中插入一个元素,并返回true 如果队列已满,抛出IllegalStateException异常
boolean add(E e);//向队列中插入一个元素,并返回true 如果队列已满,返回false
boolean offer(E e);//取出队列头部的元素,并从队列中移除 队列为空,抛出NoSuchElementException异常
E remove();//取出队列头部的元素,并从队列中移除 队列为空,返回null
E poll();//取出队列头部的元素,但并不移除 如果队列为空,抛出NoSuchElementException异常
E element();//取出队列头部的元素,但并不移除 队列为空,返回null
E peek(); 
FutureTask

FutureTask可用于异步获取执行结果或取消执行任务的场景。是Future 的一个实现。通过get()方法可以异步获取执行结果,不论FutureTask调用多少次run()或者call()方法,它都能确保只执行一次Runable或Callable任务。因此,FutureTask非常适合用于耗时高并发的计算,另外可以通过cancel()方法取消执行任务。

方法:

 1.get() //阻塞一直等待执行完成拿到结果2.get(int timeout, TimeUnit timeUnit)//阻塞一直等待执行完成拿到结果,如果在超时时间内,没有拿到抛出异常 3.isCancelled()//是否被取消4.isDone()//是否已经完成5.cancel(boolean mayInterruptIfRunning)//试图取消正在执行的任务

使用:

 1、 新建异步任务Task task = new Task();2、定义FutrueTaskFutureTask<Integer> future = new FutureTask<Integer>(task) {// 异步任务执行完成,回调@Overrideprotected void done() {try {System.out.println("future.done():" + get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}}};3、 创建线程池 执行futrueExecutorService executor = Executors.newCachedThreadPool();executor.execute(future);//执行的时候会回调Callable(即task)的call方法4、获取异步任务返回值try {// 阻塞,等待异步任务执行完毕-获取异步任务的返回值System.out.println("future.get():" + future.get());} catch (InterruptedException e) {e.printStackTrace();} catch (ExecutionException e) {e.printStackTrace();}5、自定义异步任务static class Task implements Callable<Integer> {// 返回异步任务的执行结果@Overridepublic Integer call() throws Exception {int i = 0;for (; i < 10; i++) {try {System.out.println(Thread.currentThread().getName() + "_"+ i);Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}}return i;}}
AsyncTask的源码分析

1、自定义AsyncTask的子类MyAsyncTask

AsyncTask构造函数中初始化三个对象 WorkerRunnable<Params, Result> mWorker; FutureTask mFuture;Handler mHandler;

<1> mWorker实现了Callable接口的类,内部的call方法调用MyAsyncTask的result = doInBackground()并得到返回值。执行完毕后调用postResult(result)方法<2> postResult(result)方法中使用mHandler将result发送给主线程,并调用finish方法。<3> finish方法中会判断任务是否取消。如果取消则调用MyAsyncTask的onCancelled方法,否则就会调用MyAsyncTask的onPostExecute方法

2、MyAsyncTask.execute()

该方法会调用executeOnExecutor(sDefaultExecutor)

executeOnExecutor方法内部会调用MyAsyncTask的onPreExecute()方法和sDefaultExecutor.execute(mFuture)方法

<1> sDefaultExecutor是实现了Executor的SerialExecutor一个线程池<2> 该线程池内部定义了ArrayDeque<Runnable> mTasks的队列的变量、Runnable mActive变量以及execute(Runnable r)方法(Runnable r参数就是mFuture)<3> execute方法内部首先会通过mTasks.offer添加Runnable对象。之后判断mActive是否为空,首次进来肯定为空,会执行scheduleNext()方法<4> scheduleNext()方法会从队列mTasks中取出之前添加的Runnable对象(使用mTasks.poll())赋值给mActive,并判断如果不为空交给THREAD_POOL_EXECUTOR去执行(THREAD_POOL_EXECUTOR是实现ThreadPoolExecutor的一个线程池),可见AsyncTask内部真正执行任务的是THREAD_POOL_EXECUTOR线程池<5> 添加的Runnable对象,内部会调用参数r的run方法(即mFuture的run方法)。run()内部会回调mWorker.call()方法,执行完毕后会再次去调用scheduleNext()方法执行下一个方法。从这里看出默认是串行执行的

3、更新进度条

在doInBackground中调用publishProgress(int progress)方法,在该方法内部会使用mHandler将progress发送到主线程,会调用MyAsyncTask的onProgressUpdate方法

4、取消

使用MyAsyncTask的cancel(true)取消任务的时候,内部会设置AtomicBoolean mCancelled的set方法设置true,设置取消的标志。如果doInBackground中正在循环的执行耗时任务,任务并不会停止,直至执行结束。如果想在doInBackground中停止正在执行的循环操作,那么就需要在doInBackground的循环中使用:

if(isCancelled()){break;
}来跳出循环

六、AsyncTask注意事项

1、内存泄漏

AsyncTask内存泄漏问题和handler是一样的。如果在activity中使用非静态内部类或匿名内部类来创建AsyncTask的话,由于Java非静态内部类会会持有外部类activity的隐式引用。如果AsyncTask的生命周期比Activity的长,当Activity进行销毁AsyncTask还在执行时,由于AsyncTask持有Activity的引用,导致Activity对象无法回收,进而产生内存泄露。

2、生命周期
它的生命周期会执行耗时操作,所以它不会随着activity的销毁而销毁, 需要手动取消

3、结果丢失

在屏幕旋转等造成Activity重新创建时AsyncTask数据丢失。当Activity销毁并创新创建后,还在运行的AsyncTask会持有一个Activity的非法引用即之前的Activity实例。导致onPostExecute()没有任何作用。

4、并行与串行

Android1.6之前:

任务是串行调度。一个任务执行完成另一个才能执行

Android1.6到2.3

Android团队让AsyncTask并行

Android3.0到现在

Android团队又把AsyncTask改成了串行。当然这一次的修改并没有完全禁止AsyncTask并行。可以通过设置executeOnExecutor(Executor)来实现多个AsyncTask并行。

七、AsyncTask的缺点

在3.0以前,最大支持128个线程的并发,10个任务的等待。在3.0以后,无论有多少任务,都会在其内部单线程执行。为了使AsyncTask执行并行任务并可以一次执行更多个任务,可以使用executeOnExecutor(Executor)自定义线程池来实现

public static ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
public static ExecutorService cachedThreadPool = Executors.newCachedThreadPool();
public static ExecutorService fixedThreadPool = Executors.newFixedThreadPool(3);
public static Executor exec = new ThreadPoolExecutor(15, 200, 10,TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());//顺序执行任务,与AsyncTask默认是串行执行任务一致
new MyAsyncTask((i + 1)).executeOnExecutor(singleThreadExecutor);
//无限制启动线程执行任务
new MyAsyncTask((i + 1)).executeOnExecutor(cachedThreadPool);
//指定线程的数量,每次执行固定数量
new MyAsyncTask((i + 1)).executeOnExecutor(fixedThreadPool);
//AsyncTask中定义的线程池ThreadPoolExecutor。指定每次执行的线程数量以及缓存池中的大小默认128,当线程数量超过线程数量和缓冲池的大小时会抛出异常
new MyAsyncTask((i + 1)).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
//自定义ThreadPoolExecutor
new MyAsyncTask((i + 1)).executeOnExecutor(exec);

八、使用AsyncTask注意的事项

注意的地方:

1、由于Handler需要和主线程交互,而Handler又是内置于AsnycTask中的,所以, AsyncTask的创建必须在主线程。2、AsyncTask对象的execute方法必须在主线程中调用3、AsyncTaskResult的doInBackground(mParams)方法执行异步任务运行在子线 程中,其他方法运行在主线程中,可以操作UI组件。4、不要手动的去调用AsyncTask的onPreExecute, doInBackground, publishPro- gress, onProgressUpdate, onPostExecute方法,这些都是由Android系统自 动调用的5、一个AsyncTask对象只能调用一次execute方法6、运行中可以随时调用cancel(boolean)方法取消任务,如果成功调用isCancelled() 会 返 回 true,并 且 不 会 执 行 onPostExecute() 方 法 了,取 而 代 之 的 是 调 用 onCancelled() 方法。而且从源码看,如果这个任务已经执行了这个时候调用 cancel是不会真正的把task结束,而是继续执行,只不过改变的是执行之后的回 调方法是onPostExecute还是onCancelled。

九、AsyncTask和Handler对比

AsyncTask

使用的优点:

简单、快捷、过程可控

使用的缺点:

在使用多个异步操作和并需要进行Ui变更时,就变得复杂起来.

Handler

使用的优点:

结构清晰,功能定义明确。对于多个后台任务时,简单,清晰

使用的缺点:在单个后台异步处理时,显得代码过多,结构过于复杂(相对性)

这篇关于Android开发线程间的交互之异步任务(AsyncTask)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1115355

相关文章

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

嵌入式QT开发:构建高效智能的嵌入式系统

摘要: 本文深入探讨了嵌入式 QT 相关的各个方面。从 QT 框架的基础架构和核心概念出发,详细阐述了其在嵌入式环境中的优势与特点。文中分析了嵌入式 QT 的开发环境搭建过程,包括交叉编译工具链的配置等关键步骤。进一步探讨了嵌入式 QT 的界面设计与开发,涵盖了从基本控件的使用到复杂界面布局的构建。同时也深入研究了信号与槽机制在嵌入式系统中的应用,以及嵌入式 QT 与硬件设备的交互,包括输入输出设

OpenHarmony鸿蒙开发( Beta5.0)无感配网详解

1、简介 无感配网是指在设备联网过程中无需输入热点相关账号信息,即可快速实现设备配网,是一种兼顾高效性、可靠性和安全性的配网方式。 2、配网原理 2.1 通信原理 手机和智能设备之间的信息传递,利用特有的NAN协议实现。利用手机和智能设备之间的WiFi 感知订阅、发布能力,实现了数字管家应用和设备之间的发现。在完成设备间的认证和响应后,即可发送相关配网数据。同时还支持与常规Sof

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影

Linux_kernel驱动开发11

一、改回nfs方式挂载根文件系统         在产品将要上线之前,需要制作不同类型格式的根文件系统         在产品研发阶段,我们还是需要使用nfs的方式挂载根文件系统         优点:可以直接在上位机中修改文件系统内容,延长EMMC的寿命         【1】重启上位机nfs服务         sudo service nfs-kernel-server resta

【区块链 + 人才服务】区块链集成开发平台 | FISCO BCOS应用案例

随着区块链技术的快速发展,越来越多的企业开始将其应用于实际业务中。然而,区块链技术的专业性使得其集成开发成为一项挑战。针对此,广东中创智慧科技有限公司基于国产开源联盟链 FISCO BCOS 推出了区块链集成开发平台。该平台基于区块链技术,提供一套全面的区块链开发工具和开发环境,支持开发者快速开发和部署区块链应用。此外,该平台还可以提供一套全面的区块链开发教程和文档,帮助开发者快速上手区块链开发。

Vue3项目开发——新闻发布管理系统(六)

文章目录 八、首页设计开发1、页面设计2、登录访问拦截实现3、用户基本信息显示①封装用户基本信息获取接口②用户基本信息存储③用户基本信息调用④用户基本信息动态渲染 4、退出功能实现①注册点击事件②添加退出功能③数据清理 5、代码下载 八、首页设计开发 登录成功后,系统就进入了首页。接下来,也就进行首页的开发了。 1、页面设计 系统页面主要分为三部分,左侧为系统的菜单栏,右侧