private static final String TAG = “MainActivity”;
private ProgressDialog mDialog;
private TextView mTextView;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextView = (TextView) findViewById(R.id.id_tv);
mDialog = new ProgressDialog(this);
mDialog.setMax(100);
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mDialog.setCancelable(false);
new MyAsyncTask().execute();
}
private class MyAsyncTask extends AsyncTask<Void, Integer, Void>
{
@Override
protected void onPreExecute()
{
mDialog.show();
Log.e(TAG, Thread.currentThread().getName() + " onPreExecute ");
}
@Override
protected Void doInBackground(Void… params)
{
// 模拟数据的加载,耗时的任务
for (int i = 0; i < 100; i++)
{
try
{
Thread.sleep(80);
} catch (InterruptedException e)
{
e.printStackTrace();
}
publishProgress(i);
}
Log.e(TAG, Thread.currentThread().getName() + " doInBackground ");
return null;
}
@Override
protected void onProgressUpdate(Integer… values)
{
mDialog.setProgress(values[0]);
Log.e(TAG, Thread.currentThread().getName() + " onProgressUpdate ");
}
@Override
protected void onPostExecute(Void result)
{
// 进行数据加载完成后的UI操作
mDialog.dismiss();
mTextView.setText("LOAD DATA SUCCESS ");
Log.e(TAG, Thread.currentThread().getName() + " onPostExecute ");
}
}
}
进入某个Activity,Activity中需要的数据来自于网络或者其它耗时操作,可以在AsyncTask中onPreExecute完成一些准备操作,比如上例中显示进度对话框;然后在doInBackground完成耗时操作,在进行耗时操作时还能不时的通过publishProgress给onProgressUpdate中传递参数,然后在onProgressUpdate中可以进行UI操作,比如上例更新进度条的进度;当耗时任务执行完成后,最后在onPostExecute进行设置控件数据更新UI等操作,例如隐藏进度对话框。
效果图:
3、源码解析
注:本篇源码分析基于Andorid-17,因为和3.0之前版本变动较大,有必要标出。
那么大家一定好奇,AsyncTask在Android中是如何实现的,下面进行源码分析:从我们的执行异步任务的起点开始,进入execute方法:
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;
}
18行:设置当前AsyncTask的状态为RUNNING,上面的switch也可以看出,每个异步任务在完成前只能执行一次。
20行:执行了onPreExecute(),当前依然在UI线程,所以我们可以在其中做一些准备工作。
22行:将我们传入的参数赋值给了mWorker.mParams
23行:exec.execute(mFuture)
相信大家对22行出现的mWorker,以及23行出现的mFuture都会有些困惑。
mWorker找到这个类:
private static abstract class WorkerRunnable<Params, Result> implements Callable {
Params[] mParams;
}
可以看到是Callable的子类,且包含一个mParams用于保存我们传入的参数,下面看初始化mWorker的代码:
public AsyncTask() {
mWorker = new WorkerRunnable<Params, Result>() {
public Result call() throws Exception {
mTaskInvoked.set(true);
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
//noinspection unchecked
return postResult(doInBackground(mParams));
}
};
//….
}
可以看到mWorker在构造方法中完成了初始化,并且因为是一个抽象类,在这里new了一个实现类,实现了call方法,call方法中设置mTaskInvoked=true,且最终调用doInBackground(mParams)方法,并返回Result值作为参数给postResult方法.可以看到我们的doInBackground出现了,下面继续看:
private Result postResult(Result result) {
@SuppressWarnings(“unchecked”)
Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT,
new AsyncTaskResult(this, result));
message.sendToTarget();
return result;
}
可以看到postResult中出现了我们熟悉的异步消息机制,传递了一个消息message, message.what为MESSAGE_POST_RESULT;message.object= new AsyncTaskResult(this,result);
private static class AsyncTaskResult {
final AsyncTask mTask;
final Data[] mData;
AsyncTaskResult(AsyncTask task, Data… data) {
mTask = task;
mData = data;
}
}
AsyncTaskResult就是一个简单的携带参数的对象。
看到这,我相信大家肯定会想到,在某处肯定存在一个sHandler,且复写了其handleMessage方法等待消息的传入,以及消息的处理。
private static final InternalHandler sHandler = new InternalHandler();
private static class InternalHandler extends Handler {
@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;
}
}
}
哈哈,出现了我们的handleMessage,可以看到,在接收到MESSAGE_POST_RESULT消息时,执行了result.mTask.finish(result.mData[0]);其实就是我们的AsyncTask.this.finish(result),于是看finish方法
private void finish(Result result) {
if (isCancelled()) {
onCancelled(result);
} else {
onPostExecute(result);
}
mStatus = Status.FINISHED;
}
可以看到,如果我们调用了cancel()则执行onCancelled回调;正常执行的情况下调用我们的onPostExecute(result);主要这里的调用是在handler的handleMessage中,所以是在UI线程中。如果你对异步消息机制不理解请看: Android 异步消息处理机制 让你深入理解 Looper、Handler、Message三者关系
最后将状态置为FINISHED。
mWoker看完了,应该到我们的mFuture了,依然实在构造方法中完成mFuture的初始化,将mWorker作为参数,复写了其done方法。
public AsyncTask() {
…
mFuture = new FutureTask(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 occured while executing doInBackground()”,
e.getCause());
} catch (CancellationException e) {
postResultIfNotInvoked(null);
}
}
};
}
16行:任务执行结束会调用:postResultIfNotInvoked(get());get()表示获取mWorker的call的返回值,即Result.然后看postResultIfNotInvoked方法
private void postResultIfNotInvoked(Result result) {
final boolean wasTaskInvoked = mTaskInvoked.get();
if (!wasTaskInvoked) {
postResult(result);
}
}
如果mTaskInvoked不为true,则执行postResult;但是在mWorker初始化时就已经将mTaskInvoked为true,所以一般这个postResult执行不到。
好了,到了这里,已经介绍完了execute方法中出现了mWorker和mFurture,不过这里一直是初始化这两个对象的代码,并没有真正的执行。下面我们看真正调用执行的地方。
execute方法中的:
还记得上面的execute中的23行:exec.execute(mFuture)
exec为executeOnExecutor(sDefaultExecutor, params)中的sDefaultExecutor
下面看这个sDefaultExecutor
private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;
public static final Executor SERIAL_EXECUTOR = new SerialExecutor();
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
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);
}
}
}
可以看到sDefaultExecutor其实为SerialExecutor的一个实例,其内部维持一个任务队列;直接看其execute(Runnable runnable)方法,将runnable放入mTasks队尾;
16-17行:判断当前mActive是否为空,为空则调用scheduleNext方法
20行:scheduleNext,则直接取出任务队列中的队首任务,如果不为null则传入THREAD_POOL_EXECUTOR进行执行。
下面看THREAD_POOL_EXECUTOR为何方神圣:
public static final Executor THREAD_POOL_EXECUTOR
=new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
可以看到就是一个自己设置参数的线程池,参数为:
private static final int CORE_POOL_SIZE = 5;
private static final int MAXIMUM_POOL_SIZE = 128;
private static final int KEEP_ALIVE = 1;
private static final ThreadFactory sThreadFactory = new ThreadFactory() {
private final AtomicInteger mCount = new AtomicInteger(1);
public Thread newThread(Runnable r) {
return new Thread(r, “AsyncTask #” + mCount.getAndIncrement());
}
};
private static final BlockingQueue sPoolWorkQueue =
new LinkedBlockingQueue(10);
看到这里,大家可能会认为,背后原来有一个线程池,且最大支持128的线程并发,加上长度为10的阻塞队列,可能会觉得就是在快速调用138个以内的AsyncTask子类的execute方法不会出现问题,而大于138则会抛出异常。
其实不是这样的,我们再仔细看一下代码,回顾一下sDefaultExecutor,真正在execute()中调用的为sDefaultExecutor.execute:
private static class SerialExecutor implements Executor {
final ArrayDeque mTasks = new ArrayDeque();
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);
}
}
}
可以看到,如果此时有10个任务同时调用execute(s synchronized)方法,第一个任务入队,然后在mActive = mTasks.poll()) != null被取出,并且赋值给mActivte,然后交给线程池去执行。然后第二个任务入队,但是此时mActive并不为null,并不会执行scheduleNext();所以如果第一个任务比较慢,10个任务都会进入队列等待;真正执行下一个任务的时机是,线程池执行完成第一个任务以后,调用Runnable中的finally代码块中的scheduleNext,所以虽然内部有一个线程池,其实调用的过程还是线性的。一个接着一个的执行,相当于单线程。
4、总结
到此源码解释完毕,由于代码跨度比较大,我们再回顾一下:
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;
}
18行:设置当前AsyncTask的状态为RUNNING,上面的switch也可以看出,每个异步任务在完成前只能执行一次。
20行:执行了onPreExecute(),当前依然在UI线程,所以我们可以在其中做一些准备工作。
22行:将我们传入的参数赋值给了mWorker.mParams ,mWorker为一个Callable的子类,且在内部的call()方法中,调用了doInBackground(mParams),然后得到的返回值作为postResult的参数进行执行;postResult中通过sHandler发送消息,最终sHandler的handleMessage中完成onPostExecute的调用。
23行:exec.execute(mFuture),mFuture为真正的执行任务的单元,将mWorker进行封装,然后由sDefaultExecutor交给线程池进行执行。
5、publishProgress
说了这么多,我们好像忘了一个方法:publishProgress
protected final void publishProgress(Progress… values) {
if (!isCancelled()) {
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则近万的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
如果你觉得这些内容对你有帮助,可以扫码获取!!(备注:Android)
![](https://i-blog.csdnimg.cn/blog_migrate/5dca330c10975ce75f188a030a43d3dd.jpeg)
面试复习路线,梳理知识,提升储备
自己的知识准备得怎么样,这直接决定了你能否顺利通过一面和二面,所以在面试前来一个知识梳理,看需不需要提升自己的知识储备是很有必要的。
关于知识梳理,这里再分享一下我面试这段时间的复习路线:(以下体系的复习资料是我从各路大佬收集整理好的)
- 架构师筑基必备技能
- Android高级UI与FrameWork源码
- 360°全方面性能调优
- 解读开源框架设计思想
- NDK模块开发
- 微信小程序
- Hybrid 开发与Flutter
知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:
《960全网最全Android开发笔记》
《379页Android开发面试宝典》
历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
如何使用它?
1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数
《507页Android开发相关源码解析》
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!
rk源码**
- 360°全方面性能调优
- 解读开源框架设计思想
- NDK模块开发
- 微信小程序
- Hybrid 开发与Flutter
[外链图片转存中…(img-iOnusNM1-1712608221272)]
知识梳理完之后,就需要进行查漏补缺,所以针对这些知识点,我手头上也准备了不少的电子书和笔记,这些笔记将各个知识点进行了完美的总结:
[外链图片转存中…(img-t4a6rgWS-1712608221272)]
《960全网最全Android开发笔记》
[外链图片转存中…(img-HjTtre2w-1712608221272)]
《379页Android开发面试宝典》
历时半年,我们整理了这份市面上最全面的安卓面试题解析大全
包含了腾讯、百度、小米、阿里、乐视、美团、58、猎豹、360、新浪、搜狐等一线互联网公司面试被问到的题目。熟悉本文中列出的知识点会大大增加通过前两轮技术面试的几率。
如何使用它?
1.可以通过目录索引直接翻看需要的知识点,查漏补缺。
2.五角星数表示面试问到的频率,代表重要推荐指数
[外链图片转存中…(img-qFrsMMvD-1712608221272)]
《507页Android开发相关源码解析》
只要是程序员,不管是Java还是Android,如果不去阅读源码,只看API文档,那就只是停留于皮毛,这对我们知识体系的建立和完备以及实战技术的提升都是不利的。
真正最能锻炼能力的便是直接去阅读源码,不仅限于阅读各大系统源码,还包括各种优秀的开源库。
《互联网大厂面试真题解析、进阶开发核心学习笔记、全套讲解视频、实战项目源码讲义》点击传送门即可获取!