Andorid异步处理之AsyncTask

Andorid异步处理之AsyncTask
         上一节我们讲了Handler的异步处理机制,利用线程和Handler的收发消息来实现异步处理,十分的灵活和方便,而今天我们要讲的是google封装的专门用来处理异步的AysncTask框架
 AsyncTask是一个轻量级的异步框架,根本原因其实和Handler机制的原理是一样的,主要是为了提供异步机制来解决无法在子线程中更新UI问题。而AsyncTask中封装了线程池和Handler,省去了频繁创建和回收线程的消耗以及操作Handler机制的相对复杂问题。更加轻便简洁。
        在讲解源码以前,首先让我们看看如何使用过AsyncTask。

一、AsyncTask使用
        1、继承AsyncTask
           AsyncTask是一个抽象类,因此这里我们需要继承AyncTask抽象类  public abstract class AsyncTask<Params, Progress, Result> ,并实现唯一的抽象方法doInBackground()。具体如下:

class MyAsyncTask extends AsyncTask<String,Integer,String>
{
    @Override
    protected void onPreExecute() {
        super.onPreExecute();
        tv_name.setText("即将开始");
    }

    @Override
    protected void onPostExecute(String aVoid) {
        super.onPostExecute(aVoid);
        tv_name.setText(aVoid);
    }

    @Override
    protected void onProgressUpdate(Integer... values) {
        Log.d("yy",values[0].intValue()+"");
        tv_name.setText("正在进行"+values[0].intValue());
    }

    @Override
    protected void onCancelled(String string) {
        super.onCancelled(string);
    }

    @Override
    protected void onCancelled() {
        super.onCancelled();
    }

    @Override
    protected String doInBackground(String... strings) {
        int i = 5;
        while(i>0)
        {
            i--;
            publishProgress(i);
        }
        return "完成";
    }
}

      这里可以看到AsyncTask抽象类中有三个泛型 <Params, Progress, Result>, 现在再来分析这三个泛型参数:
  • Params  作为后台任务protected String doInBackground(params... strings)执行的参数类型
  • Result 作为后台任务结束后返回的参数类型,用于下表编号为3、5的方法
  • Progress 用于后台任务执行过程中的进度显示 ,用于下表中4、7方法

        接下来我们来通过一个表格说明AsyncTask常用的方法:
编号
方法
参数
作用
执行线程
1
protected void onPreExecute ()
在执行后台任务钱调用,进行一些准备工作
主线程
2
protected String doInBackground (params... strings)
params
在此方法中执行我们的后台任务并在任务结束后返回Result参数,在方法中调用publishProgress(Progress Value),则会对应调用onProgressUpdate(Progress... values)
子线程
3
protected void onPostExecute(Result result)
Result
在后台任务完成后调用,并返回结果Result参数
主线程
4
protected void onProgressUpdate(Progress... values)
Progress
在后台任务执行时进行进度的更新
主线程
5
protected void onCancelled(Result result)
Result
在后台任务被取消时调用
主线程
6
protected void onCancelled()
在后台任务被取消时调用
主线程
7
protected final void publishProgress(Progress... values)
Progress
在执行后台任务时传递执行进度,会调用 onProgressUpdate(Progress... values)
子线程
这就是一个简单的AsyncTask使用,可以看到,除了doInBackground()和publishProgress()外,其他方法都是在UI线程中被调用的
在使用是需要在UI线程中实例化AsyncTask,并在UI线程中执行execute()方法。到这里呢,AsyncTask的使用我们就介绍完了,接下来开始分析源码了。

二、源码分析

       1、AsyncTask中的Handler封装
            
private static class InternalHandler extends Handler {
    public InternalHandler() {
        super(Looper.getMainLooper());
    }

    @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;
        }
    }
}
这里的InternalHandler继承了Handler,在构造方法中调用了 super(Looper.getMainLooper());由于AsyncTask的实例化和execute的执行都是在UI线程,因此这里的Handler也会是创建在主线程,自然Looper也是获得主线程的Looper。
因此看到这里我们可以大胆猜测,在我们执行异步任务的地方会调用InternalHandler发送消息,消息为MESSAGE_POST_RESULT和MESSAGE_POST_PROGRESS,然后在Handler中收到消息后对应的执行方法。


2、线程池分析
这里先简单介绍Executor框架,Executor框架主要包含任务,任务的执行。
首先分析,任务:被执行的任务需要实现Runnnable接口和callable接口。通常我们使用FurtureTask(不熟悉的自行百度)
                    任务的执行:一个实现Executor或者是继承 ExecutorService接口的自定义类,或者使用Executor已经提供了两个来ThreadPoolExecutor 和 ScheduledThreadPoolExecutor。

那么我们首先到源码找任务,也就是FurtureTask,可以看到,它在AsyncTask的构造方法中完成了实例化,具体如下

public AsyncTask() {
  mWorker = new WorkerRunnable<Params, Result>() {
        public Result call() throws Exception {
            mTaskInvoked.set(true);
            Result result = null;
            try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result);
            }
            return result;
        }
    };

   mFuture = new FutureTask<Result>(mWorker) {
        @Override
        protected void done() {
            try {
                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) {
                postResultIfNotInvoked(null);
            }
        }
    };
而在FurtureTask中传入了一个WorkRunnable,那么这个WorkRunnable又是什么呢?

private static abstract class WorkerRunnable<Params, Result> implements Callable<Result> {
Params[] mParams;
}

它是一个静态抽象类,实现了Callable接口。
好了,到这里这个传入WorkRunnable的FurtureTask就是我们上面说的执行的任务。既然是我们执行的异步任务,那么我们在doInBackground就应该这里执行,好的,那么回到WorkRunnable中的call()方法可以看到,在call方法中执行了如下方法

  try {
                Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
                //noinspection unchecked
                result = doInBackground(mParams);
                Binder.flushPendingCommands();
            } catch (Throwable tr) {
                mCancelled.set(true);
                throw tr;
            } finally {
                postResult(result);
            }
            return result;
这里我们就可以看到执行了result = doInBackground(mParams);,然后在finally中将result传入  postResult(result);中。这里我们先不讲postResult(result),之后会详细说明。
我们已经找到了Executor的任务,那么任务的执行类在哪里呢?这就需要我们回到代码里,如下:
那么我们就需要找到这个FurtureTask在哪里被调用?
首先我们看AsyncTask的execute()方法,发现它调用了executeOnExecutor(sDefaultExecutor, params);然后再进入executeOnExecutor()方法,这个时候就可以看到,exec.execute(mFuture);,而这里的mFuture就是我们的FurtureTask,而exec则是传入的sDefaultExecutor。那么sDefaultExecutor又是什么呢?
这个sDefaultExecutor就是我们的默认任务执行者。

private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;

而这个SERIAL_EXECUTOR如下:、

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);
        }
    }
}
也就是说实际上是执行了SerialExecutor的execute()方法。在执行完一个线程后,后调用scheduleNext();
进入 scheduleNext()会发现,这里是调用了THREAD_POOL_EXECUTOR来执行异步任务。
那么现在进入 THREAD_POOL_EXECUTOR。

private static final int CORE_POOL_SIZE = Math.max(2, Math.min(CPU_COUNT - 1, 4));
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
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是一个ThreadPoolExecutor,也是一个任务执行的线程池。其中CORE_POOL_SIZE为可以同时运行的线程数,而MAXIMUM_POOL_SIZE为可以同时有多少线程数。
在Andorid3.0以前,AsyncTask是支持5个线程同时执行的,缓冲队列中最多可以有128个。而在Android3.0以后,改为每次只能执行1个线程,之后才可以执行第二个。



原来WorkRunnable是一个实现了Callable接口的抽象类,而这个workRunnable作为参数传入FurtureTask中,而FutureTask实现了Runnable和Furture接口。
再看SerialExecutor的execute执行的Runnable,也就是说实际执行的FutureTask,并且执行的run方法是FutureTask的run方法,而在FurtureTask的run方法中则会调用Callable接口的call方法,也就是workRunnable的call()方法。

好了,到这里整理一次,SerialExcutior是执行线程的异步类,执行的是Runnable,也就是执行的是FurtureTask,具体呢则是作为参数传入FurtureTask的WorkRunnable的call方法。现在线程池,和任务就明了了。

3、前面我们已经介绍了线程池和Handler,但是这两者又是怎么样的关系呢?接下来我们就要分析AsyncTask的execute方法()分析
       首先来看execute()方法

@MainThread
public final AsyncTask<Params, Progress, Result> execute(Params... params) {
    return executeOnExecutor(sDefaultExecutor, params);
}
需要注意这里的execute()需要在UI线程中执行,并且只能执行一次。

接下里看executeOnExecutor(sDefaultExecutor, params);    这里的sDefaultExecutor就是我们前面提到的执行任务的SerialExecutor。
继续向下看

@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;
}
到这里,我们会发现,会有一个判断if (mStatus != Status.PENDING),现在看这个Status,
public enum Status {
    /**
     * Indicates that the task has not been executed yet.
     */
    PENDING,
    /**
     * Indicates that the task is running.
     */
    RUNNING,
    /**
     * Indicates that {@link AsyncTask#onPostExecute} has finished.
     */
    FINISHED,
}
可以看到Status有三个状态:
  • PENDING 表示当前的AsyncTask没有执行过execute
  • RUNNING 表示当前的AsyncTask正在执行任务
  • FINISHED 表示任务已经执行完成
回到代码里,我们发现只有在 PENDING状态时,才能执行execute,也就是在没有执行过execute()方法时才可以执行。如果任务正在执行,则Status状态为RUNNING,此时抛出new IllegalStateException("Cannot execute task:  the task is already running.");如果Status为FINISHED,此时抛出new IllegalStateException("Cannot execute task: the task has already been executed  (a task can be executed only once)")。
那么现在来解决一个问题为什么AsyncTask只能执行一次execute()?
原因是执行过一次execute()并结束任务后,AsyncTask会将Status置为FINISHED。因此在下一次执行时则会抛出异常。

好的,状态判断完毕了,继续往下走。 因为接下来要执行任务了,所以将Status置为RUNNING,  mStatus = Status.RUNNING;
到这里就会发现我们熟悉的方法,首先是    onPreExecute();就是在执行后台任务前我们执行准备的方法,然后     exec.execute(mFuture);,到这里就开始执行后台任务,传入的参数就是我们前面提到的FurtureTask,那么我们再回到SerialExecutor中,会发现是调用了FurtureTask的run方法,也就是调到了WorkRunnable的call方法。

mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Result result = null;
try {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
result = doInBackground(mParams);
Binder.flushPendingCommands();
} catch (Throwable tr) {
mCancelled.set(true);
throw tr;
} finally {
postResult(result);
}
return result;
}
};
在call()方法中会发现result = doInBackground(mParams);,这里调用了我们的doInBackground()方法,执行我们的后台任务。执行完成后返回结果result,然后调用postResult(result);

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,这里是发送了一个消息MESSAGE_POST_RESULT,message.sendToTarget();这里的Target也是我们的InternalHandler。InternalHandler接收到了消息后调用result.mTask.finish(result.mData[0]);
这里的result是AsyncTaskResult,具体如下:

@SuppressWarnings({"RawUseOfParameterizedType"})
private static class AsyncTaskResult<Data> {
    final AsyncTask mTask;
    final Data[] mData;

    AsyncTaskResult(AsyncTask task, Data... data) {
        mTask = task;
        mData = data;
    }
}

它是一个数据封装类,用于传递我们需要的数据,并在每次发送消息时,都会传入当前的AsyncTask对象。这里的result.mTask.finish(result.mData[0]);就是调用当前AsyncTask的finish()方法,我们接着看finish()方法

private void finish(Result result) {
    if (isCancelled()) {
        onCancelled(result);
    } else {
        onPostExecute(result);
    }
    mStatus = Status.FINISHED;
}

可以看到会判断是否调用的取消,如果没有调用,则会调用onPostExecute(result);并将结果传进去。同时会将状态置为FINISHED.


如果我们调用publishProgress,则会发送给一个MESSAGE_POST_PROGRESS消息,然后调用onProgressUpdate方法,并且将进度传入。

二、优缺点
  优点:(1)简单便捷,封装性好,不需要我们去手动创建线程和Handler。
               (2)过程可控,我们可以控制任务的启动和停止,同时还可以获得任务进度
 缺点:(1)在处理多个异步操作并共同进行UI变更时,比较复杂。
             (2)在Android3.0以前,线程池中真正的工作线程只有5个,多了需要排队,Android3.0以后,则会根据cpu数目判断允许同时运行的线程数目,但是线程都是依次执行的。
             (3)一个AsyncTask的execute方法只能执行一次。

三、和Handler比较
        Handler优点:结构清晰,功能定义明确;对于多个后台任务时,简单清晰
                        缺点:在单个后台处理异步时,相对AsyncTask代码复杂一些。
















猜你喜欢

转载自blog.csdn.net/ckq5254/article/details/79955180