AsyncTask使用与源码解析

Posted 单灿灿

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了AsyncTask使用与源码解析相关的知识,希望对你有一定的参考价值。

安卓更新UI的方式大致有四种:

  • 使用Handler消息传递机制;
  • 使用AsyncTask异步任务;
  • 使用runOnUiThread(action)方法;
  • 使用Handler的post(Runnabel r)方法;

其中AsyncTask和Handler是两种异步方式。我有一篇文章专门讲解Android异步消息处理机制 Handler、Looper、Message这篇文章,从源码讲解,很适合看。

今天我们着重通过示例来讲解AsyncTask。

AsyncTask是什么?作用是什么?

AsyncTask可以理解为异步任务执行者,主要用来执行一个较为耗时的异步任务,然后更新UI。

AsyncTask的调用步骤

AsyncTask是抽象类,定义了三种泛型类型 Params,Progress,Result。

  • 1) Params 启动任务执行的输入参数,比如HTTP请求的URL。调用execute()方法时传入的参数类型和doInBackgound()的参数类型

  • 2) Progress 后台任务执行的百分比。更新进度时传递的参数类型,即publishProgress()和onProgressUpdate()的参数类型

  • 3) Result 后台执行任务最终返回的结果,比如String。即doInBackground()的返回值类型。

使用步骤:

  • 首先实例化AsyncTask

  • 接着实现AsyncTask中定义的下面一个或几个方法

onPreExecute(),该方法将在执行实际的后台操作前被UI thread调用。可以在该方法中做一些准备工作,如在界面上显示一个进度条。

doInBackground(Params…),将在onPreExecute 方法执行后马上执行,该方法运行在后台线程中。这里将主要负责执行那些很耗时的后台计算工作。可以调用 publishProgress方法来更新实时的任务进度。该方法是抽象方法,子类必须实现。

onProgressUpdate(Progress…),在publishProgress方法被调用后,UI thread将调用这个方法从而在界面上展示任务的进展情况,例如通过一个进度条进行展示。

onPostExecute(Result),在doInBackground 执行完成后,onPostExecute 方法将被UI thread调用,后台的计算结果将通过该方法传递到UI thread.

下面通过代码示例来讲解。

Activity代码(里面包含全部内容,文末也有github地址):

public class AsyncTaskActivity extends AppCompatActivity 
    private List<TextView> mTextViewList;

    @Override
    protected void onCreate(Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_async_task);
        mTextViewList = new ArrayList<>();
        mTextViewList.add((TextView) findViewById(R.id.asyntask_one));
        mTextViewList.add((TextView) findViewById(R.id.asyntask_two));
        mTextViewList.add((TextView) findViewById(R.id.asyntask_three));
        mTextViewList.add((TextView) findViewById(R.id.asyntask_four));
        mTextViewList.add((TextView) findViewById(R.id.asyntask_five));
        mTextViewList.add((TextView) findViewById(R.id.asyntask_six));
        new MyAsyncTask(mTextViewList.get(0), 0).execute();
        new MyAsyncTask(mTextViewList.get(1), 1).execute();
        new MyAsyncTask(mTextViewList.get(2), 2).execute();
        new MyAsyncTask(mTextViewList.get(3), 3).execute();
        new MyAsyncTask(mTextViewList.get(4), 4).execute();
        new MyAsyncTask(mTextViewList.get(5), 5).execute();


    


    private class MyAsyncTask extends AsyncTask<Void, Integer, String> //here to shengshi use void
        private TextView mTextView;
        private int id;

        /**
         * 生成该类的对象,并调用execute方法之后
         * 首先执行的是onProExecute方法
         * 其次执行doInBackgroup方法
         */


        public MyAsyncTask(TextView textView, int id) 
            super();
            mTextView = textView;
            this.id = id;
        

        //运行在UI线程当中,并且运行在UI线程,可以对UI进行设置
        @Override
        protected void onPreExecute() 
            mTextView.setText("task " + id + "  即将开始执行异步线程");
        

        @Override
        protected String doInBackground(Void... params) 

            NetOperator netOperator = new NetOperator();
            int i = 0;
            for (i = 0; i <= 2000; i += 100) 
                netOperator.operator();
                publishProgress(i / 200);//这是更新的进度
            

            return id + " 的图片下载完成";//返回的是最后的String结果
        


        //在doInBackground方法当中,,每次调用publishProgress方法都会触发onProgressUpdate执行
        @Override
        protected void onProgressUpdate(Integer... values) 
            mTextView.setText("task " + id + "   异步操作执行到  " + values[0] + " 进度");
        

        //运行在UI线程当中,并且运行在UI线程,可以对UI进行设置

        @Override
        protected void onPostExecute(String s) 
            mTextView.setText("task " + id + "  异步操作执行结束 " + s);
        
    


    //模拟加载环境,做一个耗时操作
    public class NetOperator 

        public void operator() 
            try 
                //休眠0.01秒
                Thread.sleep(10);
             catch (InterruptedException e) 
                // TODO Auto-generated catch block
                e.printStackTrace();
            
        

    

通过观察,我们看出了通过execute来启动AsyncTask的话,他是串行执行的。

下面改变一下执行方法executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)

从上面我们可以看出,AsyncTask即可以串行执行,又可以并行执行,关键是看你怎么启动它

进入源码看一下他的执行

  @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) 
        if (mStatus != Status.PENDING) 
            switch (mStatus) 
                case RUNNING:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task is already running.");
                case FINISHED:
                    throw new IllegalStateException("Cannot execute task:"
                            + " the task has already been executed "
                            + "(a task can be executed only once)");
            
        

        mStatus = Status.RUNNING;

        onPreExecute();

        mWorker.mParams = params;
        exec.execute(mFuture);

        return this;
    

可以看出来使用execute方法最终还是执行executeOnExecutor方法,只不过是里面传的参数不一样。

我们来分析一下sDefaultExecutor,THREAD_POOL_EXECUTOR这两个参数,为何导致执行结果不同。

THREAD_POOL_EXECUTOR:

    /**
     * An @link Executor that can be used to execute tasks in parallel.
     * 可以用来并行执行任务的
     */
    public static final Executor THREAD_POOL_EXECUTOR;

    static 
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
                CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS,
                sPoolWorkQueue, sThreadFactory);
        threadPoolExecutor.allowCoreThreadTimeOut(true);
        THREAD_POOL_EXECUTOR = threadPoolExecutor;//THREAD_POOL_EXECUTOR就是一个线程池
    

看一下其中相关的参数:

   private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));//CORE_POOL_SIZE是核心线程数量,从2和(CPU核心-1与4中的最小值)取最大值
    private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;//最大线程数量
    private static final int KEEP_ALIVE_SECONDS = 30;//保活时间为30s

    private static final ThreadFactory sThreadFactory = new ThreadFactory() 
        //线程工厂,生产线程
        private final AtomicInteger mCount = new AtomicInteger(1);

        public Thread newThread(Runnable r) 
            return new Thread(r, "AsyncTask #" + mCount.getAndIncrement());
        
    ;

    private static final BlockingQueue<Runnable> sPoolWorkQueue =
            new LinkedBlockingQueue<Runnable>(128);//线程池的任务队列中可以同时有128个任务等待执行

我们现回头看executeOnExecutor方法,传入的是THREAD_POOL_EXECUTOR线程池,exec.execute方法中调用的就是THREAD_POOL_EXECUTOR的execute方法,任务就会在这个线程池中运行,调用这个方法,至少会有两个任务一起执行。

接着我们来看sDefaultExecutor

每天都在加班,太累了,明天接着分析,不好意思。

以上是关于AsyncTask使用与源码解析的主要内容,如果未能解决你的问题,请参考以下文章

Android -- AsyncTask源码解析

AsyncTask使用与源码解析

AsyncTask使用与源码解析

android源码解析之-->异步任务AsyncTask

AsyncTask源码解析

AsyncTask源码解析