• Android基础-AsyncTask详解


    一、Android中的线程

    在操作系统中,线程是操作系统调度的最小单元,同时线程又是一种受限的系统资源,即线程不可能无限制地产生,并且线程的创建和销毁都会有相应的开销。当系统中存在大量的线程时,系统会通过会时间片轮转的方式调度每个线程,因此线程不可能做到绝对的并行。

    如果在一个进程中频繁地创建和销毁线程,显然不是高效的做法。正确的做法是采用线程池,一个线程池中会缓存一定数量的线程,通过线程池就可以避免因为频繁创建和销毁线程所带来的系统开销。

    二、AsyncTask简介

    AsyncTask是一个抽象类,它是由Android封装的一个轻量级异步类(轻量体现在使用方便、代码简洁),它可以在线程池中执行后台任务,然后把执行的进度和最终结果传递给主线程并在主线程中更新UI。

    AsyncTask的内部封装了两个线程池(SerialExecutor和THREAD_POOL_EXECUTOR)和一个Handler(InternalHandler)。

    其中SerialExecutor线程池用于任务的排队,让需要执行的多个耗时任务,按顺序排列,THREAD_POOL_EXECUTOR线程池才真正地执行任务,InternalHandler用于从工作线程切换到主线程。

    1.AsyncTask的泛型参数

    AsyncTask的类声明如下

    public abstract class AstncTask<Params, Progress, Result>

    AsyncTask是一个抽象泛型类。

    其中,三个泛型类型参数的含义如下:

    Params:开始异步任务执行时传入的参数类型

    Progress:异步任务执行过程中,返回下载进度值的类型;

    Result:异步任务执行完成后,返回的结果类型。

    如果AsyncTask确定不需要传递具体参数,那么这三个泛型参数可以用Void来代替。

    有了这三个参数类型之后,也就控制了这个AsyncTask子类各个阶段的返回类型,如果有不同业务,我们就需要再另写一个AsyncTask的子类进行处理。

    2. AsyncTask的核心方法

    onPreExecute()

    这个方法会在后台任务开始执行之间调用,在主线程执行。用于进行一些界面上的初始化操作,比如显示一个进度条对话框等。

    doInBackground(Params...)

    这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务

    任务一旦完成就可以通过return语句来将任务的执行结果进行返回,如果AsyncTask的第三个泛型参数指定的是Void,就可以不返回任务执行结果。

    注意,在这个方法中是不可以进行UI操作的,如果需要更新UI元素,比如说反馈当前任务的执行进度,可以调用publishProgress(Progress...)方法来完成。

    onProgressUpdate(Progress...)

    当在后台任务中调用了publishProgress(Progress...)方法后,这个方法就很快会被调用,方法中携带的参数就是在后台任务中传递过来的。在这个方法中可以对UI进行操作,在主线程中进行,利用参数中的数值就可以对界面元素进行相应的更新。

    onPostExecute(Result)

    当doInBackground(Params...)执行完毕并通过return语句进行返回时,这个方法就很快会被调用。返回的数据会作为参数传递到此方法中,可以利用返回的数据来进行一些UI操作,在主线程中进行,比如说提醒任务执行的结果,以及关闭掉进度条对话框等。

    上面几个方法的调用顺序:

      onPreExecute()-->doInBackground()-->publishProgress()-->onProgressUpdate()-->onPostExecute()

    如果不需要执行更新进度则为:

      onPreExecute()-->onInBackground()-->onPostExecute()

    除了上面四个方法,AsyncTask还提供了onCancelled()方法,它同样在主线程中执行,当异步任务取消时,onCancelled()会被调用,这个时候onPostExecute()则不会被调用,但是要注意的是,AsyncTask中的cancel()方法并不是真正去取消任务,只是设置这个任务为取消状态,我们需要在doInBackground()判断终止任务。就好比想要终止一个线程,调用interrupt()方法,只是进行标记为中断,需要在线程内部进行标记判断然后中断线程。

    3.AsyncTask的简单使用

    class DownloadTask extends AsyncTask<Void, Integer, Boolean>{
    
        @Override
        protected void onPreExecute(){
            progressDialog.show();
        }
    
        @Override
        protected Boolean doInBackground(Void... params){
            try{
                while (true){
                    int downloadPercent = doDownload();
                    publishProgress(downloadPercent);
                    if (downloadPercent >= 100){
                        break;
                    }
                }
            }catch (Exception e){
                return false;
            }
            return  true;
        }
    
        @Override
        protected void onProgressUpdate(Integer... values){
            progressDialog.setMessage("当前下载进度:" + values[0] + "%");
        }
    
        @Override
        protected void onPostExecute(Boolean result){
            progressDialog.dismiss();
            if (result) {  
                Toast.makeText(context, "下载成功", Toast.LENGTH_SHORT).show();  
            } else {  
                Toast.makeText(context, "下载失败", Toast.LENGTH_SHORT).show();  
            }  
        }
    }

    这里我们模拟了一个下载任务,在doInBackground()方法中去执行具体的下载逻辑,在onProgressUpdate()方法中显示当前的下载进度,在onPostExecute()方法中来提示任务的执行结果。如果想要启动这个任务,只需要简单地调用以下代码即可:

    new DownloadTask().execute();

    4.使用AsyncTask的注意事项

    ①异步任务的实例必须在UI线程中创建,即AsyncTask对象必须在UI线程中创建。

    ②execute(Params... params)方法必须在UI线程中调用。

    ③不要手动调用onPreExecute(),doInBackground(Params... params),onProgressUpdate(Progress... values),onPostExecute(Result result)这几个方法。

    ④不能在doInBackground(Params... params)中更改UI组件的信息。

    ⑤一个任务实例只能执行一次,如果执行第二次将会抛出异常。

     

    三、AsyncTask的源码分析

     

     

    四、AsyncTask使用不当的后果

    1.)生命周期

    AsyncTask不与任何组件绑定生命周期,所以在Activity/或者Fragment中创建执行AsyncTask时,最好在Activity/Fragment的onDestory()调用 cancel(boolean);

    2.)内存泄漏

    如果AsyncTask被声明为Activity的非静态的内部类,那么AsyncTask会保留一个对创建了AsyncTask的Activity的引用。如果Activity已经被销毁,AsyncTask的后台线程还在执行,它将继续在内存里保留这个引用,导致Activity无法被回收,引起内存泄露。

    3.) 结果丢失

    屏幕旋转或Activity在后台被系统杀掉等情况会导致Activity的重新创建,之前运行的AsyncTask(非静态的内部类)会持有一个之前Activity的引用,这个引用已经无效,这时调用onPostExecute()再去更新界面将不再生效。

     

     

    转载自:https://lrh1993.gitbooks.io/android_interview_guide/content/android/basis/asynctask.html

    很想高飞,但我不能;不想天空,剩我一人。
  • 相关阅读:
    文艺青年会看这本《迷局》么?
    看文艺青年怎么玩微信客户端
    Sublime Text有哪些使用技巧(转)
    C++ 关键字 explicit, export, mutable
    move语义和右值引用
    C++11 std::function用法
    function adapter(函数适配器)和迭代器适配器
    for_each()的返回值
    C++11的一些新特性
    setw和setfill控制输出间隔
  • 原文地址:https://www.cnblogs.com/lixiansheng/p/11359883.html
Copyright © 2020-2023  润新知