再谈Android AsyncTask的优缺点
导语:之前做习惯了Framework层的开发,今天在武汉斗鱼公司面试APP客户端的开发,其中一道题是讲述Asynctask的优缺点,我靠,我只是知道有这么一个东西,会用而已,看来之前的生活太过于安逸,这么基础的东西都不会,特意回家总结下。
转载请注明原文链接:http://www.cnblogs.com/yanyojun/p/6414919.html
1、Asynctask简介
1.1 使用方法简介
Asynctask作为Android的基础之一,怎么使用就不多讲解了,网上到处都是教程,建议查看Android官方API文档:https://developer.android.google.cn/reference/android/os/AsyncTask.html
这里只实现一个小Demo程序,供大家赏玩:
界面:
这个程序其实特别简单,就是两个按钮,点击分别用来测试AysncTask和Handler两种模式的实现,点击后会有相应的Log提示。
功能简介:
Asynctask的实现:
- private class IAsyncTask extends AsyncTask<String, Integer, String> {
- protected String doInBackground(String... args1) {
- Log.i(TAG, "doInBackground in:" + args1[0]);
- int times = 10;
- for (int i = 0; i < times; i++) {
- publishProgress(i);//提交之后,会执行onProcessUpdate方法
- }
- Log.i(TAG, "doInBackground out");
- return "over";
- }
- /**
- * 在调用cancel方法后会执行到这里
- */
- protected void onCancelled() {
- Log.i(TAG, "onCancelled");
- }
- /**
- * 在doInbackground之后执行
- */
- protected void onPostExecute(String args3) {
- Log.i(TAG, "onPostExecute:" + args3);
- }
- /**
- * 在doInBackground之前执行
- */
- @Override
- protected void onPreExecute() {
- Log.i(TAG, "onPreExecute");
- }
- /**
- * 特别赞一下这个多次参数的方法,特别方便
- * @param args2
- */
- @Override
- protected void onProgressUpdate(Integer... args2) {
- Log.i(TAG, "onProgressUpdate:" + args2[0]);
- }
- }
点击第一个按钮后会执行这里,点击按钮的写法如下:
- mBtnSyncTask.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- new IAsyncTask().execute("yanlog test");
- }
- });
执行结果的Log如下:
- 02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in:yanlog test//doInbackground是在10824进程,11010线程中执行
- 02-19 21:55:12.179 10824-11010/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground out
- 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:0//剩下的都是在10824线程中执行,Android特别好的是,主线程的线程号跟进程号是一致的
- 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:1
- 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:2
- 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:3
- 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:4
- 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:5
- 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:6
- 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:7
- 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:8
- 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onProgressUpdate:9
- 02-19 21:55:12.184 10824-10824/com.plbear.asynctasktest I/AsyncTaskTest: onPostExecute:over
Handler+Message实现:
主要代码如下:
- private class IHandler extends Handler{
- @Override
- public void handleMessage(Message msg){
- switch(msg.what){
- case 1:
- Log.e(TAG,"handler:"+msg.obj);
- break;
- default:
- break;
- }
- }
- }
其中,调用地方如下:
- mBtnHandlerTest.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- final Handler handler = new IHandler();
- new Thread(new Runnable() {
- @Override
- public void run() {
- for (int i = 0; i < 10; i++) {
- Message msg = new Message();
- msg.what = 1;
- msg.obj = new Integer(i);
- Log.e(TAG, "post message:" + i);
- handler.sendMessage(msg);
- }
- }
- }).start();
- }
- });
可以看到Log打印结果如下:
- 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:0 //可以看到提交是在9319号子进程中提交
- 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:1
- 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:2
- 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:3
- 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:4
- 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:5
- 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:6
- 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:7
- 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:8
- 02-19 22:25:17.689 9234-9319/com.plbear.asynctasktest E/AsyncTaskTest: post message:9
- 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:0 //可以看到提交完是在9234主线程中执行。
- 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:1
- 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:2
- 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:3
- 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:4
- 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:5
- 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:6
- 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:7
- 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:8
- 02-19 22:25:17.692 9234-9234/com.plbear.asynctasktest E/AsyncTaskTest: handler:9
以上,简单梳理了下怎么实现,不赘言。
1.2 Android 内部源码实现
关于Handler+Message+Message Queue+Looper的实现就不介绍了,老生常谈了。所以下面主要看一下AsyncTask的源码实现:
AsyncTask的核心方法应该是
- public final AsyncTask<Params, Progress, Result> execute(Params... params)
那我们就看下当调用了execute方法后,都发生了什么,下面是执行的序列图。
我知道我画的不够标准了,凑合着看吧。下面关于这个图的一些说明。
- 在第4步,execute的时候,这个时候可以看到,doInBackground已经转到子线程中执行了,这个是很关键的一个点,我特意用了一个异步处理的箭头标注了。
- 在第9步,当doInbackground执行完,执行到finish方法的时候,由通过sendMessage的方法回到了主线程中了,所以后面的onPostExecute和onCanceled都是在主线程中执行的。
嗯,就这么多吧。关于AsyncTask的源码我上传到github中了,大家对照着源码看会更清楚一点。
https://github.com/YanYoJun/AndroidSource/blob/master/AsyncTask.java
关于AsyncTask的源码分析,还有一篇博客写的很好,请参看:
http://www.infocool.net/kb/OtherMobile/201610/197431.html
备注:
源码里面还有三个地方值得深究下,分别是:
- FutureTask值得看下,回头写了博客我把链接贴在这里
- AsyncTask中的SerialExecutor类写的太漂亮了,回头单独写一个博客欣赏下。
- 关于上面的ThreadPollExecutor我其实没有研究。。。回头写个博客研究下。
2、优点
简单,快捷
这个说法就是近乎于扯淡吧,主要还是看使用习惯,我就挺喜欢用Handler的。
但是Android定义了这个东西,可以看到各种消息封装的还是很不错的,很规范。大家可以按照这个“优美的框架”来写,代码不会太出格。
3、缺点
3.1 AsyncTask实际上后台线程之后一个!!!
今天仔细研究了下源码,发现网上写的大部分是错的,AsyncTask的真正的后台线程只有一个!!不信,看下面的代码:
我们首先定义一个IAsyncTAsk,其中的doInBackground方法这么写:
- private class IAsyncTask extends AsyncTask<String, Integer, String> {
- protected String doInBackground(String... args1) {
- /* Log.i(TAG, "doInBackground in:" + args1[0]);
- int times = 10;
- for (int i = 0; i < times; i++) {
- publishProgress(i);//提交之后,会执行onProcessUpdate方法
- }
- Log.i(TAG, "doInBackground out");*/
- Log.i(TAG, "doInBackground in thread:" + args1[0]);
- try {
- int times = 4;
- for (int i = 0; i < times; i++) {
- Log.i(TAG, "thread alive:" + i + " for times"+args1[0]); //这个doInBackground就打印一个Log,然后sleep 20 毫秒
- Thread.sleep(20);
- }
- } catch (Exception e) {
- }
- return "over";
- }
调用的地方这么写:
- int N = 5;
- for (int i = 0; i < N; i++) {
- Log.d(TAG,"asyncTask post Task:"+i);
- new IAsyncTask().execute("asyncTask times:"+i); //点击Button后,在onClick方法中建立5个后台子线程。
- }
我们来看打印结果:
- 02-20 21:48:08.206 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:0 //在主线程中进行提交操作
- 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:1
- 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:2
- 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:3
- 02-20 21:48:08.211 14812-14812/com.plbear.asynctasktest D/AsyncTaskTest: asyncTask post Task:4
- 02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:0 //可以看到,虽然系统开起了18067、18068、
- 02-20 21:48:08.212 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:0//18069,18070这几个子线程,但是这几个子线程
- 02-20 21:48:08.232 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:0 //是串行执行的!!!震惊了有没有!!!
- 02-20 21:48:08.253 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:0 //这不是巧合,试了好几次都是这样!!
- 02-20 21:48:08.273 14812-18067/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:0
- 02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:1
- 02-20 21:48:08.294 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:1
- 02-20 21:48:08.315 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:1
- 02-20 21:48:08.335 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:1
- 02-20 21:48:08.356 14812-18068/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:1
- 02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:2
- 02-20 21:48:08.377 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:2
- 02-20 21:48:08.397 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:2
- 02-20 21:48:08.417 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:2
- 02-20 21:48:08.438 14812-18069/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:2
- 02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:3
- 02-20 21:48:08.462 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:3
- 02-20 21:48:08.483 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:3
- 02-20 21:48:08.504 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:3
- 02-20 21:48:08.524 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:3
- 02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: doInBackground in thread:asyncTask times:4
- 02-20 21:48:08.545 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:0 for timesasyncTask times:4
- 02-20 21:48:08.565 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:1 for timesasyncTask times:4
- 02-20 21:48:08.585 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:2 for timesasyncTask times:4
- 02-20 21:48:08.606 14812-18070/com.plbear.asynctasktest I/AsyncTaskTest: thread alive:3 for timesasyncTask times:4
你本来希望系统应该这么执行
但是实际上系统是这么执行的:
那么从源码看下为啥会这样吧。
AsyncTask中默认的Exector是这个private static volatile Executor sDefaultExecutor = SERIAL_EXECUTOR;看下SERIAL_EXECUTOR是这么定义的
- public static final Executor SERIAL_EXECUTOR = new SerialExecutor(); //这是一个串行处理的Executor
- .........................
- 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() { //先把要执行的子线程统一丢到mTasks队列中,这其中封装一遍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);
- }
- }
- }
呵呵,这下子明白了吧。
Google为什么要怎么实现我不得而知,估计有什么我没有明白的好处在里面吧。那么有没有办法规避呢?
可以看到上面有一个THREAD_POOL_EXECUTOR,这个也是一个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;
- }
可以看到这个是允许一定数量的子线程并行处理的。
其中参数是这么定义的
- 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;
按照一般理解,允许同时运行的CORE进程是4个,MAXIMUM_POOL_SIZE是17个。(注:这个数字是我用荣耀8手机跑出来的,其他手机可能会有不同)
而Android中的AsyncTask提供了一个方法:
- public static void setDefaultExecutor(Executor exec) {
- sDefaultExecutor = exec;
- }
所以规避方法是:通过这个方法可以设置默认的Exector,但是这个方法是hide的,也就是Google的隐藏方法,估计需要用一下反射来处理。我偷个懒,不去实现了。
4、总结
本来嘛,我只是想简单写一下AsyncTask的一些相关知识,Copy一下网上的内容,但是没有想到写到最后,发现网上的大部分东西是错的,或者没有抓到重点。看来以后还是要自己亲自看代码,纸上得来终觉浅,便知此事要躬行。
本文中用到的工程代码可以到我的github中查看,路径:https://github.com/YanYoJun/AsyncTaskTest
好了,写完了,大家看到之后觉得还可以的话,点个赞哈。
再谈Android AsyncTask的优缺点的更多相关文章
- android Asynctask的优缺点?是否能同一时候并发100+asynctask呢?
一 Asynctask的优缺点? AsyncTask,是android提供的轻量级的异步类,能够直接继承AsyncTask,在类中实现异步操作,并提供接口反馈当前异步运行的程度(能够通过接口实现UI ...
- Android Asynctask的优缺点
导语:之前做习惯了Framework层的开发,今天在武汉斗鱼公司面试APP客户端的开发,其中一道题是讲述Asynctask的优缺点,我靠,我只是知道有这么一个东西,会用而已,看来之前的生活太过于安逸, ...
- Android 7.1.1 又出幺蛾子了 —— 再谈 Android 上的 Wifi 连接
在之前的博客文章中,我写了点在 Android 6 系统中连接到指定名称的 Wifi 的体验.然而,在 Android 7 中,有一些东西又变化了.另外就是在那篇文章中我说要提供代码,结果拖到这篇文章 ...
- 再谈Android应用瘦身
Android应用apk安装包的大小,虽然对于现在WiFi普及情况而言消耗流量已经微乎其微,但是,对于一款好的应用,对于一款负责任的应用,当然是越小越好了. 引言: .应用越小,下载越快,也就意味着新 ...
- 【转】 Pro Android学习笔记(四三):Fragment(8):再谈Transaction和管理器
目录(?)[-] Transaction的一些操作 再谈FragmentManager 调用其他fragment的方法 唤起activity 唤起fragment和相互通信 一些其它 Transact ...
- 浅谈Android应用保护(一):Android应用逆向的基本方法
对于未进行保护的Android应用,有很多方法和思路对其进行逆向分析和攻击.使用一些基本的方法,就可以打破对应用安全非常重要的机密性和完整性,实现获取其内部代码.数据,修改其代码逻辑和机制等操作.这篇 ...
- android AsyncTask介绍(转)
android AsyncTask介绍 AsyncTask和Handler对比 1 ) AsyncTask实现的原理,和适用的优缺点 AsyncTask,是android提供的轻量级的异步类,可以直接 ...
- 安卓开发_浅谈Android动画(四)
Property动画 概念:属性动画,即通过改变对象属性的动画. 特点:属性动画真正改变了一个UI控件,包括其事件触发焦点的位置 一.重要的动画类及属性值: 1. ValueAnimator 基本属 ...
- 浅谈Android应用性能之内存
本文来自http://blog.csdn.net/liuxian13183/ ,引用必须注明出处! 文/ jaunty [博主导读]在Android开发中,不免会遇到许多OOM现象,一方面可能是由于开 ...
随机推荐
- TortoiseGit创建本地库并提交到远程服务器
前半部分参考网上的例子:http://www.showerlee.com/archives/1300,但会出现“Git did not exit cleanly (exit code 128)”错误 ...
- NHibernate错误:Could not compile the mapping document的解决
用动软代码生成器的NHibernate生成模板,生成一个“XML映射模板”,老是提示Could not compile the mapping document的问题. 各种的找,就是没找到问题. 后 ...
- 20个jQuery分页插件和教程
1.客户端的jQuery 分页插件jPages jPages 是一个客户端的分页插件,但提供很多特性例如自动翻页.键盘和滚动浏览,延迟显示以及完全可定制的导航面板. Read More Demo 2. ...
- 使用 Git 命令去管理项目的版本控制(二)
参考 上一篇 完成本篇博客,本篇为作者原创,仅供学习参考. 本篇博文在上一篇的基础上这里记录了我的一个小模拟练习.本篇作为自己的学习笔记,也意在方便其他人的学习使用,达到分享目的.下面主要是操作截图 ...
- Acyclic Organic Compounds
题意: 给一以1为根的字符树,给出每个节点的字符与权值,记 $diff_{x}$ 为从 $x$ 出发向下走,能走到多少不同的字符串,求问最大的$diff_{x} + c_{x}$,并求有多少个 $di ...
- org.apache.catalina.LifecycleException报错解决方法
严重: A child container failed during startjava.util.concurrent.ExecutionException: org.apache.catalin ...
- 3.1 HiveServer2.Beeline JDBC使用
https://cwiki.apache.org/confluence/display/Hive/HiveServer2+Clients 一.HiveServer2.Beeline 1.HiveSer ...
- tensorflow weight_variable going
# coding: utf-8 import tensorflow as tf from tensorflow.examples.tutorials.mnist import input_data d ...
- 简易DIV垂直居中阴影层笼罩JS实现
$(document).ready(init); function init() { var h = $(window).height(); var w = $(window).width(); /* ...
- C++里的强制类型转换符reinterpret_cast、static_cast 、dynamic_cast、const_cast 区别
C 风格(C-style)强制转型如下: (T) exdivssion // cast exdivssion to be of type T 函数风格(Function-style)强制转型使用这样的 ...