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)
上一篇 2022年4月20日 下午9:00
下一篇 2022年4月20日 下午9:00


相关推荐

  • js实现 throttle 和 debounce

    js实现 throttle 和 debounce1 throttle 节流 drag 改变浏览器大小 触发 onresize 函数 实现拖动每过 1 秒输出一次 不足 1 秒 1 秒后输出一次 多用于高频操作 如抢票 抢购等 无论点击多少次 只固定间隔执行一次 以减轻压力 原理 设置 canRun 作为是否执行的标志 每次触发 onresize 都判断 canRun 的值 true 执行 false 不执行 第一次应该执行 所以设置 canRun 初始值为 true 当第

    2026年3月17日
    2
  • Idea激活码永久有效Idea2019.3.2激活码教程-持续更新,一步到位

    Idea激活码永久有效Idea2019.3.2激活码教程-持续更新,一步到位Idea激活码永久有效2019.3.2激活码教程-Windows版永久激活-持续更新,Idea激活码2019.3.2成功激活

    2022年6月17日
    92
  • 卸载Office密钥或删除序列号

    卸载Office密钥或删除序列号以管理员权限运行命令行查询序列号 cscript C ProgramFiles x86 MicrosoftOff Office16 ospp vbs dstatus 记住执行该命令后出现的后五位字母 替换下面的蓝色字母 卸载序列号 cscript C ProgramFiles x86 MicrosoftOff Office16 OSPP VBS

    2026年3月19日
    2
  • scrapy中ROBOTSTXT_OBEY = False,不遵守Robot协议文件的规则

    scrapy中ROBOTSTXT_OBEY = False,不遵守Robot协议文件的规则在scrapy中创建项目以后,在settings文件中有这样的一条默认开启的语句:setting文件中配置:#Obeyrobots.txtrules#默认是True,遵守robots.txt文件中的协议,遵守允许爬取的范围。#设置为False,是不遵守robo协议文件。。。ROBOTSTXT_OBEY=True观察代码可以发现,默认为True,就是要遵守rob…

    2022年6月11日
    44
  • android浏览器开发小技巧集锦

    android浏览器开发小技巧集锦本人和朋友们做了一段时间浏览器,将一些小技巧分享出来,先写一部分,慢慢写,同时也为我们的浏览器打打广告我们的浏览器将要上线,名叫沙发浏览1.网页内的右键菜单publicbooleanonLongClick(Viewview){ //获取点击的元素 HitTestResultmResult=mWebView.getHitTestResult(); fin

    2022年5月15日
    35

发表回复

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

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