总结
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的14套腾讯、字节跳动、阿里、百度等2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
当使用线程和Handler组合实现异步处理时,当每次执行耗时操作都创建一条新线程进行处理,性能开销会比较大。为了提高性能我们使用AsyncTask实现异步处理(其实也是线程和handler组合实现),因为其内部使用了java提供的线程池技术,有效的降低了线程创建数量及限定了同时运行的线程数,还有一些针对性的对池的优化操作。所以说AsyncTask是Android为我们提供的方便编写异步任务的工具类。
【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】
2 实例演示
先看下使用AsyncTask模拟下载的效果图:
看下代码,如下:
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
new TestAsyncTask(this).execute();
}
static final class TestAsyncTask extends AsyncTask<Void, Integer, Boolean> {
//如上三个泛型参数从左到右含义依次为:
//1. 在执行AsyncTask时需要传入的参数,可用于在后台任务中使用。
//2. 后台任务执行时,如果需要在界面上显示当前的进度,则使用这个。
//3. 当任务执行完毕后,如果需要对结果进行返回,则使用这个。
private Context mContext = null;
private ProgressDialog mDialog = null;
private int mCount = 0;
public TestAsyncTask(Context context) {
mContext = context;
}
//在后台任务开始执行之间调用,用于进行一些界面上的初始化操作
protected void onPreExecute() {
super.onPreExecute();
mDialog = new ProgressDialog(mContext);
mDialog.setMax(100);
mDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mDialog.show();
}
//这个方法中的所有代码都会在子线程中运行,我们应该在这里去处理所有的耗时任务
protected Boolean doInBackground(Void… params) {
while (mCount < 100) {
publishProgress(mCount);
mCount += 20;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
return true;
}
//当在后台任务中调用了publishProgress(Progress…)方法后,这个方法就很快会被调用
protected void onProgressUpdate(Integer… values) {
super.onProgressUpdate(values);
mDialog.setProgress(values[0]);
}
//当后台任务执行完毕并通过return语句进行返回时,这个方法就很快会被调用
protected void onPostExecute(Boolean aBoolean) {
super.onPostExecute(aBoolean);
if (aBoolean && mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
}
}
}
}
可以看见Android帮我们封装好的AsyncTask还是很方便使用的,咱们不做过多说明。接下来直接分析源码。
【工匠若水 http://blog.csdn.net/yanbober 转载烦请注明出处,尊重分享成果】
3 Android5.1.1(API 22)AsyncTask源码分析
通过源码可以发现AsyncTask是一个抽象类,所以我们在在上面使用时需要实现它。
那怎么下手分析呢?很简单,我们就依据上面示例的流程来分析源码,具体如下。
3-1 AsyncTask实例化源码分析
/**
- Creates a new asynchronous task. This constructor must be invoked on the UI thread.
*/
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));
}
};
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);
}
}
};
}
看见注释没有,AsyncTask的实例化只能在UI线程中。然后整个构造函数就只初始化了两个AsyncTask类的成员变量(mWorker和mFuture)。mWorker为匿名内部类的实例对象WorkerRunnable(实现了Callable接口),mFuture为匿名内部类的实例对象FutureTask,传入了mWorker作为形参(重写了FutureTask类的done方法)。
3-2 AsyncTask的execute方法源码分析
正如上面实例一样,得到AsyncTask实例化对象之后就执行了execute方法,所以看下execute方法的源码,如下:
public final AsyncTask<Params, Progress, Result> execute(Params… params) {
return executeOnExecutor(sDefaultExecutor, params);
}
可以看见,execute调运了executeOnExecutor方法,executeOnExecutor方法除过传入了params形参以外,还传入了一个static的SerialExecutor对象(SerialExecutor实现了Executor接口)。继续看下executeOnExecutor源码,如下:
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;
}
首先判断AsyncTask异步任务的状态,当处于RUNNING和FINISHED时就报IllegalStateException非法状态异常。由此可以看见一个AsyncTask的execute方法只能被调运一次。接着看见17行onPreExecute();没有?看下这个方法源码,如下:
/**
-
Runs on the UI thread before {@link #doInBackground}.
-
@see #onPostExecute
-
@see #doInBackground
*/
protected void onPreExecute() {
}
空方法,而且通过注释也能看见,这不就是我们AsyncTask中第一个执行的方法吗?是的。
回过头继续往下看,看见20行exec.execute(mFuture);代码没?exec就是形参出入的上面定义的static SerialExecutor对象(SerialExecutor实现了Executor接口),所以execute就是SerialExecutor静态内部类的方法喽,在执行execute方法时还传入了AsyncTask构造函数中实例化的第二个成员变量mFuture。我们来看下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);
}
}
}
在源码中可以看见,SerialExecutor在AsyncTask中是以常量的形式被使用的,所以在整个应用程序中的所有AsyncTask实例都会共用同一个SerialExecutor对象。
接着可以看见,SerialExecutor是使用ArrayDeque这个队列来管理Runnable对象的,如果我们一次性启动了很多个任务,首先在第一次运行execute()方法的时候会调用ArrayDeque的offer()方法将传入的Runnable对象添加到队列的最后,然后判断mActive对象是不是等于null,第一次运行是null,然后调用scheduleNext()方法,在这个方法中会从队列的头部取值,并赋值给mActive对象,然后调用THREAD_POOL_EXECUTOR去执行取出的取出的Runnable对象。之后如果再有新的任务被执行时就等待上一个任务执行完毕后才会得到执行,所以说同一时刻只会有一个线程正在执行,其余的均处于等待状态,这就是SerialExecutor类的核心作用。
我们再来看看上面用到的THREAD_POOL_EXECUTOR与execute,如下:
public abstract class AsyncTask<Params, Progress, Result> {
…
private static final int CPU_COUNT = Runtime.getRuntime().availableProcessors();
private static final int CORE_POOL_SIZE = CPU_COUNT + 1;
private static final int MAXIMUM_POOL_SIZE = CPU_COUNT * 2 + 1;
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(128);
/**
- An {@link Executor} that can be used to execute tasks in parallel.
*/
public static final Executor THREAD_POOL_EXECUTOR
= new ThreadPoolExecutor(CORE_POOL_SIZE, MAXIMUM_POOL_SIZE, KEEP_ALIVE,
TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory);
…
}
看见没有,实质就是在一个线程池中执行,这个THREAD_POOL_EXECUTOR线程池是一个常量,也就是说整个App中不论有多少AsyncTask都只有这一个线程池。也就是说上面SerialExecutor类中execute()方法的所有逻辑就是在子线程中执行,注意SerialExecutor的execute方法有一个Runnable参数,这个参数就是mFuture对象,所以我们看下FutureTask类的run()方法,如下源码:
最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。
Android高级架构师之路很漫长,一起共勉吧!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
多少AsyncTask都只有这一个线程池。也就是说上面SerialExecutor类中execute()方法的所有逻辑就是在子线程中执行,注意SerialExecutor的execute方法有一个Runnable参数,这个参数就是mFuture对象,所以我们看下FutureTask类的run()方法,如下源码:
最后我还整理了很多Android中高级的PDF技术文档。以及一些大厂面试真题解析文档。
[外链图片转存中…(img-xHVklX9w-1715708290729)]
Android高级架构师之路很漫长,一起共勉吧!
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!