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)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • linux lefse分析,LEfSe分析,你真的懂了么

    linux lefse分析,LEfSe分析,你真的懂了么在微生物多样性分析中,LEfSe分析自从”出生“就倍受青睐,今天小昌宝宝就跟大家唠唠这个高频出镜的LEfSe分析,从LEfSe的定义、结果、原理、涉及到的检验四个方面进行解释。一LEfSe的定义LEfSe分析即LDAEffectSize分析,是一种用于发现和解释高维度数据生物标识(基因、通路和分类单元等)的分析工具,可以进行两个或多个分组的比较,它强调统计意义和生物相关性,能够在组与组之间寻…

    2022年6月13日
    77
  • GIMP 2.10教程「建议收藏」

    GIMP 2.10教程「建议收藏」更新一下(2020-12-27),有大神刚完成人工翻译,质量很好,地址在此:https://www.ycproject.cn/gimp/gimp.html下文可以忽略了GIMP_2.10中文教程(谷歌机翻)GIMP是全平台(桌面)下的Photoshop,专门处理图片的。先放原文地址:https://docs.gimp.org/2.10/zh_CN/(基于2.10.18版)GIMP中文教程太少了,搜了一大圈找到一个靠谱点全一点的,是@笨⼩璀在2014年基于2012年的2.8版翻译的,翻译

    2022年6月17日
    24
  • sendfile:Linux中的”零拷贝”

    sendfile:Linux中的”零拷贝”如今几乎每个人都听说过Linux中所谓的”零拷贝”特性,然而我经常碰到没有充分理解这个问题的人们。因此,我决定写一些文章略微深入的讲述这个问题,希望能将这个有用的特性解释清楚。在本文中,将从用户空间应用程序的角度来阐述这个问题,因此有意忽略了复杂的内核实现。什么是”零拷贝”为了更好的理解问题的解决法,我们首先需要理解问题本身。首先我们以一个网络服务守护进程为例,考虑它在将存储在文件

    2022年6月1日
    29
  • Java知识体系最强总结(2021版)[通俗易懂]

    更新于2019-12-1510:38:00本人从事Java开发已多年,平时有记录问题解决方案和总结知识点的习惯,整理了一些有关Java的知识体系,这不是最终版,会不定期的更新。也算是记录自己在从事编程工作的成长足迹,通过博客可以促进博主与阅读者的共同进步,结交更多志同道合的朋友。特此分享给大家,本人见识有限,写的博客难免有错误或者疏忽的地方,还望各位大佬指点,在此表示感激不尽。文章目录…

    2022年4月6日
    47
  • SPI接口调试

    SPI接口调试以MCP2515芯片的SPI接口为例说明。

    2022年6月18日
    28
  • SSH会话连接超时问题

    SSH会话连接超时问题

    2021年10月7日
    57

发表回复

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

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