【Android源码解析】从源码角度深入分析AsyncTask原理

###一. 什么是AsyncTask? 是Android提供的**轻量级**的异步类,封装了**线程池和handler**。它是个抽象类,使用时需要去继承它,然后去实现异步操作,并反馈异步执行的**结果**。

###二. AsyncTask使用方法
首先先来看个例子,这是之前写过的一个gif图片下载的异步任务。使用AsyncTask要注意它的***三个参数***和***五个方法***。

  1. 三个参数
    第一个,代表执行AsyncTask传入的参数;
    第二个,代表执行后台任务的进度;
    第三个,代表执行完任务返回的结果。
  2. 五个方法
    (1)onPreExecute:在ui线程调用,后台操作执行前调用;经常用于显示一个进度条。
    (2)doInBackground:做的是耗时操作,并返回执行结果。同时,可以在doInBackground中调用publishProgress来发布进度单位。
    (3)onPostExecute:接受执行结果。
    (4)onProgressUpdate:当publishProgress调用完该方法被执行,一般用于控制进度条的进度。
public class GifImageDownLoader extends AsyncTask<String, Void, File> {
    private static final String foldPath = Environment.getExternalStorageDirectory() + "/CTRIP/cache/";
    private ICallBack<File, String> mCallBack;

    public GifImageDownLoader(ICallBack<File, String> callBack) {
        this.mCallBack = callBack;
    }

    @Override
    protected File doInBackground(String... params) {
        String[] url = params[0].split("/");
        String gifName = url[url.length - 1];
        File f = new File(foldPath + gifName);
        if (!f.exists()) {
            f = downLoadGifImage(params[0], gifName);
        }
        return f;
    }

    @Override
    protected void onPostExecute(File f) {
        if (isCancelled() || f == null) {
            mCallBack.fail(null);
            return;
        }
        mCallBack.success(f, null);
    }

    private File downLoadGifImage(String gifUrl, String gifName) {
        File gifImageFile = null;
        InputStream is = null;
        FileOutputStream fos = null;
        try {
            URL downLoadUrl = new URL(gifUrl);
            HttpURLConnection conn = (HttpURLConnection) downLoadUrl.openConnection();
            conn.setConnectTimeout(5000);
            conn.setRequestMethod("GET");
            if (conn.getResponseCode() == 200) {
                File fold = new File(Environment.getExternalStorageDirectory() + "/CTRIP/cache");
                if (!fold.exists()) {
                    fold.mkdirs();
                }
                is = conn.getInputStream();
                gifImageFile = new File(foldPath, gifName);
                fos = new FileOutputStream(gifImageFile);
                int len = 0;
                byte[] buffer = new byte[1024];
                while ((len = is.read(buffer)) != -1) {
                    fos.write(buffer, 0, len);
                }
                fos.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
                if (fos != null) {
                    fos.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return gifImageFile;
    }

}

###三. AsyncTask内部原理
Android主线程肩负绘制UI界面和即使响应用户操作的重任,所以为了避免用户点击按钮没有反应,应该把耗时操作放在子线程进行。

  1. AsyncTask本质是一个静态的线程池,其子类实现的异步任务都提交到静态线程池中进行。
  2. 线程池的工作线程执行doInBackground(mParams)方法执行异步任务。
  3. 当任务状态改变后,子线程向UI线程发送消息,AsyncTask内部的handler相应这些消息,并调用相关回调函数。

###四. 线程池ThreadPoolExecutor
创建线程池一般不直接new一个线程池对象,因为需要配置一堆东西,我们来看下其构造方法:
这里写图片描述
所以官方也不推荐我们这样创建线程池,而是推荐使用Executors的工厂方法来创建线程池,Executors是官方提供的一个工厂类,它里面封装了众多功能的线程池,从而使得我们创建线程池非常简便,主要提供了五种线程池:
#####1. newFixedThreadPool()
创建一个固定线程数量的线程池,该线程池中的线程数量都不变,即不会创建新的线程,也不会销毁已经创建好的线程,自始至终都是那几个固定的线程工作。
如果有空闲线程则处理任务,如果没有则将任务放到一个队列中,一旦线程空闲了则按FIFO的方式处理队列中的任务。

public static ExecutorService newFixedThreadPool(int nThreads) {
        return new ThreadPoolExecutor(nThreads, nThreads,
                                      0L, TimeUnit.MILLISECONDS,
                                      new LinkedBlockingQueue<Runnable>());
}

#####2. newCachedThreadPool()
该线程池中的线程数量不固定,可根据实际情况调整。
如果有新任务提交,当没有空闲线程则创建新线程,如果有空闲线程则复用该空闲线程,当空闲线程空闲时间超过配置的等待时间则被销毁。

 public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
}

#####3. newSingleThreadPool()
只有一个线程的线程池。

public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
}

#####4. newScheduledThreadPool()
可定时执行某任务的线程池。

public static ScheduledExecutorService newScheduledThreadPool(
            int corePoolSize, ThreadFactory threadFactory) {
        return new ScheduledThreadPoolExecutor(corePoolSize, threadFactory);
}

#####5. newSingleThreadScheduledPool()
只有一个线程的可定时执行某任务的线程池。

可以看到工厂方法最终都是通过ThreadPoolExecutor来创建线程池。在它的构造方法的参数中,最重要的是BlockingQueue任务队列,不同线程池的任务队列实现方法肯定不同。
而线程池的使用也很简单,当创建好线程池后,只需要调用其execute方法即可。

####AsyncTask源码解析
AsyncTask的源码其实非常短,只有700多行。我们先来看AsyncTask的构造方法。
#####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);
                }
            }
        };
    }
  1. workRunnable是一个Callable,大家可能对Callable不熟悉,它其实跟Runnable使用非常像,只不过Callable有返回值而Runnable没有。
  2. FutureTask是Callable的包装类。
    ####execute方法
    execute方法其实执行的是executeOnExecutor方法。
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;
}

可以看到executeOnExecutor传入的第一个参数是sDefaultExecutor,这是一个线程池。我们稍后来分析这个线程池,我们先来看下executeOnExecutor里都做了什么。

  1. 首先是判断AsyncTask当前的执行状态,只有当前状态是Status.PENDING才可继续使用;
  2. 接着设置当前状态为RUNNING状态。
  3. 然后调用onPreExecute方法。
  4. 接着为mWorker设置参数,并执行该任务。
    #####AsyncTask中线程池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() {
            if ((mActive = mTasks.poll()) != null) {
                THREAD_POOL_EXECUTOR.execute(mActive);
            }
        }
    }

可以看到SerialExecutor就是将异步任务一个一个放入队列里,然后再去执行。不过还需要注意的是execute方法加上了synchronized修饰,从而保证AsyncTask中的任务是串行执行的
###五. 内部handler
当任务执行完通过internalHandler讲执行结果同步给主线程。

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

###六. 最后
以上就是我对AsyncTask源码的分析,后续当我有新的理解时还会继续更新。至于为什么想到要去看AsyncTask的源码了呢?主要是因为最近想要改进一下我们的缓存机制,希望能在合适的时机清理一下缓存,我们用AsyncTask比较多,也知道它是封装了线程池和handler,但具体怎么实现的还真没有系统地读过。
好了,时间不早了,现在是夜里十二点十五,大家晚安。如有任何疑问可以邮件联系我zhshan@ctrip.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值