【Android笔记】AsyncTask基本使用以及源码解析

前言

Android中,线程是操作系统调度的最小单位。线程分为主线程和子线程。主线程用来处理界面的交互,而耗时操作(网络请求,复杂的数据库查询)必须在子线程中来完成。通过Handler消息机制完成主线程和子线程之间的通信。

每个任务都需要一个线程去执行,但是不可能每个任务的执行都是伴随着线程的销毁和重新创建,十分的耗费性能。所以用线程池去缓存一定数目的线程,由线程池来管理执行任务的线程,避免了频繁的创建和销毁。Android中提供了AsyncTask类,内部就是由线程池和Handler实现的。各位大佬,这篇看了包会!

这里写图片描述

(一) AsyncTask使用

public abstract class AsyncTask<Params, Progress, Result>

AsyncTask是一个抽象类,如果使用的话需要自定义一个类继承它。它提供了4个核心的方法。这里用一个示例来做介绍。


一、使用核心方法介绍

1.1 自定义类继承AsyncTask

//静态属性声明处,此处MyAsyncTask为静态类
 companion object {

         class MyAsyncTask(private var taskName: String) : AsyncTask<String, Int, String>() {
			//核心方法1
             override fun onPreExecute() {
                super.onPreExecute()
                 Log.d("MyAsyncTask", taskName + " OnPreExecute")
            }
			//核心方法2
            override fun doInBackground(vararg params: String?): String {

                try {
                    Thread.sleep(5000)
                } catch (e: Exception) {
                    e.printStackTrace()
                }
				//辅助方法
                publishProgress(50)
                
                Log.d("MyAsyncTask", taskName + " doInBackground")
                
                return taskName
            }
			//核心方法3
            override fun onPostExecute(result: String?) {
                super.onPostExecute(result)
                Log.d("MyAsyncTask",
                        "I am Task" + result + " executed at  " + SimpleDateFormat("yyyy-mm-dd HH:mm:ss").format(Date()))

            }
			//核心方法4
            override fun onProgressUpdate(vararg values: Int?) {
                super.onProgressUpdate(*values)

                Log.d("MyAsyncTask", taskName + " onProgressUpdate")
            }
        }
    }
    

声明好了静态内部类MyAsyncTask后,执行异步任务。


execute!!.setOnClickListener({
    MyAsyncTask("AsyncTask1").execute()
})
 

介绍一下上面点击按钮执行任务操作,在项目中是没有写findViewById去加载Button,通过Kotlin Android Extensions拓展插件直接可以使用xml布局里定义的控件,有兴趣的同学可以去Kotlin官网了解一波。

下面是MyAsyncTask执行的结果。

这里写图片描述


根据图示介绍一下AsyncTask的核心方法

1-2、onPreExecute

在主线中执行,用于异步任务执行之前做一些准备工作,比如初始化加载进度条之类。


1-3、doInBackground(vararg params: String?)

在子线程中执行,真正执行异步任务的方法,参数是异步任务输入参数。在此方法中可以通过publishProgress方法来更新异步任务当前的进度,调用publishProgress方法会同步调用onProgressUpdate


1-4. onProgressUpdate(vararg values: Int?)

在主线程中执行,此方法用来实时显示当前任务的执行进度。作用是可以在此处更新UI上的进度条进度或者做其他操作。


1-5. onPostExecute(result: String?)

在主线程中执行,异步任务执行结束之后会执行此方法。参数是doInBackground的返回值。

AsyncTask的核心方法就是上面所述,执行任务是在子线程中,而任务准备工作,进度更新等全是在主线程中完成。AsyncTask实例的创建也是要在主线程中去执行。


二、源码解析

根据代码执行顺序,我们首先看下AsyncTaskexecute方法。

//主线程中执行
@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后台执行任务所需参数
       mWorker.mParams = params;
       //线程池执行线程任务,mFuture封装了开启线程的mWorker
       exec.execute(mFuture);

       return this;
   }
    

我们看下上面executeOnExecutor方法中几个重要的属性。exec →SerialExecutormWorker → WorkerRunnablemFuture → FutureTask

WorkerRunnable:任务执行返回的结果

这里mWorker对象的创建是在AsyncTask的构造函数中,本质是开启一个工作线程,call方法将任务执行完的结果返回。


mWorker = new WorkerRunnable<Params, Result>() {
     public Result call() throws Exception {
           //标记任务已经被调用过
           mTaskInvoked.set(true);
           Result result = null;
           try {
               //将线程的优先级设置为后台,为了执行完任务方便回收
               Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
               //后台执行任务
               result = doInBackground(mParams);
               Binder.flushPendingCommands();
           } catch (Throwable tr) {
            //有异常将此任务取消
               mCancelled.set(true);
               throw tr;
           } finally {
             //任务结束
             postResult(result);
           }
           return result;
       }
   };
   

FutureTask:线程任务(理解为要执行的任务)

这里将mWorker任务的参数封装成FutureTask对象,按照中式英语理解的话就是将来要执行的任务。本质是一个Runnable对象。

mFuture = new FutureTask<Result>(mWorker) {
       @Override
       //任务完成后的回调
       protected void done() {
          try {
	          //调用下方postResult方法
               postResultIfNotInvoked(get());
           } catch (InterruptedException e) {
               android.util.Log.w(LOG_TAG, e);
          } catch (ExecutionException e) {
               throw new RuntimeException("An error occurred while executing doInBackground()",
                            e.getCause());
          } catch (CancellationException e) {
		       //调用下方postResult
               postResultIfNotInvoked(null);
        }
     }
};

当线程任务执行结束后,调用postResult(Result result)通过InternalHandler将线程池(子线程任务)切换到主线程中。


private Result postResult(Result result) {
    @SuppressWarnings("unchecked")
    Message message = getHandler().obtainMessage(MESSAGE_POST_RESULT,
           new AsyncTaskResult<Result>(this, result));
    message.sendToTarget();
    return result;
}

getHandler获取的就是InternalHandler对象,这是一个静态内部类。切换线程作用。

private static class InternalHandler extends Handler {
    public InternalHandler(Looper looper) {
         super(looper);
     }

    @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"})
    @Override
    public void handleMessage(Message msg) {
        AsyncTaskResult<?> result = (AsyncTaskResult<?>) msg.obj;
        switch (msg.what) {
	        //任务结束标志,返回结果
            case MESSAGE_POST_RESULT:
	                // There is only one result
	                result.mTask.finish(result.mData[0]);
                break;
             //任务执行过程,返回进度 
             case MESSAGE_POST_PROGRESS:
                    result.mTask.onProgressUpdate(result.mData);
                break;
         }
    }
}

exec:线程池执行类

execSerialExecutor串行线程池对象,主要作用是将要执行的任务mFutureTask通过此类被插入到任务队列mTasks中,整合任务执行顺序作用。会不断从队列中取出任务来执行,如果当前任务执行完毕,mTasks会查找队列顶端是否有mFutureTask对象。有的话就执行。没有任务结束。


//源码中不是这样写的,这里为了方便理解
public static final Executor exec = new 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() {
	        //队列最上方的一个元素不为null即队列顶端有空闲的任务
            if ((mActive = mTasks.poll()) != null) {
	         	//真正执行任务的线程池:THREAD_POOL_EXECUTOR核心类ThreadPoolExecutor对象
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }
   

其实真正执行任务的线程池是:ThreadPoolExecutor核心类的对象THREAD_POOL_EXECUTOR

THREAD_POOL_EXECUTOR.execute(mActive);

目前 AsyncTaskAndroid3.0以上版本的执行顺序是串行的,有个多个任务添加到mTasks队列中,执行的顺序是从队列顶端往下依次执行。如果想实现多线程的并行任务。只要将

 MyAsyncTask("AsyncTask1").execute()

替换为即可:

 MyAsyncTask("AsyncTask1").executeOnExecutor()

总结

Android中多线程任务虽然没有Java中使用频率高,但是是一个比较重要的知识点。AsyncTask可以在多线程任务中发挥好的作用,比如项目中的在线更新等。线程池也是特别重要的知识点,几乎是面试必问,接下来将会介绍线程池的工作原理等相关知识。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值