今天向大家介绍一个很有用的异步任务类处理类,分别包含了AsyncTask各个环节中的异常处理、大量并发执行而不发生异常、字符串数据缓存等功能。并且感谢@马天宇(http://litesuits.com/)给我的思路与指点。

创新互联公司为客户提供专业的成都做网站、成都网站制作、程序、域名、空间一条龙服务,提供基于WEB的系统开发. 服务项目涵盖了网页设计、网站程序开发、WEB系统开发、微信二次开发、成都手机网站制作等网站方面业务。
研究过Android系统源码的同学会发现:AsyncTask在android2.3的时候线程池是一个核心数为5线程,队列可容纳10线程,***执行128个任务,这存在一个问题,当你真的有138个并发时,即使手机没被你撑爆,那么超出这个指标应用绝对crash掉。 后来升级到3.0,为了避免并发带来的一些列问题,AsyncTask竟然成为序列执行器了,也就是你即使你同时execute N个AsyncTask,它也是挨个排队执行的。 这一点请同学们一定注意,AsyncTask在3.0以后,是异步的没错,但不是并发的。关于这一点的改进办法,我之前写过一篇《Thread并发请求封装——深入理解AsyncTask类》没有看过的同学可以看这里,本文是在这个基础上对AsyncTask做进一步的优化。
根据Android4.0源码我们可以看到,在AsyncTask中默认有两个执行器,ThreadPoolExecutor和SerialExecutor,分别表示并行执行器和串行执行器。但是默认的并行执行器并不能执行大于128个任务的处理,所以我们在此定义一个根据lru调度策略的并行执行器。源码可以看这里。
- /**
 - * 用于替换掉原生的mThreadPoolExecutor,可以大大改善Android自带异步任务框架的处理能力和速度。
 - * 默认使用LIFO(后进先出)策略来调度线程,可将***的任务快速执行,当然你自己可以换为FIFO调度策略。
 - * 这有助于用户当前任务优先完成(比如加载图片时,很容易做到当前屏幕上的图片优先加载)。
 - */
 - private static class SmartSerialExecutor implements Executor {
 - /**
 - * 这里使用{@link ArrayDequeCompat}作为栈比{@link Stack}性能高
 - */
 - private ArrayDequeCompat
 mQueue = new ArrayDequeCompat ( - serialMaxCount);
 - private ScheduleStrategy mStrategy = ScheduleStrategy.LIFO;
 - private enum ScheduleStrategy {
 - LIFO, FIFO;
 - }
 - /**
 - * 一次同时并发的数量,根据处理器数量调节
 
- * cpu count : 1 2 3 4 8 16 32
 
- * once(base*2): 1 2 3 4 8 16 32
 
- * 一个时间段内最多并发线程个数: 双核手机:2 四核手机:4 ... 计算公式如下:
 - */
 - private static int serialOneTime;
 - /**
 - * 并发***数量,当投入的任务过多大于此值时,根据Lru规则,将最老的任务移除(将得不到执行)
 
- * cpu count : 1 2 3 4 8 16 32
 
- * base(cpu+3) : 4 5 6 7 11 19 35
 
- * max(base*16): 64 80 96 112 176 304 560
 
- */
 - private static int serialMaxCount;
 - private void reSettings(int cpuCount) {
 - serialOneTime = cpuCount;
 - serialMaxCount = (cpuCount + 3) * 16;
 - }
 - public SmartSerialExecutor() {
 - reSettings(CPU_COUNT);
 - }
 - @Override
 - public synchronized void execute(final Runnable command) {
 - Runnable r = new Runnable() {
 - @Override
 - public void run() {
 - command.run();
 - next();
 - }
 - };
 - if ((mThreadPoolExecutor).getActiveCount() < serialOneTime) {
 - // 小于单次并发量直接运行
 - mThreadPoolExecutor.execute(r);
 - } else {
 - // 如果大于并发上限,那么移除最老的任务
 - if (mQueue.size() >= serialMaxCount) {
 - mQueue.pollFirst();
 - }
 - // 新任务放在队尾
 - mQueue.offerLast(r);
 - }
 - }
 - public synchronized void next() {
 - Runnable mActive;
 - switch (mStrategy) {
 - case LIFO:
 - mActive = mQueue.pollLast();
 - break;
 - case FIFO:
 - mActive = mQueue.pollFirst();
 - break;
 - default:
 - mActive = mQueue.pollLast();
 - break;
 - }
 - if (mActive != null) {
 - mThreadPoolExecutor.execute(mActive);
 - }
 - }
 - }
 
以上便是对AsyncTask的并发执行优化,接下来我们看对异常捕获的改进。
真正说起来,这并不算是什么功能上的改进,仅仅是一种开发上的技巧。代码过长,我删去了一些,仅留下重要部分。
- /**
 - * 安全异步任务,可以捕获任意异常,并反馈给给开发者。
 
- * 从执行前,执行中,执行后,乃至更新时的异常都捕获。
 
- */
 - public abstract class SafeTask
 extends - KJTaskExecutor
 { - private Exception cause;
 - @Override
 - protected final void onPreExecute() {
 - try {
 - onPreExecuteSafely();
 - } catch (Exception e) {
 - exceptionLog(e);
 - }
 - }
 - @Override
 - protected final Result doInBackground(Params... params) {
 - try {
 - return doInBackgroundSafely(params);
 - } catch (Exception e) {
 - exceptionLog(e);
 - cause = e;
 - }
 - return null;
 - }
 - @Override
 - protected final void onProgressUpdate(Progress... values) {
 - try {
 - onProgressUpdateSafely(values);
 - } catch (Exception e) {
 - exceptionLog(e);
 - }
 - }
 - @Override
 - protected final void onPostExecute(Result result) {
 - try {
 - onPostExecuteSafely(result, cause);
 - } catch (Exception e) {
 - exceptionLog(e);
 - }
 - }
 - @Override
 - protected final void onCancelled(Result result) {
 - onCancelled(result);
 - }
 - }
 
其实从代码就可以看出,仅仅是对原AsyncTask类中各个阶段的代码做了一次try..catch... 但就是这一个小优化,不仅可以使代码整齐(我觉得try...catch太多真的很影响代码美观),而且在最终都可以由一个onPostExecuteSafely(xxx)来整合处理,使得结构更加紧凑。
让AsyncTask附带数据缓存功能
我们在做APP开发的时候,网络访问都会加上缓存处理,其中的原因我想就不必讲了。那么如果让AsyncTask自身就附带网络JSON缓存,岂不是更好?其实实现原理很简单,就是将平时我们写在外面的缓存方法放到AsyncTask内部去实现,注释已经讲解的很清楚了,这里就不再讲了
- /**
 - * 本类主要用于获取网络数据,并将结果缓存至文件,文件名为key,缓存有效时间为value
 
- * 注:{@link #CachedTask#Result}需要序列化,否则不能或者不能完整的读取缓存。
 
- */
 - public abstract class CachedTask
 - extends SafeTask
 { - private String cachePath = "folderName"; // 缓存路径
 - private String cacheName = "MD5_effectiveTime"; // 缓存文件名格式
 - private long expiredTime = 0; // 缓存时间
 - private String key; // 缓存以键值对形式存在
 - private ConcurrentHashMap
 cacheMap; - /**
 - * 构造方法
 - * @param cachePath 缓存路径
 - * @param key 存储的key值,若重复将覆盖
 - * @param cacheTime 缓存有效期,单位:分
 - */
 - public CachedTask(String cachePath, String key, long cacheTime) {
 - if (StringUtils.isEmpty(cachePath)
 - || StringUtils.isEmpty(key)) {
 - throw new RuntimeException("cachePath or key is empty");
 - } else {
 - this.cachePath = cachePath;
 - // 对外url,对内url的md5值(不仅可以防止由于url过长造成文件名错误,还能防止恶意修改缓存内容)
 - this.key = CipherUtils.md5(key);
 - // 对外单位:分,对内单位:毫秒
 - this.expiredTime = TimeUnit.MILLISECONDS.convert(
 - cacheTime, TimeUnit.MINUTES);
 - this.cacheName = this.key + "_" + cacheTime;
 - initCacheMap();
 - }
 - }
 - private void initCacheMap() {
 - cacheMap = new ConcurrentHashMap
 (); - File folder = FileUtils.getSaveFolder(cachePath);
 - for (String name : folder.list()) {
 - if (!StringUtils.isEmpty(name)) {
 - String[] nameFormat = name.split("_");
 - // 若满足命名格式则认为是一个合格的cache
 - if (nameFormat.length == 2 && (nameFormat[0].length() == 32 || nameFormat[0].length() == 64 || nameFormat[0].length() == 128)) {
 - cacheMap.put(nameFormat[0], TimeUnit.MILLISECONDS.convert(StringUtils.toLong(nameFormat[1]), TimeUnit.MINUTES));
 - }
 - }
 - }
 - }
 - /**
 - * 做联网操作,本方法运行在线程中
 - */
 - protected abstract Result doConnectNetwork(Params... params)
 - throws Exception;
 - /**
 - * 做耗时操作
 - */
 - @Override
 - protected final Result doInBackgroundSafely(Params... params)
 - throws Exception {
 - Result res = null;
 - Long time = cacheMap.get(key);
 - long lastTime = (time == null) ? 0 : time; // 获取缓存有效时间
 - long currentTime = System.currentTimeMillis(); // 获取当前时间
 - if (currentTime >= lastTime + expiredTime) { // 若缓存无效,联网下载
 - res = doConnectNetwork(params);
 - if (res == null)
 - res = getResultFromCache();
 - else
 - saveCache(res);
 - } else { // 缓存有效,使用缓存
 - res = getResultFromCache();
 - if (res == null) { // 若缓存数据意外丢失,重新下载
 - res = doConnectNetwork(params);
 - saveCache(res);
 - }
 - }
 - return res;
 - }
 - private Result getResultFromCache() {
 - Result res = null;
 - ObjectInputStream ois = null;
 - try {
 - ois = new ObjectInputStream(new FileInputStream(
 - FileUtils.getSaveFile(cachePath, key)));
 - res = (Result) ois.readObject();
 - } catch (Exception e) {
 - e.printStackTrace();
 - } finally {
 - FileUtils.closeIO(ois);
 - }
 - return res;
 - }
 - /**
 - * 保存数据,并返回是否成功
 - */
 - private boolean saveResultToCache(Result res) {
 - boolean saveSuccess = false;
 - ObjectOutputStream oos = null;
 - try {
 - oos = new ObjectOutputStream(new FileOutputStream(
 - FileUtils.getSaveFile(cachePath, key)));
 - oos.writeObject(res);
 - saveSuccess = true;
 - } catch (Exception e) {
 - e.printStackTrace();
 - } finally {
 - FileUtils.closeIO(oos);
 - }
 - return saveSuccess;
 - }
 - /**
 - * 清空缓存文件(异步)
 - */
 - public void cleanCacheFiles() {
 - cacheMap.clear();
 - File file = FileUtils.getSaveFolder(cachePath);
 - final File[] fileList = file.listFiles();
 - if (fileList != null) {
 - // 异步删除全部文件
 - TaskExecutor.start(new Runnable() {
 - @Override
 - public void run() {
 - for (File f : fileList) {
 - if (f.isFile()) {
 - f.delete();
 - }
 - }
 - }// end run()
 - });
 - }// end if
 - }
 - /**
 - * 移除一个缓存
 - */
 - public void remove(String key) {
 - // 对内是url的MD5
 - String realKey = CipherUtils.md5(key);
 - for (Map.Entry
 entry : cacheMap.entrySet()) { - if (entry.getKey().startsWith(realKey)) {
 - cacheMap.remove(realKey);
 - return;
 - }
 - }
 - }
 - /**
 - * 如果缓存是有效的,就保存
 - * @param res 将要缓存的数据
 - */
 - private void saveCache(Result res) {
 - if (res != null) {
 - saveResultToCache(res);
 - cacheMap.put(cacheName, System.currentTimeMillis());
 - }
 - }
 - }
 
                网站栏目:一套完善的Android异步任务类
                
                分享地址:http://www.csdahua.cn/qtweb/news32/32.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网