asynctask使用_androidstudiohelloworld教程

asynctask使用_androidstudiohelloworld教程Android AsyncTask 详解

大家好,又见面了,我是你们的朋友全栈君。

Android AsyncTask 详解

内容划分

  • AsyncTask简介
  • 简单使用
  • 繁杂部分和源码浅析
  • 一些坑的地方

AsyncTask简介

AsyncTask enables proper and easy use of the UI thread. This class allows you to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.

AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.) If you need to keep threads running for long periods of time, it is highly recommended you use the various APIs provided by the java.util.concurrent package such as Executor, ThreadPoolExecutor and FutureTask.

这是Google Android 开发文档上关于AsyncTask的介绍,大概意思是AsyncTask设计为一个对于Thread和Handle的辅助类,主要让开发者方便的使用UI Thread和后台Thread的操作( 比如在后台线程下载文件,同时要在UI线程更新下载进度 )。同时这不是一个通用的多线程编程框架,他被设计为用于能够在 最多几秒的时间内返回结果的任务。

简单使用

这里我们模拟一个后台下载一些文件,并在用户界面显示一个ProgressDialog来显示下载进度的功能。

/** * author: zyinux * date: on 2018/10/26 */
public class DownloadTask extends AsyncTask<String,Integer,Boolean> {

    ProgressDialog progressDialog;

    Context context;

    public DownloadTask(Context context) {
        this.context=context;

    }

    @Override
    protected void onPreExecute() {
        progressDialog=new ProgressDialog(context);
        progressDialog.setMax(100);
    // progressDialog.setCancelable(false);
    //注意这里我将上一行代码注释掉,使得dialog能够被取消,至于为什么这么做后面解释
        progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        progressDialog.show();
    }

    @Override
    protected Boolean doInBackground(String... strings) {
        int pro=0;

        while (true){
            pro++;
            publishProgress(pro);
            if (pro>=100){
                break;
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        return true;
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        progressDialog.setProgress(values[0]);
    }

    @Override
    protected void onPostExecute(Boolean aBoolean) {
        progressDialog.dismiss();
        if (aBoolean){
            Toast.makeText(context,"下载成功",Toast.LENGTH_SHORT).show();
        }else {
            Toast.makeText(context,"下载失败",Toast.LENGTH_SHORT).show();
        }
    }
}
复制代码

下面是Activity中调用的主要代码

new DownloadTask(this).execute("testurl");
//使用非常简单,new 之后执行execute传入执行的参数即可
复制代码

运行效果如图

很普通的效果,下面来分析下上面的代码 首先是

public class DownloadTask extends AsyncTask<String,Integer,Boolean> 复制代码

这一行泛型尖括号里的三个类型,具体对应三个

  • Params 执行时发送给任务的参数的类型
  • Progress 后台执行过程进度的类型
  • Result 执行结果返回值的类型 当不需要这些参数的时候可以设置为<Void,Void,Void> 接着是实现的几个主要方法
protected void onPreExecute() {}
protected Boolean doInBackground(String... strings) {}
protected void onProgressUpdate(Integer... values) {}
protected void onPostExecute(Boolean aBoolean) { }
复制代码
  • onPreExecute 方法执行在UI线程,会在做后台任务之前调用,可以在这里执行一些初始化操作,例如上面的显示Dialog
  • doInBackground 改方法执行在后台线程,任务中的耗时操作都应该在这里执行,AsyncTask内部维持了一个线程池,来对该方法调用执行优化。该方法的参数类型就是上面设置的 Params ,也就是执行调用代码中execute里传递来的参数。在该方法内部可以调用publishProgress方法来传递当前的进度。
  • onProgressUpdate 在publishProgress方法后,系统会调用该方法,该方法运行在UI Thread,所以可以在这里做UI更新的操作,比如更新ProgressDialog的进度。这里传递的参数的类型就是上文里的 Progress。
  • onPostExecute 在doInBackground方法执行完成后会执行该方法,同样运行在UI Thread。而传入的参数就是doInBackground方法的返回值,该类型由上文的Result指定。

繁杂部分和源码浅析

上面基本讲解了AsyncTask的使用方法了。细心的小伙伴可能注意到我上面的这两句代码

// progressDialog.setCancelable(false);
//注意这里我将上一行代码注释掉,使得dialog能够被取消,至于为什么这么做后面解释
复制代码

现在来解释这里这么写的原因,假设我们运行app,并执行DownloadTask,这时候屏幕上弹出一个进度框,目前为止一切都没有问题。这个时候我们点击屏幕的其他地方,进度框会被取消掉,接着我们再次执行DownloadTask,小伙伴们猜猜现在会发生什么?

由于不太方便录屏和传gif图,我这里就简单说下会发生的事情:进度框会再度弹出,这没什么问题,但是进度条会停留在0%不动,直到一段时间之后弹出Toast显示下载完成,接着进度条开始慢慢增加,当达到百分之百时再次弹出Toast提示下载完成。

###为什么会这样? 首先我们知道,取消dialog并不会取消掉AsyncTask,所以再次执行DownloadTask时,相当于此时有两个AsyncTask任务在执行。这就引出了一个问题,多个AsyncTask执行时是串行还是并行?

串行还是并行?

先说答案,默认是串行的,为什么,我们来看源码。 当执行 new DownloadTask(this).execute(“testurl”); 后:

    @MainThread
    public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }

    接着继续看

    @MainThread
    public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec, Params... params) {
        //看这里我们直到onPreExecute方法一定在doInBackground方法之前调用,并且是在UI Thread
        onPreExecute();
        /** *具体执行方法在这里 我们直到这个exec就是上一步传进来的sDefaultExecutor *这是一个用来管理线程池的框架 */
        exec.execute(mFuture);
        return this;
    }

    初始化的地方,重点这里初始化为static final 是一个类静态变量,是类实例共享的
    public static final Executor SERIAL_EXECUTOR = new SerialExecutor();    

    继续看这个SerialExecutor
    private static class SerialExecutor implements Executor {
        final ArrayDeque<Runnable> mTasks = new ArrayDeque<Runnable>();
        Runnable mActive;

        public synchronized void execute(final Runnable r) {
            mTasks.offer(new Runnable() {
                public void run() {
                    try {
                        r.run();
                    } finally {
                        scheduleNext();
                    }
                }
            });
            if (mActive == null) {
                scheduleNext();
            }
        }

        protected synchronized void scheduleNext() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

    将任务保存在ArrayDeque中,这是一个FIFO的队列,最后执行这个队列中的每一个任务。所以当执行多个AsyncTask时,他们是串行执行的。
复制代码
上面说了这时一般情况,那么特殊情况呢?
复制代码
DownloadTask task=new DownloadTask(this);
task.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
复制代码

这时候多个任务就不是一定排队按顺序执行了,具体执行顺序要看系统对线程的调度了,小伙伴们可以自己测试一下看看。不过一般不推荐这么使用,除非你有特殊需求。

一些坑的地方

关于cancel方法

public final boolean cancel(boolean mayInterruptIfRunning) {}
复制代码

传入的参数表示当前任务执行时是否可以取消。但是当你的doInBackground方法中执行一个循环或者一个IO流读写任务,即使你传入了true,改方法也无法取消这个任务的执行。区别在于调用这个方法后,doInBackground执行完成时会调用onCancelled方法,而不是onPostExecute方法,所以cancel无法保证任务能够被取消

内存泄漏

上面的示列代码从Activity中传入了一个context。而AsyncTask的生命周期和Activity是无关的,那么当Activity被finish后,AsyncTask依然存在,而他持有着Activity的引用导致Activity无法被垃圾回收。 同时如果使用了非静态匿名内部类来实现AsyncTask,由于Java内部类的特点,他同样会持有当前Activity的引用造成内存泄漏。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/107231.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • python怎么实现检验_python实现KMO检验和Bartlett’s球形检验

    python怎么实现检验_python实现KMO检验和Bartlett’s球形检验1.KMOKMO(Kaiser-Meyer-Olkin)检验统计量是用于比较变量间简单相关系数和偏相关系数的指标。主要应用于多元统计的因子分析。KMO统计量是取值在0和1之间。使用说明:Kaiser给出了常用的kmo度量标准: 0.9以上表示非常适合;0.8表示适合;0.7表示一般;0.6表示不太适合;0.5以下表示极不适合。KMO统计量是取值在0和1之间。当所有变量间的简单相关系数平方和远远大于…

    2022年6月17日
    43
  • Python求逆矩阵_3x3下三角矩阵求逆矩阵

    Python求逆矩阵_3x3下三角矩阵求逆矩阵1:导入包numpy  fromnumpyimport*2:定义初始化矩阵  a1=mat([[3,4],[2,16]])  //这是一个2×2的矩阵3:求a1的逆矩阵  a2=a1.I  

    2022年8月21日
    5
  • maven详细教程_maven的安装与配置

    maven详细教程_maven的安装与配置学习maven的使用,看到一篇很实用的入门教程(菜鸟级入门)2007-08-2814:01:04标签:maven职场休闲一、前言早就知道maven在java项目的管理方面名声显赫,于是就想着学习掌握之,于是查阅了大量文档。发现这些文档的作者都是java的大腕,大多都是站在掌握了一定maven基础的角度上进行介绍,让我这初学者看的云里雾里不…

    2025年10月7日
    3
  • 分布式存储Tair

    分布式存储TairTairtair 是淘宝在 2010 年开源的分布式 k v 缓存 存储系统 主要支持以下四种引擎 mdb 类似 memcahe rdb 采用 redis 中的存储结构 支持 k v list hash set 等 ldb leveldb kdb KyotoCabinet 可以让我们更方便的使用这些 kv 数据库 其中 mdb rdb 是分布式缓存 ldb kdb 是分布式存储 tair 支持 version 每个数据中包含一个 version 信息 可以保证数据都是基于最新的数据进行更新 比如有数据

    2025年10月27日
    4
  • 十二、适配器模式——解决充电的烦恼 #和设计模式一起旅行#

    轻轻的我走了, 正如我轻轻的来; 我轻轻的招手, 作别西天的云彩。 ——徐志摩 《再别康桥》故事背景把奶茶店盘了出去,我和设计模式MM,继续上路,坐着冒着烟的飞机来到了剑桥,这里真是个美丽的地方,我用手机拍了很多的照片手机没电了,也玩的累了。找了个酒店 ,准备休息一下,然后给我的手机充充电。 才知道英国的插座都是下面这个样子:而我的…

    2022年2月27日
    44
  • TortoiseSVN菜单项功能说明

    TortoiseSVN菜单项功能说明

    2021年9月19日
    50

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号