根据3个问题来加深对AsyncTask源码的理解

根据下列的问题来加深对AsyncTask源码的理解

由于个人水平有限,若本文存在问题,还望指出。
1.AsyncTask中封装了几个线程池,作用分别是啥。
2.AsyncTask对象为何要在主线程创建
3.AsyncTask对象为何只能执行一次

使用场景

在Android中封装了子线程的类有很多,如IntentService,AsyncTask,HandlerThread等,它们有着不同的使用场景。IntentService是Service的封装,方便service进行耗时操作的处理,同时作为四大组件之一,它的优先级很高,不容易被杀死。AsyncTask里面封装了线程池和handler,适用于多任务的处理的和主线程与子线程之间的信息交互。HandlerThread里面封装好了Looper。

基本使用

1.最简单的使用

 AsyncTask task = new AsyncTask() {
            @Override
            protected Object doInBackground(Object[] objects) {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                TLog.e("task",Thread.currentThread().getName());
                return null;
            }
        };
        task.execute();

上述代码,将AsyncTask做为内部类使用,且只重写了AsyncTask中的抽象方法。代码日志为:在这里插入图片描述
可以看出在doInBackground中函数中,使用的是子线程,这也是为啥可以在这个函数中做耗时操作的原因。但上述代码中存在一个非常明显的问题。内存泄漏,因为内部类会隐式的持有外部类的引用,且在内部类中有耗时操作。于是,在我们使用Asynctask时,一般都是建议使用静态类或者外部类,如下代码所示。

public static class MyTask extends AsyncTask<Void,Integer,Boolean>{
        @Override
        protected Boolean doInBackground(Void... voids) {
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return null;
        }
    }

且如果需要在该类中使用context时,建议使用Application或者采用弱引用。如以下代码所示。

 		Context mContext;
        Context context;
        WeakReference<MainActivity> mReference;
        public MyTask(Context context){
            mContext = context.getApplicationContext();
        }

        public MyTask(MainActivity activity){
            mReference = new WeakReference<>(activity);
            context = mReference.get();
        }

成员变量

	public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
    private static final int MESSAGE_POST_RESULT = 0x1;
    private static final int MESSAGE_POST_PROGRESS = 0x2;
    private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
    private static InternalHandler sHandler;
    private final WorkerRunnable<Params, Result> mWorker;
    private final FutureTask<Result> mFuture;
    private volatile Status mStatus = Status.PENDING;
    public static final Executor THREAD_POOL_EXECUTOR;

SERIAL_EXECUTOR:串行线程池,串行取出WorkRunable(API-26)
MESSAGE: UI线程与doInbackground中的子线程通信的消息
sHandler :UI线程中的handler
mWorker:实现了callable接口,在mWorker中封装了doinbackground的方法,doinbackground方法的输入参数为Params,并将结果以Result参数返回。
(callable接口和runnable接口类似,只不过callable支持泛型和有返回值)
mFuture:实现了runable接口和future接口,线程池中执行的runnable为此mFuture
mStatus:Asynctask当前实例的状态,在执行的时候,若mStatus的状态部位Pending,则会抛出异常,这是限制同一AsyncTask实例执行两次的源头。
THREAD_POOL_EXECUTOR:处理任务的线程池
从这个这些参数可以得出以下几点:
1.看出AsyncTask中有两个线程池
2.主线程和子线程之间的通信也是通过handler进行

方法介绍

1.构造方法

源码中有3个构造方法,但实际上就一个,如下所示。(把源码那些非主要逻辑的代码给清楚掉了,这样看出去更清楚。)

Creates a new asynchronous task. This constructor must be invoked on the UI thread.> 源码英文介绍的意思大概是,创建一个异步的任务,这个构造器必须在ui线程执行。

 public AsyncTask(@Nullable Looper callbackLooper) {
        mHandler = callbackLooper == null || callbackLooper == Looper.getMainLooper() ? getMainHandler(): new Handler(callbackLooper);
        mWorker = new WorkerRunnable<Params, Result>() {
            public Result call() throws Exception {
              ...
                Result result = null;
                try {
                    ...
                    result = doInBackground(mParams);
                  ...
                } catch (Throwable tr) {
                   ...
                } finally {
                    postResult(result);
                }
                return result;
            }
        };

        mFuture = new FutureTask<Result>(mWorker) {
            @Override
            protected void done() {
           			...
                }
            }
        };
    }

如果非要转牛角尖,该构造函数,在有loop的子线程中创建,其实也不会报错,也可以正常执行,但源码上面的都说了要在UI线程执行了,就在UI线程执行吧,还有另外一点是低版本的源码是必须在UI线程执行的,不然要出错,权当是为了兼容低版本吧。
API-26版本中的Handler的创建,如下所示

API-26
 private static Handler getMainHandler() {
        synchronized (AsyncTask.class) {
            if (sHandler == null) {
                sHandler = new InternalHandler(Looper.getMainLooper());
            }
            return sHandler;
        }
    }
 低版本
 private static final InternalHandler sHandler = new InternalHandler();

API-26中的handler使用的loop是主线程中的loop。
低版本中的Handler的使用的是创建AsyncTask中的线程中的loop。(静态成员会在加载类时进行初始化)从这点来看的话,AsyncTask就必须要在主线程中创建了。

该构造函数中,创建了mWorker 和mFuture两个重要的成员变量。
mWorker中最重要的两个方法是doInBackground和postResult,doInBackground方法是一个抽象方法,输入参数为泛型Params,是我们必须重写的,然后在里面写具体的处理逻辑,返回值为泛型Result。postResult为将doInBackground方法的返回通过handler发送给主线程。
mFuture方法既然实现了runable接口,那么我们肯定要看它的run方法,代码如下。

 public void run() {
       ...
        try {
            Callable<V> c = callable;
            if (c != null && state == NEW) {
                V result;
                boolean ran;
                try {
                    result = c.call();
                    ran = true;
                } catch (Throwable ex) {
                  ....
                }
                if (ran)
                    set(result);
            }
        } finally {
           ....
        }
    }

上述代码中callable就是mWorker,然后调用了mWorker.call,返回值result就是doInBackground的返回值。看到这,可以知道,在mFuture中执行了mWorker,mWorker中执行了doInbackground,即具体的子线程处理逻辑,然后将结果通过postResult方法中的handler传递给了主线程。

2.execute方法

执行方法也有3个,我们常用的为两个,且第一个是对第二个的封装。

  public final AsyncTask<Params, Progress, Result> execute(Params... params) {
        return executeOnExecutor(sDefaultExecutor, params);
    }
  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方法,executeOnExecutor方法中第一个参数为线程池,这里使用的是sDefaultExecutor线程池,该线程池将mWorker排队取出,一个接一个的扔到THREAD_POOL_EXECUTOR线程池中串行执行(执行完一个再扔下一个)。如果要并行执行,则可以直接使用executeOnExecutor方法,并将输入参数exec设置成THREAD_POOL_EXECUTOR线程池。

执行逻辑

在这里可以看到通过mStatus成员变量去判断该Asynctask的实例是否第一次执行,若不是,则会抛出异常。然后将Params参数赋值给了mWork.mParams,即execute方法中输入的Params最终到了doInbackground的输入,接着通过exec线程池执行mFuture。AsyncTask默认的是串行执行(低版本的有并行执行的,但如今的高版本都是串行执行),使用的是sDefaultExecutor线程池。

3.Cancel方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值