当前位置:首页 » SEO知识

AsyncTask学习

2017-10-31 14:40 本站整理 浏览(5)

在学习Android的时候,开始用到比较多的异步处理的类大概就是AsyncTask,但是我们很多时候只知道调用,却不知道思考一些东西。

本文就简单的总结和分析了一些AsyncTask的知识。

一、AsyncTask使用
直接继承AsyncTask,它一共有3个泛型参数Params, Progress, Result.

public abstract class AsyncTask<Params, Progress, Result> 
  • Params表示AsyncTask执行任务的参数的类型(传入的参数的类型,在doInBackground用到的,使用execute方法中传入)
  • Progress表示在后台线程处理的过程中,可以阶段性地发布结果的数据类型(在onProgressUpdate中用到,使用publicProgress中传入)
  • Result表示任务全部完成后所返回的数据类型(doInBackground方法的返回值)

必须实现的方法有一个:

protected abstract Result doInBackground(Params... params);

这个方法是在异步线程中执行的操作,我们主要处理的也是这个方法。

有3个方法需要熟悉:

#异步执行之前也就是doInBackground之前执行
protected void onPreExecute()
#异步执行之后也就是doInBackground之后执行
protected void onPostExecute(Result result)
#执行过程中执行
protected void onProgressUpdate(Progress... values) 

这3个方法都是在UI线程中,所以可以直接更新UI

执行的方法有两个:

public final AsyncTask<Params, Progress, Result> execute(Params... params) {

execute方法,每次只能执行一个线程,不支持并发。

public final AsyncTask<Params, Progress, Result> executeOnExecutor(Executor exec,
            Params... params) {

executeOnExecutor添加自己的线程池,那么可以支持并发。

AsyncTask使用的注意事项

  • AsyncTask的实例必须在主线程中创建。

  • AsyncTask的execute方法必须在主线程中调用。

  • onPreExecute()、onPostExecute(Result),、doInBackground(Params…) 和 onProgressUpdate(Progress…)这四个方法都是回调方法,Android会自动调用,我们不应自己调用。

  • 对于一个AsyncTack的实例,只能执行一次execute方法,在该实例上第二次执行execute方法时就会抛出异常。

前两条基本是一个意思,就是使用的AsyncTask时候在UI线程中创建执行。4个主要的方法不能在外部去调用,一个AsyncTask执行一次。

二、源码分析

AsyncTask主要实现是线程池+handler来实现的。

线程池:

    private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
    // We want at least 2 threads and at most 4 threads in the core pool,
    // preferring to have 1 less than the CPU count to avoid saturating
    // the CPU with background work
    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;
    private static final int KEEP_ALIVE_SECONDS = 30; 

    /**
     * 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;
    }

使用ThreadPoolExecutor创建一个线程池,这线程次的核心线程是动态的,根据cpu的数量来定(2-4)个,在4.4之前是核心线程数是5。

    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);
            }
        }
    }

把Runnable放入双端队列末尾,交给线程池去处理。(java.util.ArrayDeque.offer(E e) 方法将指定的元素E在此deque队列的末尾。

java.util.ArrayDeque.poll() 检索并移除此queue队列表示的队列的头部。返回null如果此queue队列为空。)

那么具体的线程在哪呢?

        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);
                }
            }
        };

主要就是这个FutureTask+Callable实现了异步线程,WorkerRunnable 实现了Callable接口。

(实现多线程的方式有几种?可能现在需要多记一种Callable)

主要Handler:

    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;
            }
        }
    }

在实例化AsyncTask的时候回去准备好线程池、线程和handler,在execute执行的时候把线程扔到线程池中执行,执行中可以发送数据handler(publishProgress),结束后发消息给handler(postResult)。

根据源代码可以总结出:

  1. execute方法会调用executeOnExecutor方法,使用的线程池还是默认的
  2. executeOnExecutor和execute在UI线程执行,那么里面调用的所有方法也是在UI线程中,所以onPreExecute()方法在UI线程中
  3. handler使用的是UI线程的looper = Looper.getMainLooper(),那么Handler的handleMessage方法在UI线程中,所以onPostExecute和onProgressUpdate在UI线程中。

AsyncTask的注意事项基本可以从源码中看出原因,基本的AsyncTask总结就这些,希望一个不一样的自己。