在学习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队列表示的队列的头部,如果此queue队列为空,返回null)

具体的线程:

        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学习的更多相关文章

  1. Android AsyncTask学习

    Android程序有UI进程和后台进程,在执行一些耗时的操作时,如果在UI进程中,很可能出现假死的情况,用户体验会受到影响,因此,那些耗时进程往往就放在了后台进程中,用户体验能更好一些.网络情况不稳定 ...

  2. 如何自学Android--转

    原文地址:http://blog.csdn.net/lavor_zl/article/details/51217319 1. Java知识储备 本知识点不做重点讲解: 对于有基础的同学推荐看<J ...

  3. 如何自学 Android 的?

    http://android.jobbole.com/83380/ 1. Java知识储备 本知识点不做重点讲解:对于有基础的同学推荐看<Java编程思想>,巩固基础,查漏补全,了解并熟悉 ...

  4. 我是怎样自学 Android 的?

    1. Java知识储备 本知识点不做重点解说: 对于有基础的同学推荐看<Java编程思想>,巩固基础,查漏补全,了解并熟悉很多其它细节知识点. 对于没有基础的同学推荐看一本Java基础的书 ...

  5. AsyncTask官方学习

    异步任务学习 这两天使用到特别多的AsyncTask类,一块来学习一下吧 AsyncTask允许更方便和简单使用UI线程,这个类允许你在UI线程中进行后台操作和展示结果,而无需操作Thread或者ha ...

  6. Android学习之AsyncTask

    我们在<Android学习之Handler消息传递机制>(http://www.cnblogs.com/zhouhb/p/5812447.html)已提到过,Android只允许UI线程修 ...

  7. Android 学习笔记多媒体技术之 AsyncTask+实现音频播放...

    PS:今天搞了一下如何实现音频播放...结果被坑了,看书上写的代码是挺简单的,但是有个函数就是死活没看懂,这真是受不了...最后才弄明白,原来是一个实现异步任务的一个类...这个类使用java.uti ...

  8. Android应用开发学习笔记之AsyncTask

    作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz 在上一篇文章中我们学习了多线程和Handler消息处理机制,如果有计算量比较大的任务,可以创建一个新线程执行计算工作 ...

  9. Android开发学习之路--异步消息Handler,Message,Looper和AsyncTask之初体验

    在简易音乐播放器中,用了Handler,也没有过多地去研究学习,这里再学习下android下的异步消息处理机制.这里用了Handler主要是在线程中不能更新UI,而需要通过Handler才可以.关于异 ...

随机推荐

  1. python实例编写(3)--对话框,多窗口,下拉框,上传文件

    一.对话框: 例:点击百度的登录,弹出的小窗口 #coding=utf-8 from selenium import webdriver from time import sleep dr=webdr ...

  2. 利用ASCII码生成指定规则的字符串

    /** * 上送终端编号的后两位生成规则 总共可以生成 (36*36-1)1295个编号 * 01...09 0A...0Z * 10...19 1A...1Z * ............... * ...

  3. net core 安装web模板

    ---恢复内容开始--- 今天想试试在Linux用C#开发WebAPI,查了下,要用: dotnet new -t Web 来建立工程,结果我试了下,出来这段: Invalid input switc ...

  4. hadoop源码import到eclipse工程

    1.解压hadoop-1.1.2.tar.gz,重点在src文件夹 2.在eclipse中通过菜单栏创建一个java工程,工程名随便 3.在创建的工程上,点击右键,在弹出菜单中选择最后一项,在弹出窗口 ...

  5. Linux - 设置/取消代理

    export http_proxy=118.210.42.251:44367 或: export https_proxy=118.210.42.251:44367   要取消该设置: unset ht ...

  6. vue-ajax小封装

    1. js 文件: /** ajax封装:* 1. 引入文件* 2. new Vue().ajax.get(url,data,fn,ojson), 或 new Vue().ajax.post(url, ...

  7. 【ASP.NET MVC】jqGrid 增删改查详解

    1   概述 本篇文章主要是关于JqGrid的,主要功能包括使用JqGrid增删查改,导入导出,废话不多说,直接进入正题. 2   Demo相关 2.1   Demo展示 第一部分 第二部分 2.2 ...

  8. 记2017问鼎杯预赛的wp---来自一个小菜鸡的感想

    这次准备写一下几个misc和密码题目..很坑. 打了一整天的比赛,越来越觉得自己很菜了. 有一道题目叫做"真真假假",这道题目只有一个提示--Xor.第一眼知道是异或,也就知道这一 ...

  9. Linux之不得不说的init(Linux启动级别的含义 init 0-6)

    init 0:关机: init 1:单用户模式(只root进行维护): init 2:多用户 init 3:完全多用户 init 4:安全模式 init 5:图形化 init 6:重启 可以在/etc ...

  10. uva11538

    解题思路: 1. 计数问题, 有三种相对摆放方式: 水平, 竖直, 对角线. 根据加法原理即可, 并且没有交集. 水平和竖直是一样的, 只要n*m矩形旋转90度. 所以结果是: n*m*(m-1)+n ...