浅谈android中异步加载之取消异步加载二

2024-08-27 14:48

本文主要是介绍浅谈android中异步加载之取消异步加载二,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先,我得解释一下为什么我的标题取消异步加载打引号,这是因为可能最后实现效果并不是你自己想象中的那样。大家看取消异步加载,这不是很简单吗?AsyncTask中不是有一个cancel方法吗?直接调用该方法不就行了吗?但是事实上是这样的吗?如果真是这样,我相信我就没有以写这个作为一篇博客的必要了。为什么会有这样的想法呢?实际上源于我上一篇中Demo中的一个BUG,然后解决该BUG,需要去取消异步任务,是怎么样,我们不妨来看看。

 首先,还是来一起回顾一下上篇博客中加载进度条Demo吧。

AsyncTask子类:

01.package com.mikyou.utils;  
02.  
03.import android.os.AsyncTask;  
04.import android.widget.ProgressBar;  
05.import android.widget.TextView;  
06.  
07.public class MikyouAsyncTaskProgressBarUtils extends AsyncTask<Void, Integer, String>{  
08.    private TextView tv;  
09.    private ProgressBar bar;  
10.    public MikyouAsyncTaskProgressBarUtils(TextView tv,ProgressBar bar){//这里我就采用构造器将TextView,ProgressBar直接传入,然后在该类中直接更新UI  
11.        this.bar=bar;  
12.        this.tv=tv;  
13.  
14.    }  
15.    @Override  
16.    protected String doInBackground(Void... params) {  
17.        for(int i=1;i<101;i++){  
18.            try {  
19.                Thread.sleep(1000);  
20.            } catch (InterruptedException e) {  
21.                e.printStackTrace();  
22.            }  
23.            publishProgress(i);  
24.        }  
25.        return "下载完成";  
26.    }  
27.    @Override  
28.    protected void onProgressUpdate(Integer... values) {  
29.        bar.setProgress(values[0]);  
30.        tv.setText("下载进度:"+values[0]+"%");  
31.        super.onProgressUpdate(values);  
32.    }  
33.    @Override  
34.    protected void onPostExecute(String result) {  
35.        tv.setText(result);  
36.        super.onPostExecute(result);  
37.    }  
38.}  

MainActivity

01.package com.mikyou.asynctask;  
02.  
03.import com.mikyou.utils.MikyouAsyncTaskProgressBarUtils;  
04.  
05.import android.app.Activity;  
06.import android.os.Bundle;  
07.import android.view.View;  
08.import android.widget.Button;  
09.import android.widget.ProgressBar;  
10.import android.widget.TextView;  
11.  
12.public class MainActivity extends Activity {  
13.    private Button downLoad;  
14.    private ProgressBar bar;  
15.    private TextView tv;  
16.    @Override  
17.    protected void onCreate(Bundle savedInstanceState) {  
18.        super.onCreate(savedInstanceState);  
19.        setContentView(R.layout.activity_main);  
20.        initView();  
21.    }  
22.    private void initView() {  
23.        bar=(ProgressBar) findViewById(R.id.bar);  
24.        tv=(TextView) findViewById(R.id.tv);  
25.    }  
26.    public void download(View view){  
27.        MikyouAsyncTaskProgressBarUtils mikyouAsyncTaskProgressBarUtils=new MikyouAsyncTaskProgressBarUtils(tv, bar);  
28.        mikyouAsyncTaskProgressBarUtils.execute();  
29.    }  
30.  
31.}  

运行结果的Demo的BUG:



通过分析上面的demo会发现:

当我们点击开始下载后,进度条就开始更新了,然后就在更新中途退出Activity,然后再次进入Activity,点击开始下载会发现,等了好一会,进度条才开始更新。

原因:这并不是本程序的一个BUG,而是异步加载的一个机制,因为异步加载从源码中我们可以得出,它底层的实现还是Thread或者Thread-pool+Handler机制

那么它的机制就是如果当前开始的线程没有执行完毕,其他的线程必须等待,这也就解释了为什么说异步加载是安全,因为可以基于该机制避免了线程同步带来的安全问题。
解决办法:其实很简单的就是将异步加载的生命周期和我们的Activity的生命周期进行绑定,当我第一次在中途中断的时候,退出Activity时会调用OnPause
方法,只需要在OnPause方法中顺便把本次中断的异步任务取消即可,也即把当前的线程池中的线程给取消了,当下次重新进入的时候,线程池中并没有
其他的子线程,那么它就无需等待其他的线程了,直接运行。

通过以上的分析,我们就可以得到了一个解决办法,那就是如何去取消当前的异步任务,那就我们就按照大家的一致想法使用cancel方法来终止异步任务。

来了看看是否可以达到我们想要的效果呢??一起来看看

package com.mikyou.asynctask;import com.mikyou.utils.MikyouAsyncTaskProgressBarUtils;import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;public class MainActivity extends Activity {private Button downLoad;private ProgressBar bar;private TextView tv;private MikyouAsyncTaskProgressBarUtils mTask;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();}private void initView() {bar=(ProgressBar) findViewById(R.id.bar);tv=(TextView) findViewById(R.id.tv);}public void download(View view){mTask=new MikyouAsyncTaskProgressBarUtils(tv, bar);mTask.execute();}//将异步任务的生命周期与Activity进行绑定
@Override
protected void onPause() {//判断当前的异步任务是否为空,并且判断当前的异步任务的状态是否是运行状态{RUNNING(运行),PENDING(准备),FINISHED(完成)}
if (mTask!=null&&mTask.getStatus()==AsyncTask.Status.RUNNING) {/***cancel(true) 取消当前的异步任务,传入的true,表示当中断异步任务时继续已经运行的线程的操作,*但是为了线程的安全一般为让它继续设为true* */mTask.cancel(true);}super.onPause();
}
}
运行结果:

大家仔细看下,发现好像我们加的cancel方法并没有什么用,这也就我这篇需要讲的了,

为什么无法去停止一个异步任务,这是为什么呢?

注意:这是因为cancel方法只是发出一个请求取消异步任务的信号, 将对应当前的异步任务标记为CANCEL状态,而并不是真正取消线程的执行,而此时异步任务中的线程仍然在执行并没有结束,所以效果依然是这样的,并且在java中我们是无法直接暴力将一个线程给停止掉 既然我们知道无法去取消一个已经正在运行的线程,但是我们如何去解决这个BUG呢?在异步任务中还给我们提供一个isCanceled的回调方法,也就是当我已经给当前的异步任,调用了cancel(true)方法,发出一个请求取消异步任务的信号,那么此时的isCanceled的回调方法会直接返回一个true,那么我们就可以通过判断当前异步任务isCanceled是否为true,来终止线程中的操作而不是去终止线程,从而达到了界面显示好像线程中的操作被终止了,而实际上该线程依然在运行

因为我们是无法去彻彻底底地采用暴力的方法直接kill一个线程,所以我们不能直接去取消一个异步任务,但是我们可以通过调用cancel方法来发送一个取消异步任务的请求信号,这时候就会给mCanceled的值设置为true,标记为该异步任务是取消了,通过回调方法isCanceled来返回一个true,表示该异步任务取消。如果有疑问我们来看下cancel反方法的源码就知道了。

    /*** <p>Attempts to cancel execution of this task.  This attempt will* fail if the task has already completed, already been cancelled,* or could not be cancelled for some other reason. If successful,* and this task has not started when <tt>cancel</tt> is called,* this task should never run. If the task has already started,* then the <tt>mayInterruptIfRunning</tt> parameter determines* whether the thread executing this task should be interrupted in* an attempt to stop the task.</p>* * <p>Calling this method will result in {@link #onCancelled(Object)} being* invoked on the UI thread after {@link #doInBackground(Object[])}* returns. Calling this method guarantees that {@link #onPostExecute(Object)}* is never invoked. After invoking this method, you should check the* value returned by {@link #isCancelled()} periodically from* {@link #doInBackground(Object[])} to finish the task as early as* possible.</p>** @param mayInterruptIfRunning <tt>true</tt> if the thread executing this*        task should be interrupted; otherwise, in-progress tasks are allowed*        to complete.** @return <tt>false</tt> if the task could not be cancelled,*         typically because it has already completed normally;*         <tt>true</tt> otherwise** @see #isCancelled()* @see #onCancelled(Object)*/public final boolean cancel(boolean mayInterruptIfRunning) {mCancelled.set(true);return mFuture.cancel(mayInterruptIfRunning);}

会看mCanceled会设置为true,该值将会通过isCanceled方法回调传出去。所以,我们采用这样一个想法,既然我们不能去终止一个线程,那么我们可以间接解决这个bug

通过判断该值直接终止线程中的操作,而不是去终止线程。

package com.mikyou.asynctask;import com.mikyou.utils.MikyouAsyncTaskProgressBarUtils;import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ProgressBar;
import android.widget.TextView;public class MainActivity extends Activity {private Button downLoad;private ProgressBar bar;private TextView tv;private MikyouAsyncTaskProgressBarUtils mTask;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initView();}private void initView() {bar=(ProgressBar) findViewById(R.id.bar);tv=(TextView) findViewById(R.id.tv);}public void download(View view){mTask=new MikyouAsyncTaskProgressBarUtils(tv, bar);mTask.execute();}//将异步任务的生命周期与Activity进行绑定
@Override
protected void onPause() {//判断当前的异步任务是否为空,并且判断当前的异步任务的状态是否是运行状态{RUNNING(运行),PENDING(准备),FINISHED(完成)}
if (mTask!=null&&mTask.getStatus()==AsyncTask.Status.RUNNING) {/***cancel(true) 取消当前的异步任务,传入的true,表示当中断异步任务时继续已经运行的线程的操作,*但是为了线程的安全一般为让它继续设为true* */mTask.cancel(true);/*** 但是重新运行后会发现还是不能起到效果,* 注意:这是因为cancel方法只是发出一个请求取消异步任务的信号,* 将对应当前的异步任务标记为CANCEL状态,而并不是真正取消线程的执行,* 而此时异步任务中的线程仍然在执行并没有结束* 所以效果依然是这样的,并且在java中我们是无法直接暴力将一个线程给停止掉* 既然我们知道无法去取消一个已经正在运行的线程,但是我们如何去解决这个BUG呢?* 在异步任务中还给我们提供一个isCanceled的回调方法,也就是当我已经给当前的异步任务* 调用了cancel(true)方法,发出一个请求取消异步任务的信号,那么此时的isCanceled的回调方法* 会直接返回一个true,那么我们就可以通过判断当前异步任务isCanceled是否为true,来终止* 线程中的操作而不是去终止线程,从而达到了界面显示好像线程中的操作被终止了,而实际上* 该线程依然在运行* */
}super.onPause();
}
}

AsyncTask的子类

package com.mikyou.utils;import android.os.AsyncTask;
import android.widget.ProgressBar;
import android.widget.TextView;public class MikyouAsyncTaskProgressBarUtils extends AsyncTask<Void, Integer, String>{private TextView tv;private ProgressBar bar;public MikyouAsyncTaskProgressBarUtils(TextView tv,ProgressBar bar){//这里我就采用构造器将TextView,ProgressBar直接传入,然后在该类中直接更新UIthis.bar=bar;this.tv=tv;}@Overrideprotected String doInBackground(Void... params) {for(int i=1;i<101;i++){try {if (isCancelled()) {//判断如果为true那么说明已经有请求取消当前任务的信号了,既然无法终止线程的运行,但是可以终止运行在线程中一系列操作break;}Thread.sleep(100);} catch (InterruptedException e) {e.printStackTrace();}publishProgress(i);}return "下载完成";}@Overrideprotected void onProgressUpdate(Integer... values) {if (isCancelled()) {//判断如果为true那么说明已经有请求取消当前任务的信号了,既然无法终止线程的运行,但是可以终止运行在线程中一系列操作,使它空运行,无法达到更新UI的效果return ;}bar.setProgress(values[0]);tv.setText("下载进度:"+values[0]+"%");super.onProgressUpdate(values);}@Overrideprotected void onPostExecute(String result) {tv.setText(result);super.onPostExecute(result);}
}

运行结果:


这篇关于浅谈android中异步加载之取消异步加载二的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


原文地址:
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.chinasem.cn/article/1111975

相关文章

Android学习总结之Java和kotlin区别超详细分析

《Android学习总结之Java和kotlin区别超详细分析》Java和Kotlin都是用于Android开发的编程语言,它们各自具有独特的特点和优势,:本文主要介绍Android学习总结之Ja... 目录一、空安全机制真题 1:Kotlin 如何解决 Java 的 NullPointerExceptio

使用Python获取JS加载的数据的多种实现方法

《使用Python获取JS加载的数据的多种实现方法》在当今的互联网时代,网页数据的动态加载已经成为一种常见的技术手段,许多现代网站通过JavaScript(JS)动态加载内容,这使得传统的静态网页爬取... 目录引言一、动态 网页与js加载数据的原理二、python爬取JS加载数据的方法(一)分析网络请求1

IDEA下"File is read-only"可能原因分析及"找不到或无法加载主类"的问题

《IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题》:本文主要介绍IDEA下Fileisread-only可能原因分析及找不到或无法加载主类的问题,具有很好的参... 目录1.File is read-only”可能原因2.“找不到或无法加载主类”问题的解决总结1.File

重新对Java的类加载器的学习方式

《重新对Java的类加载器的学习方式》:本文主要介绍重新对Java的类加载器的学习方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍1.1、简介1.2、符号引用和直接引用1、符号引用2、直接引用3、符号转直接的过程2、加载流程3、类加载的分类3.1、显示

在 PyQt 加载 UI 三种常见方法

《在PyQt加载UI三种常见方法》在PyQt中,加载UI文件通常指的是使用QtDesigner设计的.ui文件,并将其转换为Python代码,以便在PyQt应用程序中使用,这篇文章给大家介绍在... 目录方法一:使用 uic 模块动态加载 (不推荐用于大型项目)方法二:将 UI 文件编译为 python 模

Android NDK版本迭代与FFmpeg交叉编译完全指南

《AndroidNDK版本迭代与FFmpeg交叉编译完全指南》在Android开发中,使用NDK进行原生代码开发是一项常见需求,特别是当我们需要集成FFmpeg这样的多媒体处理库时,本文将深入分析A... 目录一、android NDK版本迭代分界线二、FFmpeg交叉编译关键注意事项三、完整编译脚本示例四

Android与iOS设备MAC地址生成原理及Java实现详解

《Android与iOS设备MAC地址生成原理及Java实现详解》在无线网络通信中,MAC(MediaAccessControl)地址是设备的唯一网络标识符,本文主要介绍了Android与iOS设备M... 目录引言1. MAC地址基础1.1 MAC地址的组成1.2 MAC地址的分类2. android与I

浅谈Redis Key 命名规范文档

《浅谈RedisKey命名规范文档》本文介绍了Redis键名命名规范,包括命名格式、具体规范、数据类型扩展命名、时间敏感型键名、规范总结以及实际应用示例,感兴趣的可以了解一下... 目录1. 命名格式格式模板:示例:2. 具体规范2.1 小写命名2.2 使用冒号分隔层级2.3 标识符命名3. 数据类型扩展命

Python 异步编程 asyncio简介及基本用法

《Python异步编程asyncio简介及基本用法》asyncio是Python的一个库,用于编写并发代码,使用协程、任务和Futures来处理I/O密集型和高延迟操作,本文给大家介绍Python... 目录1、asyncio是什么IO密集型任务特征2、怎么用1、基本用法2、关键字 async1、async

Spring框架中@Lazy延迟加载原理和使用详解

《Spring框架中@Lazy延迟加载原理和使用详解》:本文主要介绍Spring框架中@Lazy延迟加载原理和使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、@Lazy延迟加载原理1.延迟加载原理1.1 @Lazy三种配置方法1.2 @Component