├── .gitignore ├── LICENSE.txt ├── README.md ├── jars └── lite-async-1.0.0.jar ├── library ├── AndroidManifest.xml ├── project.properties └── src │ └── com │ └── litesuits │ └── android │ └── async │ ├── ArrayDequeCompat.java │ ├── ArraysCompat.java │ ├── AsyncTask.java │ ├── CachedTask.java │ ├── Log.java │ ├── SafeTask.java │ ├── SimpleCachedTask.java │ ├── SimpleSafeTask.java │ ├── SimpleTask.java │ └── TaskExecutor.java └── sample ├── AndroidManifest.xml ├── project.properties ├── res ├── drawable-xxhdpi │ └── ic_launcher.png ├── drawable │ ├── ic_launcher.png │ └── selector_button.xml ├── layout │ └── act_list_btn.xml ├── values-zh │ └── strings.xml └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml └── src └── com └── litesuits └── android └── samples ├── BaseActivity.java └── LiteAsyncSamplesActivity.java /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Local configuration file (sdk path, etc) 17 | *.properties 18 | proguard-project.txt 19 | 20 | # Eclipse project files 21 | .classpath 22 | .project 23 | .settings 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Intellij project files 29 | *.iml 30 | *.ipr 31 | *.iws 32 | .idea/ 33 | 34 | .DS_Store 35 | 36 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | android-lite-async 2 | ================== 3 | 4 | An ameliorative, enhanced AsyncTask for Android. LiteAsync provides SimpleTask, SafeTask, CachedTask, etc, for rapid development. More convenient is, it has a TaskExecutor which can executes ordered, cyclicbarrier, delayed and timer Task. 5 | 6 | #同学们在日常开发中有没有遇到以下场景: 7 | 1. 两个原子任务,任务2需要等待任务1完成了才能进行。 8 | 2. 任务3需要等任务1和任务2都完成了才能进行,但是1和2可以并发以节省时间。看起来要写很多代码来调度任务。 9 | 3. 服务器接口压力过大,要被你的调用频度调戏到down机啦! 10 | 4. 系统的异步任务类AsyncTask要用的泛型太多太重啦,并且只能在主线程使用,不爽! 11 | 5. 要么大量并发使手机cpu吃紧卡到爆,要么不能真正(Android系统自带AsyncTask)并发执行。不爽! 12 | 13 | OK,如果你都遇到过,恭喜你,说明你的应用对开发者要求还是挺碉的。 14 | 那么是不是需要很多的代码才能完成这种和谐并发和任务调度呢?nooooo!有了Crossbow,我们只要一行代码。 15 | 比方说场景2, Task3要等待Task1,Task2执行完才能执行,我们使用LiteAsync可以这样做: 16 | 17 | ```java 18 | TaskExecutor.newCyclicBarrierExecutor().put(task1).put(task2).start(task3); 19 | ``` 20 | 21 | 这么一行代码,低调,内敛,而又充满能量,再多的任务可以执行,Task1,Task2并发执行,且随时可取消执行,结束(或取消)时会自动调度Task3执行。 22 | 23 | #关于android并发 24 | 来谈谈并发,研究过Android系统源码的同学会发现:AsyncTask在android2.3的时候线程池是一个核心数为5线程,队列可容纳10线程,最大执行128个任务,这存在一个问题,当你真的有138个并发时,即使手机没被你撑爆,那么超出这个指标应用绝对crash掉。 25 | 后来升级到4.0,为了避免并发带来的一些列问题,AsyncTask竟然成为序列执行器了,也就是你即使你同时execute N个AsyncTask,它也是挨个排队执行的。 26 | 这一点请同学们一定注意,AsyncTask在4.0以后,是异步的没错,但不是并发的。 27 | 28 | 言归正传,我们来看看LiteAsync能做些什么吧: 29 | 30 | #异步任务AsyncTask 31 | 1. Ameliorative AsyncTask:真正可并发,均衡手机能力与开销,针对短时间大量并发有调控策略,可在子线程执行。 32 | 2. SimpleTask:具备Ameliorative AsyncTask所有特性,简化了使用方法,仅设置一个泛型(结果类)即可。 33 | 3. SafeTask:具备Ameliorative AsyncTask所有特性,但是各个环节是安全的,能捕获任何异常,并传递给开发者。 34 | 4. CachedTask:具备Ameliorative AsyncTask所有特性,增加了对结果的缓存,可设置一个超时时间,只有在超时后才去异步执行,否则取缓存结果返回。 35 | 36 | #任务调度器TaskExecutor 37 | 1. 顺序执行器,使一系列异步任务按序执行,非并发 38 | 2. 关卡执行器,使一系列异步任务并发执行,最后会调度执行一个终点任务 39 | 3. 延迟执行器,使一个异步任务延迟开发者指定的时间后执行 40 | 4. 心跳执行器,是一个异步任务按执行的间隔持续执行 41 | 42 | 恩,全部介绍完了,它很简单,却是最贴心的异步&并发爱心天使。 43 | 我在github工程里各自都谢了demo和案例,约10来个,足够你起步啦,现在就用起来吧骚年! 44 | 45 | 46 | 关于作者(About Author) 47 | ----- 48 | 我的博客 :[http://vmatianyu.cn](http://vmatianyu.cn/) 49 | 50 | 我的开源站点 :[http://litesuits.com](http://litesuits.com/) 51 | 52 | 点击加入QQ群: 53 | [42960650](http://jq.qq.com/?_wv=1027&k=cxjcDa) 54 | 55 | [47357508](http://jq.qq.com/?_wv=1027&k=Z7l0Av) 56 | 57 | 我的论坛帖子 58 | ----- 59 | [LiteHttp:极简且智能的 android HTTP 框架库 (专注于网络)](http://www.eoeandroid.com/thread-326584-1-1.html) 60 | 61 | [LiteOrm:极简且智能的 android ORM 框架库 (专注数据库)](http://www.eoeandroid.com/thread-538203-1-1.html) 62 | 63 | [LiteAsync:强势的 android 异步 框架库 (专注异步与并发)](http://www.eoeandroid.com/thread-538212-1-1.html) 64 | 65 | [LiteCommon:丰富通用的android工具类库(专注于基础组件)](http://www.eoeandroid.com/thread-557246-1-1.html) 66 | 67 | 我的博客帖子 68 | ----- 69 | [关于java的线程并发和锁的总结](http://www.vmatianyu.cn/summary-of-the-java-thread-concurrency-and-locking.html) 70 | 71 | [android开发技术经验总结60条](http://www.vmatianyu.cn/summarization-of-technical-experience.html) 72 | 73 | [聚划算android客户端1期教训总结](http://www.vmatianyu.cn/poly-effective-client-1-issues-lessons.html) 74 | 75 | [移动互联网产品设计小结](http://www.vmatianyu.cn/summary-of-mobile-internet-product-design.html) 76 | -------------------------------------------------------------------------------- /jars/lite-async-1.0.0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litesuits/android-lite-async/203736b3013e6f9cea1c150d2d03351cec1a5adf/jars/lite-async-1.0.0.jar -------------------------------------------------------------------------------- /library/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /library/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-19 15 | android.library=true 16 | -------------------------------------------------------------------------------- /library/src/com/litesuits/android/async/ArrayDequeCompat.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.android.async; 2 | 3 | import java.util.ArrayDeque; 4 | 5 | /** 6 | * 精简版{@link ArrayDeque}实现。 7 | * 兼容旧版本Android的{@link ArrayDeque},高性能Stack和Queue。 8 | * @author MaTianyu 9 | * 2014-1-31上午12:37:26 10 | */ 11 | public class ArrayDequeCompat { 12 | private transient E[] elements; 13 | private transient int head; 14 | private transient int tail; 15 | private static final int MIN_INITIAL_CAPACITY = 8; 16 | 17 | // ****** Array allocation and resizing utilities ****** 18 | private void allocateElements(int numElements) { 19 | int initialCapacity = MIN_INITIAL_CAPACITY; 20 | // Find the best power of two to hold elements. 21 | // Tests "<=" because arrays aren't kept full. 22 | if (numElements >= initialCapacity) { 23 | initialCapacity = numElements; 24 | initialCapacity |= (initialCapacity >>> 1); 25 | initialCapacity |= (initialCapacity >>> 2); 26 | initialCapacity |= (initialCapacity >>> 4); 27 | initialCapacity |= (initialCapacity >>> 8); 28 | initialCapacity |= (initialCapacity >>> 16); 29 | initialCapacity++; 30 | 31 | if (initialCapacity < 0) // Too many elements, must back off 32 | initialCapacity >>>= 1;// Good luck allocating 2 ^ 30 elements 33 | } 34 | elements = (E[]) new Object[initialCapacity]; 35 | } 36 | 37 | /** 38 | * Double the capacity of this deque. Call only when full, i.e., 39 | * when head and tail have wrapped around to become equal. 40 | */ 41 | private void doubleCapacity() { 42 | assert head == tail; 43 | int p = head; 44 | int n = elements.length; 45 | int r = n - p; // number of elements to the right of p 46 | int newCapacity = n << 1; 47 | if (newCapacity < 0) throw new IllegalStateException("Sorry, deque too big"); 48 | Object[] a = new Object[newCapacity]; 49 | System.arraycopy(elements, p, a, 0, r); 50 | System.arraycopy(elements, 0, a, r, p); 51 | elements = (E[]) a; 52 | head = 0; 53 | tail = n; 54 | } 55 | 56 | public ArrayDequeCompat() { 57 | elements = (E[]) new Object[16]; 58 | } 59 | 60 | public ArrayDequeCompat(int numElements) { 61 | allocateElements(numElements); 62 | } 63 | 64 | public void addFirst(E e) { 65 | if (e == null) throw new NullPointerException("e == null"); 66 | elements[head = (head - 1) & (elements.length - 1)] = e; 67 | if (head == tail) doubleCapacity(); 68 | } 69 | 70 | public void addLast(E e) { 71 | if (e == null) throw new NullPointerException("e == null"); 72 | elements[tail] = e; 73 | if ((tail = (tail + 1) & (elements.length - 1)) == head) doubleCapacity(); 74 | } 75 | 76 | public boolean offer(E e) { 77 | return offerLast(e); 78 | } 79 | 80 | public boolean offerFirst(E e) { 81 | addFirst(e); 82 | return true; 83 | } 84 | 85 | public boolean offerLast(E e) { 86 | addLast(e); 87 | return true; 88 | } 89 | 90 | public E poll() { 91 | return pollFirst(); 92 | } 93 | 94 | public E pollFirst() { 95 | int h = head; 96 | @SuppressWarnings("unchecked") 97 | E result = (E) elements[h]; 98 | // Element is null if deque empty 99 | if (result == null) return null; 100 | elements[h] = null; // Must null out slot 101 | head = (h + 1) & (elements.length - 1); 102 | return result; 103 | } 104 | 105 | public E pollLast() { 106 | int t = (tail - 1) & (elements.length - 1); 107 | @SuppressWarnings("unchecked") 108 | E result = (E) elements[t]; 109 | if (result == null) return null; 110 | elements[t] = null; 111 | tail = t; 112 | return result; 113 | } 114 | 115 | /** 116 | * Returns the number of elements in this deque. 117 | * 118 | * @return the number of elements in this deque 119 | */ 120 | public int size() { 121 | return (tail - head) & (elements.length - 1); 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /library/src/com/litesuits/android/async/ArraysCompat.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.android.async; 2 | 3 | import java.lang.reflect.Array; 4 | import java.util.Arrays; 5 | 6 | /** 7 | * 兼容旧版本Android的 {@link Arrays}。 8 | * 9 | * @author MaTianyu 10 | * 2014-1-31下午6:12:32 11 | */ 12 | public class ArraysCompat { 13 | 14 | @SuppressWarnings("unchecked") 15 | public static T[] copyOf(T[] original, int newLength) { 16 | return (T[]) copyOf(original, newLength, original.getClass()); 17 | } 18 | 19 | public static T[] copyOf(U[] original, int newLength, Class newType) { 20 | @SuppressWarnings("unchecked") 21 | T[] copy = ((Object) newType == (Object) Object[].class) ? (T[]) new Object[newLength] : (T[]) Array 22 | .newInstance(newType.getComponentType(), newLength); 23 | System.arraycopy(original, 0, copy, 0, Math.min(original.length, newLength)); 24 | return copy; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /library/src/com/litesuits/android/async/AsyncTask.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 litesuits.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.litesuits.android.async; 17 | 18 | import android.os.Handler; 19 | import android.os.Looper; 20 | import android.os.Message; 21 | import android.os.Process; 22 | import android.widget.ListView; 23 | 24 | import java.util.Stack; 25 | import java.util.concurrent.*; 26 | import java.util.concurrent.atomic.AtomicBoolean; 27 | import java.util.concurrent.atomic.AtomicInteger; 28 | 29 | /** 30 | * see {@link android.os.AsyncTask} 31 | *

在系统基础上 32 | *

    33 | *
  • 1. 增强并发能力,根据处理器个数设置线程开销
  • 34 | *
  • 2. 大量线程并发状况下优化线程并发控制及调度策略
  • 35 | *
  • 3. 支持子线程建立并执行{@link AsyncTask},{@link #onPostExecute(Object)}方法一定会在主线程执行
  • 36 | *
37 | * @author MaTianyu 38 | * 2014-1-30下午3:10:43 39 | */ 40 | public abstract class AsyncTask { 41 | private static final String LOG_TAG = "AsyncTask"; 42 | 43 | private static int CPU_COUNT = Runtime.getRuntime().availableProcessors(); 44 | static { 45 | Log.i(LOG_TAG, "CPU : " + CPU_COUNT); 46 | } 47 | /*********************************** 基本线程池(无容量限制) *******************************/ 48 | /** 49 | * 有N处理器,便长期保持N个活跃线程。 50 | */ 51 | private static final int CORE_POOL_SIZE = CPU_COUNT; 52 | private static final int MAXIMUM_POOL_SIZE = Integer.MAX_VALUE; 53 | private static final int KEEP_ALIVE = 10; 54 | private static final ThreadFactory sThreadFactory = new ThreadFactory() { 55 | private final AtomicInteger mCount = new AtomicInteger(1); 56 | 57 | public Thread newThread(Runnable r) { 58 | return new Thread(r, "AsyncTask #" + mCount.getAndIncrement()); 59 | } 60 | }; 61 | private static final BlockingQueue sPoolWorkQueue = new SynchronousQueue(); 62 | /** 63 | * An {@link Executor} that can be used to execute tasks in parallel. 64 | * 核心线程数为{@link #CORE_POOL_SIZE},不限制并发总线程数! 65 | * 这就使得任务总能得到执行,且高效执行少量(<={@link #CORE_POOL_SIZE})异步任务。 66 | * 线程完成任务后保持{@link #KEEP_ALIVE}秒销毁,这段时间内可重用以应付短时间内较大量并发,提升性能。 67 | * 它实际控制并执行线程任务。 68 | */ 69 | public static final ThreadPoolExecutor mCachedSerialExecutor = new ThreadPoolExecutor(CORE_POOL_SIZE, 70 | MAXIMUM_POOL_SIZE, KEEP_ALIVE, TimeUnit.SECONDS, sPoolWorkQueue, sThreadFactory); 71 | 72 | /*********************************** 线程并发控制器 *******************************/ 73 | /** 74 | * 并发量控制: 根据cpu能力控制一段时间内并发数量,并发过量大时采用Lru方式移除旧的异步任务,默认采用LIFO策略调度线程运作,开发者可选调度策略有LIFO、FIFO。 75 | */ 76 | public static final Executor mLruSerialExecutor = new SmartSerialExecutor(); 77 | 78 | /** 79 | * 它大大改善Android自带异步任务框架的处理能力和速度。 80 | * 默认地,它使用LIFO(后进先出)策略来调度线程,可将最新的任务快速执行,当然你自己可以换为FIFO调度策略。 81 | * 这有助于用户当前任务优先完成(比如加载图片时,很容易做到当前屏幕上的图片优先加载)。 82 | * 83 | * @author MaTianyu 84 | * 2014-2-3上午12:46:53 85 | */ 86 | private static class SmartSerialExecutor implements Executor { 87 | /** 88 | * 这里使用{@link ArrayDequeCompat}当栈比{@link Stack}性能高 89 | */ 90 | private ArrayDequeCompat mQueue = new ArrayDequeCompat(serialMaxCount); 91 | private ScheduleStrategy mStrategy = ScheduleStrategy.LIFO; 92 | 93 | private enum ScheduleStrategy { 94 | /** 95 | * 队列中最后加入的任务最先执行 96 | */ 97 | LIFO, 98 | /** 99 | * 队列中最先加入的任务最先执行 100 | */ 101 | FIFO; 102 | } 103 | /** 104 | * 一次同时并发的线程数量,根据处理器数量调节 105 | * 106 | *

cpu count (base) : 1 2 3 4 8 16 32 107 | *

once exe (base*2) : 1 2 3 4 8 16 32 108 | * 109 | *

一个时间段内最多并发线程个数: 110 | * 双核手机:2 111 | * 四核手机:4 112 | * ... 113 | * 计算公式如下: 114 | */ 115 | private static int serialOneTime; 116 | /** 117 | * 最大排队任务数量,当投入的任务过多大于此值时,根据Lru规则,将最老的任务移除(将得不到执行) 118 | *

cpu count : 1 2 3 4 8 16 32 119 | *

base(cpu+3) : 4 5 6 7 11 19 35 120 | *

max(base*16): 64 80 96 112 176 304 560 121 | */ 122 | private static int serialMaxCount; 123 | private int cpuCount = CPU_COUNT; 124 | 125 | private void reSettings(int cpuCount) { 126 | this.cpuCount = cpuCount; 127 | serialOneTime = cpuCount; 128 | serialMaxCount = (cpuCount + 3) * 16; 129 | } 130 | 131 | public SmartSerialExecutor() { 132 | reSettings(CPU_COUNT); 133 | } 134 | 135 | @Override 136 | public synchronized void execute(final Runnable command) { 137 | Runnable r = new Runnable() { 138 | @Override 139 | public void run() { 140 | command.run(); 141 | next(); 142 | } 143 | }; 144 | if (mCachedSerialExecutor.getActiveCount() < serialOneTime) { 145 | // 小于单次并发量直接运行 146 | mCachedSerialExecutor.execute(r); 147 | } else { 148 | // 如果大于并发上限,那么移除最老的任务 149 | if (mQueue.size() >= serialMaxCount) { 150 | mQueue.pollFirst(); 151 | } 152 | // 新任务放在队尾 153 | mQueue.offerLast(r); 154 | 155 | // 动态获取目前cpu处理器数目,并调整设置。 156 | // int proCount = Runtime.getRuntime().availableProcessors(); 157 | // if (proCount != cpuCount) { 158 | // cpuCount = proCount; 159 | // reSettings(proCount); 160 | // } 161 | } 162 | 163 | } 164 | 165 | public synchronized void next() { 166 | Runnable mActive; 167 | switch (mStrategy) { 168 | case LIFO : 169 | mActive = mQueue.pollLast(); 170 | break; 171 | case FIFO : 172 | mActive = mQueue.pollFirst(); 173 | break; 174 | default : 175 | mActive = mQueue.pollLast(); 176 | break; 177 | } 178 | if (mActive != null) mCachedSerialExecutor.execute(mActive); 179 | } 180 | } 181 | 182 | /*********************************** 其他 *******************************/ 183 | 184 | private static final int MESSAGE_POST_RESULT = 0x1; 185 | private static final int MESSAGE_POST_PROGRESS = 0x2; 186 | 187 | protected static final InternalHandler sHandler; 188 | static { 189 | if (Looper.myLooper() != Looper.getMainLooper()) { 190 | sHandler = new InternalHandler(Looper.getMainLooper()); 191 | } else { 192 | sHandler = new InternalHandler(); 193 | } 194 | } 195 | 196 | private static volatile Executor sDefaultExecutor = mCachedSerialExecutor; 197 | private final WorkerRunnable mWorker; 198 | private final FutureTask mFuture; 199 | 200 | private volatile Status mStatus = Status.PENDING; 201 | 202 | private final AtomicBoolean mCancelled = new AtomicBoolean(); 203 | private final AtomicBoolean mTaskInvoked = new AtomicBoolean(); 204 | private FinishedListener finishedListener; 205 | 206 | /** 207 | * Indicates the current status of the task. Each status will be set only once 208 | * during the lifetime of a task. 209 | */ 210 | public enum Status { 211 | /** 212 | * Indicates that the task has not been executed yet. 213 | */ 214 | PENDING, 215 | /** 216 | * Indicates that the task is running. 217 | */ 218 | RUNNING, 219 | /** 220 | * Indicates that {@link AsyncTask#onPostExecute} has finished. 221 | */ 222 | FINISHED, 223 | } 224 | 225 | /** @hide Used to force static handler to be created. */ 226 | public static void init() { 227 | sHandler.getLooper(); 228 | } 229 | 230 | /** @hide */ 231 | public static void setDefaultExecutor(Executor exec) { 232 | sDefaultExecutor = exec; 233 | } 234 | 235 | /** 236 | * Creates a new asynchronous task. This constructor must be invoked on the UI thread. 237 | */ 238 | public AsyncTask() { 239 | mWorker = new WorkerRunnable() { 240 | public Result call() throws Exception { 241 | mTaskInvoked.set(true); 242 | Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 243 | return postResult(doInBackground(mParams)); 244 | } 245 | }; 246 | 247 | mFuture = new FutureTask(mWorker) { 248 | @Override 249 | protected void done() { 250 | try { 251 | postResultIfNotInvoked(get()); 252 | } catch (InterruptedException e) { 253 | android.util.Log.w(LOG_TAG, e); 254 | } catch (ExecutionException e) { 255 | throw new RuntimeException("An error occured while executing doInBackground()", e.getCause()); 256 | } catch (CancellationException e) { 257 | postResultIfNotInvoked(null); 258 | } 259 | } 260 | }; 261 | } 262 | 263 | private void postResultIfNotInvoked(Result result) { 264 | final boolean wasTaskInvoked = mTaskInvoked.get(); 265 | if (!wasTaskInvoked) { 266 | postResult(result); 267 | } 268 | } 269 | 270 | private Result postResult(Result result) { 271 | @SuppressWarnings("unchecked") 272 | Message message = sHandler.obtainMessage(MESSAGE_POST_RESULT, new AsyncTaskResult(this, result)); 273 | message.sendToTarget(); 274 | return result; 275 | } 276 | 277 | /** 278 | * Returns the current status of this task. 279 | * 280 | * @return The current status. 281 | */ 282 | public final Status getStatus() { 283 | return mStatus; 284 | } 285 | 286 | /** 287 | * Override this method to perform a computation on a background thread. The 288 | * specified parameters are the parameters passed to {@link #execute} 289 | * by the caller of this task. 290 | * 291 | * This method can call {@link #publishProgress} to publish updates 292 | * on the UI thread. 293 | * 294 | * @param params The parameters of the task. 295 | * 296 | * @return A result, defined by the subclass of this task. 297 | * 298 | * @see #onPreExecute() 299 | * @see #onPostExecute 300 | * @see #publishProgress 301 | */ 302 | protected abstract Result doInBackground(Params... params); 303 | 304 | /** 305 | * Runs on the UI thread before {@link #doInBackground}. 306 | * 307 | * @see #onPostExecute 308 | * @see #doInBackground 309 | */ 310 | protected void onPreExecute() {} 311 | 312 | /** 313 | *

Runs on the UI thread after {@link #doInBackground}. The 314 | * specified result is the value returned by {@link #doInBackground}.

315 | * 316 | *

This method won't be invoked if the task was cancelled.

317 | * 318 | * @param result The result of the operation computed by {@link #doInBackground}. 319 | * 320 | * @see #onPreExecute 321 | * @see #doInBackground 322 | * @see #onCancelled(Object) 323 | */ 324 | @SuppressWarnings({"UnusedDeclaration"}) 325 | protected void onPostExecute(Result result) {} 326 | 327 | /** 328 | * Runs on the UI thread after {@link #publishProgress} is invoked. 329 | * The specified values are the values passed to {@link #publishProgress}. 330 | * 331 | * @param values The values indicating progress. 332 | * 333 | * @see #publishProgress 334 | * @see #doInBackground 335 | */ 336 | @SuppressWarnings({"UnusedDeclaration"}) 337 | protected void onProgressUpdate(Progress... values) {} 338 | 339 | /** 340 | *

Runs on the UI thread after {@link #cancel(boolean)} is invoked and 341 | * {@link #doInBackground(Object[])} has finished.

342 | * 343 | *

The default implementation simply invokes {@link #onCancelled()} and 344 | * ignores the result. If you write your own implementation, do not call 345 | * super.onCancelled(result).

346 | * 347 | * @param result The result, if any, computed in 348 | * {@link #doInBackground(Object[])}, can be null 349 | * 350 | * @see #cancel(boolean) 351 | * @see #isCancelled() 352 | */ 353 | @SuppressWarnings({"UnusedParameters"}) 354 | protected void onCancelled(Result result) { 355 | onCancelled(); 356 | } 357 | 358 | /** 359 | *

Applications should preferably override {@link #onCancelled(Object)}. 360 | * This method is invoked by the default implementation of 361 | * {@link #onCancelled(Object)}.

362 | * 363 | *

Runs on the UI thread after {@link #cancel(boolean)} is invoked and 364 | * {@link #doInBackground(Object[])} has finished.

365 | * 366 | * @see #onCancelled(Object) 367 | * @see #cancel(boolean) 368 | * @see #isCancelled() 369 | */ 370 | protected void onCancelled() {} 371 | 372 | /** 373 | * Returns true if this task was cancelled before it completed 374 | * normally. If you are calling {@link #cancel(boolean)} on the task, 375 | * the value returned by this method should be checked periodically from 376 | * {@link #doInBackground(Object[])} to end the task as soon as possible. 377 | * 378 | * @return true if task was cancelled before it completed 379 | * 380 | * @see #cancel(boolean) 381 | */ 382 | public final boolean isCancelled() { 383 | return mCancelled.get(); 384 | } 385 | 386 | /** 387 | *

Attempts to cancel execution of this task. This attempt will 388 | * fail if the task has already completed, already been cancelled, 389 | * or could not be cancelled for some other reason. If successful, 390 | * and this task has not started when cancel is called, 391 | * this task should never run. If the task has already started, 392 | * then the mayInterruptIfRunning parameter determines 393 | * whether the thread executing this task should be interrupted in 394 | * an attempt to stop the task.

395 | * 396 | *

Calling this method will result in {@link #onCancelled(Object)} being 397 | * invoked on the UI thread after {@link #doInBackground(Object[])} 398 | * returns. Calling this method guarantees that {@link #onPostExecute(Object)} 399 | * is never invoked. After invoking this method, you should check the 400 | * value returned by {@link #isCancelled()} periodically from 401 | * {@link #doInBackground(Object[])} to finish the task as early as 402 | * possible.

403 | * 404 | * @param mayInterruptIfRunning true if the thread executing this 405 | * task should be interrupted; otherwise, in-progress tasks are allowed 406 | * to complete. 407 | * 408 | * @return false if the task could not be cancelled, 409 | * typically because it has already completed normally; 410 | * true otherwise 411 | * 412 | * @see #isCancelled() 413 | * @see #onCancelled(Object) 414 | */ 415 | public final boolean cancel(boolean mayInterruptIfRunning) { 416 | mCancelled.set(true); 417 | return mFuture.cancel(mayInterruptIfRunning); 418 | } 419 | 420 | /** 421 | * Waits if necessary for the computation to complete, and then 422 | * retrieves its result. 423 | * 424 | * @return The computed result. 425 | * 426 | * @throws CancellationException If the computation was cancelled. 427 | * @throws ExecutionException If the computation threw an exception. 428 | * @throws InterruptedException If the current thread was interrupted 429 | * while waiting. 430 | */ 431 | public final Result get() throws InterruptedException, ExecutionException { 432 | return mFuture.get(); 433 | } 434 | 435 | /** 436 | * Waits if necessary for at most the given time for the computation 437 | * to complete, and then retrieves its result. 438 | * 439 | * @param timeout Time to wait before cancelling the operation. 440 | * @param unit The time unit for the timeout. 441 | * 442 | * @return The computed result. 443 | * 444 | * @throws CancellationException If the computation was cancelled. 445 | * @throws ExecutionException If the computation threw an exception. 446 | * @throws InterruptedException If the current thread was interrupted 447 | * while waiting. 448 | * @throws TimeoutException If the wait timed out. 449 | */ 450 | public final Result get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, 451 | TimeoutException { 452 | return mFuture.get(timeout, unit); 453 | } 454 | 455 | /** 456 | * Executes the task with the specified parameters. The task returns 457 | * itself (this) so that the caller can keep a reference to it. 458 | * 459 | *

Execute a task immediately. 460 | * 461 | *

This method must be invoked on the UI thread. 462 | * 463 | *

用于重要、紧急、单独的异步任务,该Task立即得到执行。 464 | *

加载类似瀑布流时产生的大量并发(一定程度允许任务被剔除队列)时请用{@link AsyncTask#executeAllowingLoss(Object...)} 465 | * @param params The parameters of the task. 466 | * 467 | * @return This instance of AsyncTask. 468 | * 469 | * @throws IllegalStateException If {@link #getStatus()} returns either 470 | * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. 471 | * 472 | * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) 473 | * @see #execute(Runnable) 474 | */ 475 | public final AsyncTask execute(final Params... params) { 476 | return executeOnExecutor(sDefaultExecutor, params); 477 | } 478 | 479 | /** 480 | *

用于瞬间大量并发的场景,比如,假设用户拖动{@link ListView}时如果需要加载大量图片,而拖动过去时间很久的用户已经看不到,允许任务丢失。 481 | *

This method execute task wisely when a large number of task will be submitted. 482 | * @param params 483 | * @return 484 | */ 485 | public final AsyncTask executeAllowingLoss(Params... params) { 486 | return executeOnExecutor(mLruSerialExecutor, params); 487 | } 488 | 489 | /** 490 | * Executes the task with the specified parameters. The task returns 491 | * itself (this) so that the caller can keep a reference to it. 492 | * 493 | *

This method is typically used with {@link #mCachedSerialExecutor} to 494 | * allow multiple tasks to run in parallel on a pool of threads managed by 495 | * AsyncTask, however you can also use your own {@link Executor} for custom 496 | * behavior. 497 | * 498 | *

This method must be invoked on the UI thread. 499 | * 500 | * @param exec The executor to use. {@link #mCachedSerialExecutor} is available as a 501 | * convenient process-wide thread pool for tasks that are loosely coupled. 502 | * @param params The parameters of the task. 503 | * 504 | * @return This instance of AsyncTask. 505 | * 506 | * @throws IllegalStateException If {@link #getStatus()} returns either 507 | * {@link AsyncTask.Status#RUNNING} or {@link AsyncTask.Status#FINISHED}. 508 | * 509 | * @see #execute(Object[]) 510 | */ 511 | public final AsyncTask executeOnExecutor(Executor exec, Params... params) { 512 | if (mStatus != Status.PENDING) { 513 | switch (mStatus) { 514 | case RUNNING : 515 | throw new IllegalStateException("Cannot execute task:" + " the task is already running."); 516 | case FINISHED : 517 | throw new IllegalStateException("Cannot execute task:" + " the task has already been executed " 518 | + "(a task can be executed only once)"); 519 | } 520 | } 521 | 522 | mStatus = Status.RUNNING; 523 | 524 | onPreExecute(); 525 | 526 | mWorker.mParams = params; 527 | exec.execute(mFuture); 528 | 529 | return this; 530 | } 531 | 532 | /** 533 | * Convenience version of {@link #execute(Object...)} for use with 534 | * a simple Runnable object. See {@link #execute(Object[])} for more 535 | * information on the order of execution. 536 | *

用于重要、紧急、单独的异步任务,该Runnable立即得到执行。 537 | *

加载类似瀑布流时产生的大量并发(任务数超出限制允许任务被剔除队列)时请用{@link AsyncTask#executeAllowingLoss(Runnable)} 538 | * @see #execute(Object[]) 539 | * @see #executeOnExecutor(java.util.concurrent.Executor, Object[]) 540 | */ 541 | public static void execute(Runnable runnable) { 542 | sDefaultExecutor.execute(runnable); 543 | } 544 | 545 | /** 546 | *

用于瞬间大量并发的场景,比如,假设用户拖动{@link ListView}时如果需要启动大量异步线程,而拖动过去时间很久的用户已经看不到,允许任务丢失。 547 | *

This method execute runnable wisely when a large number of task will be submitted. 548 | *

任务数限制情况见{@link SmartSerialExecutor} 549 | * immediate execution for important or urgent task. 550 | * @param runnable 551 | */ 552 | public static void executeAllowingLoss(Runnable runnable) { 553 | mLruSerialExecutor.execute(runnable); 554 | } 555 | 556 | /** 557 | * This method can be invoked from {@link #doInBackground} to 558 | * publish updates on the UI thread while the background computation is 559 | * still running. Each call to this method will trigger the execution of 560 | * {@link #onProgressUpdate} on the UI thread. 561 | * 562 | * {@link #onProgressUpdate} will note be called if the task has been 563 | * canceled. 564 | * 565 | * @param values The progress values to update the UI with. 566 | * 567 | * @see #onProgressUpdate 568 | * @see #doInBackground 569 | */ 570 | protected final void publishProgress(Progress... values) { 571 | if (!isCancelled()) { 572 | sHandler.obtainMessage(MESSAGE_POST_PROGRESS, new AsyncTaskResult(this, values)).sendToTarget(); 573 | } 574 | } 575 | 576 | private void finish(Result result) { 577 | if (isCancelled()) { 578 | onCancelled(result); 579 | if (finishedListener != null) finishedListener.onCancelled(); 580 | } else { 581 | onPostExecute(result); 582 | if (finishedListener != null) finishedListener.onPostExecute(); 583 | } 584 | mStatus = Status.FINISHED; 585 | } 586 | 587 | protected FinishedListener getFinishedListener() { 588 | return finishedListener; 589 | } 590 | 591 | protected void setFinishedListener(FinishedListener finishedListener) { 592 | this.finishedListener = finishedListener; 593 | } 594 | 595 | private static class InternalHandler extends Handler { 596 | public InternalHandler() { 597 | super(); 598 | } 599 | 600 | public InternalHandler(Looper looper) { 601 | super(looper); 602 | } 603 | 604 | @SuppressWarnings({"unchecked", "RawUseOfParameterizedType"}) 605 | @Override 606 | public void handleMessage(Message msg) { 607 | AsyncTaskResult result = (AsyncTaskResult) msg.obj; 608 | switch (msg.what) { 609 | case MESSAGE_POST_RESULT : 610 | // There is only one result 611 | result.mTask.finish(result.mData[0]); 612 | break; 613 | case MESSAGE_POST_PROGRESS : 614 | result.mTask.onProgressUpdate(result.mData); 615 | break; 616 | } 617 | } 618 | } 619 | 620 | private static abstract class WorkerRunnable implements Callable { 621 | Params[] mParams; 622 | } 623 | 624 | @SuppressWarnings({"RawUseOfParameterizedType"}) 625 | private static class AsyncTaskResult { 626 | final AsyncTask mTask; 627 | final Data[] mData; 628 | 629 | AsyncTaskResult(AsyncTask task, Data... data) { 630 | mTask = task; 631 | mData = data; 632 | } 633 | } 634 | 635 | public static interface FinishedListener { 636 | void onCancelled(); 637 | 638 | void onPostExecute(); 639 | } 640 | 641 | } 642 | -------------------------------------------------------------------------------- /library/src/com/litesuits/android/async/CachedTask.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.android.async; 2 | 3 | import android.content.Context; 4 | 5 | import java.io.*; 6 | import java.util.concurrent.ConcurrentHashMap; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | *

Cached AsyncTask 缓存异步任务 11 | *

它主要用于获取网络数据,给它一个缓存时间,只要未超时,它将先从本地获取,仅当超时或本地获取失败时才去真正联网完成。 12 | * 每个Task都必须有唯一标示:key,它唯一标示一个缓存任务,不同的任务绝对不能一样,否则会混淆超时时间。 13 | * {@link #CachedTask#Result} 需要序列化否则不能或者不能完整的读取缓存。 14 | * @author MaTianyu 15 | * 2014-2-23下午8:57:55 16 | */ 17 | public abstract class CachedTask 18 | extends SafeTask { 19 | private static final String TAG = CachedTask.class.getSimpleName(); 20 | private static final String DEFAULT_PATH = "/cachedtask"; 21 | private long expiredTime = 0; 22 | private static String cachePath; 23 | private String key; 24 | private static ConcurrentHashMap cachedTimeMap = new ConcurrentHashMap(); 25 | 26 | public static void cleanCacheFiles(Context context) { 27 | cachedTimeMap.clear(); 28 | cachePath = context.getFilesDir().getAbsolutePath() + DEFAULT_PATH; 29 | File file = new File(cachePath); 30 | final File[] fileList = file.listFiles(); 31 | if (fileList != null) { 32 | TaskExecutor.start(new Runnable() { 33 | @Override 34 | public void run() { 35 | for (File f : fileList) { 36 | if (f.isFile()) f.delete(); 37 | } 38 | } 39 | }); 40 | 41 | } 42 | } 43 | public static void removeKeyValue(String key) { 44 | cachedTimeMap.remove(key); 45 | } 46 | 47 | /** 48 | * @param context app context 49 | * @param key identify label, each single cachedtask should not be the same. 50 | * @param cacheTime expired time 51 | * @param unit if timeunit is null, see cacheTime as millisecond. 52 | */ 53 | public CachedTask(Context context, String key, long cacheTime, TimeUnit unit) { 54 | if (context == null) throw new RuntimeException("CachedTask Initialized Must has Context"); 55 | cachePath = context.getFilesDir().getAbsolutePath() + DEFAULT_PATH; 56 | if (key == null) throw new RuntimeException("CachedTask Must Has Key for Search "); 57 | this.key = key; 58 | if (unit != null) expiredTime = unit.toMillis(cacheTime); 59 | else expiredTime = cacheTime; 60 | } 61 | 62 | protected abstract Result doConnectNetwork(Params... params) throws Exception; 63 | 64 | @Override 65 | protected final Result doInBackgroundSafely(Params... params) throws Exception { 66 | Result res = null; 67 | try { 68 | Long time = cachedTimeMap.get(key); 69 | long lastTime = time == null ? 0 : time; 70 | if (System.currentTimeMillis() - lastTime >= expiredTime) { 71 | res = doConnectNetwork(params); 72 | if (res != null) { 73 | if (Log.isPrint) Log.d(TAG, "doConnectNetwork: sucess"); 74 | cachedTimeMap.put(key, System.currentTimeMillis()); 75 | saveResultToCache(res); 76 | } else { 77 | if (Log.isPrint) Log.d(TAG, "doConnectNetwork: false"); 78 | res = getResultFromCache(); 79 | } 80 | } else { 81 | res = getResultFromCache(); 82 | if (res == null) { 83 | res = doConnectNetwork(params); 84 | if (res != null) { 85 | if (Log.isPrint) Log.d(TAG, "doConnectNetwork: sucess"); 86 | cachedTimeMap.put(key, System.currentTimeMillis()); 87 | saveResultToCache(res); 88 | } else { 89 | if (Log.isPrint) Log.d(TAG, "doConnectNetwork: false"); 90 | } 91 | } 92 | } 93 | } catch (Exception e) { 94 | e.printStackTrace(); 95 | } 96 | return res; 97 | } 98 | 99 | @SuppressWarnings("unchecked") 100 | private Result getResultFromCache() { 101 | ObjectInputStream ois = null; 102 | try { 103 | ois = new ObjectInputStream(new FileInputStream(new File(cachePath, key))); 104 | Object obj = ois.readObject(); 105 | 106 | if (obj != null) { 107 | if (Log.isPrint) Log.i(TAG, key+ " read from cache: "+obj); 108 | return (Result) obj; 109 | } 110 | } catch (Exception e) { 111 | e.printStackTrace(); 112 | } finally { 113 | if (ois != null) try { 114 | ois.close(); 115 | } catch (IOException e) { 116 | e.printStackTrace(); 117 | } 118 | } 119 | if (Log.isPrint) Log.e(TAG, "read ResultFromCache: fail "); 120 | return null; 121 | } 122 | 123 | private boolean saveResultToCache(Result res) { 124 | ObjectOutputStream oos = null; 125 | try { 126 | File dir = new File(cachePath); 127 | if (!dir.exists()) dir.mkdirs(); 128 | oos = new ObjectOutputStream(new FileOutputStream(new File(dir, key))); 129 | oos.writeObject(res); 130 | if (Log.isPrint) Log.i(TAG, key +" saveto cache: "+res); 131 | return true; 132 | } catch (Exception e) { 133 | e.printStackTrace(); 134 | } finally { 135 | if (oos != null) try { 136 | oos.close(); 137 | } catch (IOException e) { 138 | e.printStackTrace(); 139 | } 140 | } 141 | if (Log.isPrint) Log.e(TAG, "save Result To Cache: fail"); 142 | return false; 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /library/src/com/litesuits/android/async/Log.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.android.async; 2 | 3 | /** 4 | * the logger 5 | * 6 | * @author MaTianyu 7 | * 2014-1-1下午4:05:39 8 | */ 9 | public final class Log { 10 | 11 | public static boolean isPrint = true; 12 | private static String defaultTag = "Log"; 13 | 14 | private Log() {} 15 | 16 | public static void setTag(String tag) { 17 | defaultTag = tag; 18 | } 19 | 20 | public static int i(Object o) { 21 | return isPrint && o != null ? android.util.Log.i(defaultTag, o.toString()) : -1; 22 | } 23 | 24 | public static int i(String m) { 25 | return isPrint && m != null ? android.util.Log.i(defaultTag, m) : -1; 26 | } 27 | 28 | /*********************** Log ***************************/ 29 | public static int v(String tag, String msg) { 30 | return isPrint && msg != null ? android.util.Log.v(tag, msg) : -1; 31 | } 32 | 33 | public static int d(String tag, String msg) { 34 | return isPrint && msg != null ? android.util.Log.d(tag, msg) : -1; 35 | } 36 | 37 | public static int i(String tag, String msg) { 38 | return isPrint && msg != null ? android.util.Log.i(tag, msg) : -1; 39 | } 40 | 41 | public static int w(String tag, String msg) { 42 | return isPrint && msg != null ? android.util.Log.w(tag, msg) : -1; 43 | } 44 | 45 | public static int e(String tag, String msg) { 46 | return isPrint && msg != null ? android.util.Log.e(tag, msg) : -1; 47 | } 48 | 49 | /*********************** Log with object list ***************************/ 50 | public static int v(String tag, Object... msg) { 51 | return isPrint ? android.util.Log.v(tag, getLogMessage(msg)) : -1; 52 | } 53 | 54 | public static int d(String tag, Object... msg) { 55 | return isPrint ? android.util.Log.d(tag, getLogMessage(msg)) : -1; 56 | } 57 | 58 | public static int i(String tag, Object... msg) { 59 | return isPrint ? android.util.Log.i(tag, getLogMessage(msg)) : -1; 60 | } 61 | 62 | public static int w(String tag, Object... msg) { 63 | return isPrint ? android.util.Log.w(tag, getLogMessage(msg)) : -1; 64 | } 65 | 66 | public static int e(String tag, Object... msg) { 67 | return isPrint ? android.util.Log.e(tag, getLogMessage(msg)) : -1; 68 | } 69 | 70 | private static String getLogMessage(Object... msg) { 71 | if (msg != null && msg.length > 0) { 72 | StringBuilder sb = new StringBuilder(); 73 | for (Object s : msg) { 74 | if (msg != null) sb.append(s.toString()); 75 | } 76 | return sb.toString(); 77 | } 78 | return ""; 79 | } 80 | 81 | /*********************** Log with Throwable ***************************/ 82 | public static int v(String tag, String msg, Throwable tr) { 83 | return isPrint && msg != null ? android.util.Log.v(tag, msg, tr) : -1; 84 | } 85 | 86 | public static int d(String tag, String msg, Throwable tr) { 87 | return isPrint && msg != null ? android.util.Log.d(tag, msg, tr) : -1; 88 | } 89 | 90 | public static int i(String tag, String msg, Throwable tr) { 91 | return isPrint && msg != null ? android.util.Log.i(tag, msg, tr) : -1; 92 | } 93 | 94 | public static int w(String tag, String msg, Throwable tr) { 95 | return isPrint && msg != null ? android.util.Log.w(tag, msg, tr) : -1; 96 | } 97 | 98 | public static int e(String tag, String msg, Throwable tr) { 99 | return isPrint && msg != null ? android.util.Log.e(tag, msg, tr) : -1; 100 | } 101 | 102 | /*********************** TAG use Object Tag ***************************/ 103 | public static int v(Object tag, String msg) { 104 | return isPrint ? android.util.Log.v(tag.getClass().getSimpleName(), msg) : -1; 105 | } 106 | 107 | public static int d(Object tag, String msg) { 108 | return isPrint ? android.util.Log.d(tag.getClass().getSimpleName(), msg) : -1; 109 | } 110 | 111 | public static int i(Object tag, String msg) { 112 | return isPrint ? android.util.Log.i(tag.getClass().getSimpleName(), msg) : -1; 113 | } 114 | 115 | public static int w(Object tag, String msg) { 116 | return isPrint ? android.util.Log.w(tag.getClass().getSimpleName(), msg) : -1; 117 | } 118 | 119 | public static int e(Object tag, String msg) { 120 | return isPrint ? android.util.Log.e(tag.getClass().getSimpleName(), msg) : -1; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /library/src/com/litesuits/android/async/SafeTask.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.android.async; 2 | 3 | /** 4 | *

安全异步任务,可以捕获任意异常,并反馈给给开发者。 5 | *

从执行前,执行中,执行后,乃至更新时的异常都捕获。 6 | *

当{@link #doInBackgroundSafely(Object...)}有异常时,Exception将会被传递到 7 | * {@link #onPostExecuteSafely(Object, Exception)}。 8 | *

9 | *

如果用户取消了任务,那么会将回调{@link #onCancelled()}。 10 | * 11 | * @author MaTianyu 12 | * 2014-2-23下午9:22:34 13 | */ 14 | public abstract class SafeTask extends AsyncTask { 15 | private Exception cause; 16 | private boolean printStackTrace = true; 17 | 18 | @Override 19 | protected final void onPreExecute() { 20 | try { 21 | onPreExecuteSafely(); 22 | } catch (Exception e) { 23 | if (printStackTrace) e.printStackTrace(); 24 | } 25 | } 26 | 27 | @Override 28 | protected final Result doInBackground(Params... params) { 29 | try { 30 | return doInBackgroundSafely(params); 31 | } catch (Exception e) { 32 | if (printStackTrace) e.printStackTrace(); 33 | cause = e; 34 | } 35 | return null; 36 | } 37 | 38 | @Override 39 | protected final void onProgressUpdate(Progress... values) { 40 | try { 41 | onProgressUpdateSafely(values); 42 | } catch (Exception e) { 43 | if (printStackTrace) e.printStackTrace(); 44 | } 45 | } 46 | 47 | @Override 48 | protected final void onPostExecute(Result result) { 49 | try { 50 | onPostExecuteSafely(result, cause); 51 | } catch (Exception e) { 52 | if (printStackTrace) e.printStackTrace(); 53 | } 54 | } 55 | 56 | ; 57 | 58 | @Override 59 | protected final void onCancelled(Result result) { 60 | onCancelled(); 61 | } 62 | 63 | ; 64 | 65 | /** 66 | *

取代了{@link AsyncTask#onPreExecute()}, 这个方法的任意异常都能被捕获:它是安全的。 67 | *

注意:本方法将在开发者启动任务的线程执行。 68 | */ 69 | protected void onPreExecuteSafely() throws Exception {} 70 | 71 | ; 72 | 73 | /** 74 | *

Child Thread 75 | *

取代了{@link AsyncTask#doInBackground(Object...)}, 这个方法的任意异常都能被捕获:它是安全的。 76 | *

如果它发生了异常,Exception将会通过{@link #onPostExecuteSafely(Object, Exception)} 77 | * 传递。 78 | * 79 | * @param params 入参 80 | * @return 81 | */ 82 | protected abstract Result doInBackgroundSafely(Params... params) throws Exception; 83 | 84 | /** 85 | *

Main UI Thread 86 | *

用于取代{@link AsyncTask#onPostExecute(Object)}。 87 | *

注意:本方法一定执行在主线程, 这个方法的任意异常都能被捕获:它是安全的。 88 | * 89 | * @param result 90 | */ 91 | protected void onPostExecuteSafely(Result result, Exception e) throws Exception {} 92 | 93 | ; 94 | 95 | /** 96 | *

Main UI Thread 97 | *

用于取代{@link AsyncTask#onProgressUpdate(Object...)}, 98 | *

这个方法的任意异常都能被捕获:它是安全的。 99 | * 100 | * @param values 更新传递的值 101 | */ 102 | protected void onProgressUpdateSafely(Progress... values) throws Exception {} 103 | 104 | } 105 | -------------------------------------------------------------------------------- /library/src/com/litesuits/android/async/SimpleCachedTask.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.android.async; 2 | 3 | import android.content.Context; 4 | 5 | import java.io.Serializable; 6 | import java.util.concurrent.TimeUnit; 7 | 8 | /** 9 | * 简单的异步任务,仅仅指定返回结果的类型,不可输入参数 10 | * 11 | * @author MaTianyu 12 | * 2014-2-23下午8:57:55 13 | */ 14 | public abstract class SimpleCachedTask extends CachedTask { 15 | public SimpleCachedTask(Context context, String key, long cacheTime, TimeUnit unit) { 16 | super(context, key, cacheTime, unit); 17 | } 18 | 19 | @Override 20 | protected T doConnectNetwork(Object... params) throws Exception{ 21 | return doConnectNetwork(); 22 | } 23 | 24 | protected abstract T doConnectNetwork() throws Exception; 25 | } 26 | -------------------------------------------------------------------------------- /library/src/com/litesuits/android/async/SimpleSafeTask.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.android.async; 2 | 3 | /** 4 | * 简单的安全异步任务,仅仅指定返回结果的类型,不可输入参数 5 | * 6 | * @author MaTianyu 7 | * 2014-2-23下午8:57:55 8 | */ 9 | public abstract class SimpleSafeTask extends SafeTask { 10 | protected abstract T doInBackgroundSafely() throws Exception; 11 | 12 | //@Override 13 | //protected void onPreExecuteSafely() throws Exception {} 14 | 15 | @Override 16 | protected final T doInBackgroundSafely(Object... params) throws Exception { 17 | return doInBackgroundSafely(); 18 | } 19 | 20 | //@Override 21 | //protected void onPostExecuteSafely(T result, Exception e) throws Exception {} 22 | } 23 | -------------------------------------------------------------------------------- /library/src/com/litesuits/android/async/SimpleTask.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.android.async; 2 | 3 | /** 4 | * 简单的异步任务,仅仅指定返回结果的类型,不可输入参数 5 | * 6 | * @author MaTianyu 7 | * 2014-2-23下午8:57:55 8 | */ 9 | public abstract class SimpleTask extends AsyncTask { 10 | 11 | @Override 12 | protected T doInBackground(Object... params) { 13 | return doInBackground(); 14 | } 15 | 16 | protected abstract T doInBackground(); 17 | } 18 | -------------------------------------------------------------------------------- /library/src/com/litesuits/android/async/TaskExecutor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2013 litesuits.com 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | package com.litesuits.android.async; 17 | 18 | import android.os.Handler; 19 | import android.os.Looper; 20 | 21 | import java.util.ArrayList; 22 | import java.util.LinkedList; 23 | import java.util.Timer; 24 | import java.util.TimerTask; 25 | import java.util.concurrent.CountDownLatch; 26 | import java.util.concurrent.TimeUnit; 27 | 28 | /** 29 | * the {@link TaskExecutor} can execute task in many ways. 30 | *

    31 | *
  • 1. OrderedTask, 有序的执行一些列任务。 32 | *
  • 2. CyclicBarrierTask, 并发的执行一系列任务,且会在所有任务执行完成时集中到一个关卡点(执行特定的函数)。 33 | *
  • 3. Delayed Task, 延时任务。 34 | *
  • 4. Timer Runnable, 定时任务。 35 | *
36 | * 37 | * @author MaTianyu 38 | * 2014-2-3下午6:30:14 39 | */ 40 | public class TaskExecutor { 41 | 42 | /** 43 | * 开子线程 44 | * 45 | * @param run 46 | */ 47 | public static void start(Runnable run) { 48 | AsyncTask.execute(run); 49 | } 50 | 51 | /** 52 | * 开子线程,并发超出数量限制时允许丢失任务。 53 | * 54 | * @param run 55 | */ 56 | public static void startAllowingLoss(Runnable run) { 57 | AsyncTask.executeAllowingLoss(run); 58 | } 59 | 60 | /** 61 | * 有序异步任务执行器 62 | * 63 | * @return 64 | */ 65 | public static OrderedTaskExecutor newOrderedExecutor() { 66 | return new OrderedTaskExecutor(); 67 | } 68 | 69 | /** 70 | * 关卡异步任务执行器 71 | * 72 | * @return 73 | */ 74 | public static CyclicBarrierExecutor newCyclicBarrierExecutor() { 75 | return new CyclicBarrierExecutor(); 76 | } 77 | 78 | /** 79 | * 延时异步任务 80 | * 81 | * @param task 82 | * @param time 83 | * @param unit 84 | */ 85 | public static void startDelayedTask(final AsyncTask task, long time, TimeUnit unit) { 86 | long delay = time; 87 | if (unit != null) delay = unit.toMillis(time); 88 | new Handler(Looper.getMainLooper()).postDelayed(new Runnable() { 89 | @Override 90 | public void run() { 91 | task.execute(); 92 | } 93 | }, delay); 94 | } 95 | 96 | /** 97 | * 启动定时任务 98 | * 99 | * @param run 100 | * @param delay >0 延迟时间 101 | * @param period >0 心跳间隔时间 102 | * @return 103 | */ 104 | public static Timer startTimerTask(final Runnable run, long delay, long period) { 105 | Timer timer = new Timer(); 106 | TimerTask timerTask = new TimerTask() { 107 | @Override 108 | public void run() { 109 | run.run(); 110 | } 111 | }; 112 | timer.scheduleAtFixedRate(timerTask, delay, period); 113 | return timer; 114 | } 115 | 116 | public static class OrderedTaskExecutor { 117 | LinkedList> taskList = new LinkedList>(); 118 | private transient boolean isRunning = false; 119 | 120 | public OrderedTaskExecutor put(AsyncTask task) { 121 | synchronized (taskList) { 122 | if (task != null) taskList.add(task); 123 | } 124 | return this; 125 | } 126 | 127 | public void start() { 128 | if (isRunning) return; 129 | isRunning = true; 130 | for (AsyncTask each : taskList) { 131 | final AsyncTask task = each; 132 | task.setFinishedListener(new AsyncTask.FinishedListener() { 133 | 134 | @Override 135 | public void onPostExecute() { 136 | synchronized (taskList) { 137 | executeNext(); 138 | } 139 | } 140 | 141 | @Override 142 | public void onCancelled() { 143 | synchronized (taskList) { 144 | taskList.remove(task); 145 | if (task.getStatus() == AsyncTask.Status.RUNNING) { 146 | executeNext(); 147 | } 148 | } 149 | } 150 | }); 151 | } 152 | executeNext(); 153 | } 154 | 155 | @SuppressWarnings("unchecked") 156 | private void executeNext() { 157 | AsyncTask next = null; 158 | if (taskList.size() > 0) { 159 | next = taskList.removeFirst(); 160 | } 161 | if (next != null) { 162 | next.execute(); 163 | } else { 164 | isRunning = false; 165 | } 166 | } 167 | } 168 | 169 | public static class CyclicBarrierExecutor { 170 | ArrayList> taskList = new ArrayList>(); 171 | private transient boolean isRunning = false; 172 | 173 | public CyclicBarrierExecutor put(AsyncTask task) { 174 | if (task != null) taskList.add(task); 175 | return this; 176 | } 177 | 178 | public void start(final AsyncTask finishTask) { 179 | start(finishTask, 0, null); 180 | } 181 | 182 | @SuppressWarnings("unchecked") 183 | public void start(final AsyncTask endOnUiTask, final long time, final TimeUnit unit) { 184 | if (isRunning) throw new RuntimeException("CyclicBarrierExecutor only can start once."); 185 | isRunning = true; 186 | final CountDownLatch latch = new CountDownLatch(taskList.size()); 187 | new SimpleTask() { 188 | 189 | @Override 190 | protected Boolean doInBackground() { 191 | try { 192 | if (unit == null) latch.await(); 193 | else latch.await(time, unit); 194 | } catch (InterruptedException e) { 195 | e.printStackTrace(); 196 | } 197 | return true; 198 | } 199 | 200 | @Override 201 | protected void onPostExecute(Boolean aBoolean) { 202 | endOnUiTask.execute(); 203 | } 204 | }.execute(); 205 | startInternal(latch); 206 | } 207 | 208 | public void start(Runnable endOnUiThread) { 209 | start(endOnUiThread, 0, null); 210 | } 211 | 212 | public void start(final Runnable endOnUiThread, final long time, final TimeUnit unit) { 213 | if (isRunning) throw new RuntimeException("CyclicBarrierExecutor only can start once."); 214 | isRunning = true; 215 | final CountDownLatch latch = new CountDownLatch(taskList.size()); 216 | new SimpleTask() { 217 | 218 | @Override 219 | protected Boolean doInBackground() { 220 | try { 221 | if (unit == null) latch.await(); 222 | else latch.await(time, unit); 223 | } catch (InterruptedException e) { 224 | e.printStackTrace(); 225 | } 226 | return true; 227 | } 228 | 229 | @Override 230 | protected void onPostExecute(Boolean aBoolean) { 231 | endOnUiThread.run(); 232 | } 233 | }.execute(); 234 | startInternal(latch); 235 | } 236 | 237 | private void startInternal(final CountDownLatch latch) { 238 | for (AsyncTask each : taskList) { 239 | each.setFinishedListener(new AsyncTask.FinishedListener() { 240 | 241 | @Override 242 | public void onPostExecute() { 243 | latch.countDown(); 244 | } 245 | 246 | @Override 247 | public void onCancelled() { 248 | latch.countDown(); 249 | } 250 | }); 251 | each.execute(); 252 | } 253 | } 254 | 255 | } 256 | } 257 | -------------------------------------------------------------------------------- /sample/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 16 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /sample/project.properties: -------------------------------------------------------------------------------- 1 | # This file is automatically generated by Android Tools. 2 | # Do not modify this file -- YOUR CHANGES WILL BE ERASED! 3 | # 4 | # This file must be checked in Version Control Systems. 5 | # 6 | # To customize properties used by the Ant build system edit 7 | # "ant.properties", and override values to adapt the script to your 8 | # project structure. 9 | # 10 | # To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): 11 | #proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt 12 | 13 | # Project target. 14 | target=android-19 15 | android.library=false 16 | android.library.reference.1=../library 17 | -------------------------------------------------------------------------------- /sample/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litesuits/android-lite-async/203736b3013e6f9cea1c150d2d03351cec1a5adf/sample/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sample/res/drawable/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/litesuits/android-lite-async/203736b3013e6f9cea1c150d2d03351cec1a5adf/sample/res/drawable/ic_launcher.png -------------------------------------------------------------------------------- /sample/res/drawable/selector_button.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /sample/res/layout/act_list_btn.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 12 | 13 | 20 | 21 | 25 | 26 | 27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /sample/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | LiteAsync 5 | Lite实验室:Async 6 | Log中查看执行结果 7 | 8 | 9 | 10 | AsyncTask Execute 11 | AsyncTask Exe Runnable 12 | Simple Task 13 | Safe Task 14 | Task Cancel 15 | Ordered Task Executor 16 | CyclicBarrier Task Executor 17 | Delayed Task 18 | Timer Task 19 | Cached Task:可减少服务器压力 20 | 21 | -------------------------------------------------------------------------------- /sample/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 17 | #00000000 18 | #FFFFFF 19 | #000000 20 | 21 | #000000 22 | #E3DBCF 23 | #FFFFFF 24 | #f1f1f1 25 | #efefef 26 | #000000 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /sample/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10dp 5 | 40dp 6 | 15dp 7 | 6dp 8 | 20sp 9 | 16sp 10 | 16dp 11 | 16dp 12 | 13 | -------------------------------------------------------------------------------- /sample/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | LiteAsync 5 | LiteLab:Async 6 | Result in The Log 7 | 8 | 9 | AsyncTask Execute 10 | AsyncTask Exe Runnable 11 | Simple Task 12 | Safe Task 13 | Task Cancel 14 | Ordered Task Executor 15 | CyclicBarrier Task Executor 16 | Delayed Task 17 | Timer Task 18 | Cached Task:Reduce Pressure of Server 19 | 20 | 21 | -------------------------------------------------------------------------------- /sample/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 21 | 22 | 28 | 29 | -------------------------------------------------------------------------------- /sample/src/com/litesuits/android/samples/BaseActivity.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.android.samples; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | import android.view.View; 6 | import android.view.View.OnClickListener; 7 | import android.widget.Button; 8 | import android.widget.LinearLayout; 9 | import android.widget.LinearLayout.LayoutParams; 10 | import android.widget.ScrollView; 11 | import android.widget.TextView; 12 | import com.litesuits.android.async.Log; 13 | import com.litesuits.async.R; 14 | 15 | /** 16 | * 动态添加按钮和点击事件 17 | * 18 | * @author MaTianyu 19 | * 2014-2-25下午2:36:30 20 | */ 21 | public abstract class BaseActivity extends Activity implements OnClickListener { 22 | protected String TAG = "BaseActivity"; 23 | private TextView mTvSubTitle; 24 | public LinearLayout container; 25 | public ScrollView scroll; 26 | 27 | @Override 28 | protected void onCreate(Bundle savedInstanceState) { 29 | super.onCreate(savedInstanceState); 30 | setContentView(R.layout.act_list_btn); 31 | TAG = this.getClass().getSimpleName(); 32 | Log.setTag(TAG); 33 | 34 | container = (LinearLayout) findViewById(R.id.container); 35 | scroll = (ScrollView) container.getParent(); 36 | TextView tv = (TextView) container.findViewById(R.id.title); 37 | tv.setText(getMainTitle()); 38 | mTvSubTitle = (TextView) container.findViewById(R.id.sub_title); 39 | 40 | String[] bttxt = getButtonTexts(); 41 | if (bttxt != null) { 42 | for (int i = 0; i < bttxt.length; i++) { 43 | Button bt = new Button(this); 44 | LayoutParams lp = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT); 45 | int margin = getResources().getDimensionPixelSize(R.dimen.common_marin); 46 | lp.setMargins(margin, margin, margin, margin); 47 | bt.setId(i); 48 | bt.setText(bttxt[i]); 49 | bt.setOnClickListener(this); 50 | bt.setLayoutParams(lp); 51 | container.addView(bt); 52 | } 53 | } 54 | } 55 | 56 | /** 57 | * 获取主标题 58 | */ 59 | public abstract String getMainTitle(); 60 | 61 | /** 62 | * 设置二级标题 63 | */ 64 | public void setSubTitile(String st) { 65 | mTvSubTitle.setText(st); 66 | } 67 | 68 | /** 69 | * 取button列表 70 | */ 71 | public abstract String[] getButtonTexts(); 72 | 73 | /** 74 | * 在{@link #onClick(View)} 里调用。 75 | * id值得含义为:若{@link #getButtonTexts()}的string[]数组长度为len,则id从0,1,2到len-1. 76 | * 点击第N个按钮,id变为N。 77 | */ 78 | public abstract Runnable getButtonClickRunnable(final int id); 79 | 80 | @Override 81 | public void onClick(View v) { 82 | Runnable r = getButtonClickRunnable(v.getId()); 83 | if (r != null) { 84 | new Thread(r).start(); 85 | } 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /sample/src/com/litesuits/android/samples/LiteAsyncSamplesActivity.java: -------------------------------------------------------------------------------- 1 | package com.litesuits.android.samples; 2 | 3 | 4 | import android.os.Bundle; 5 | import android.os.SystemClock; 6 | import android.view.Menu; 7 | import com.litesuits.android.async.*; 8 | import com.litesuits.async.R; 9 | 10 | import java.io.Serializable; 11 | import java.util.ArrayList; 12 | import java.util.Arrays; 13 | import java.util.Timer; 14 | import java.util.concurrent.TimeUnit; 15 | 16 | public class LiteAsyncSamplesActivity extends BaseActivity { 17 | Timer timer; 18 | 19 | /** 20 | * 在{@link BaseActivity#onCreate(Bundle)}中设置视图 21 | */ 22 | @Override 23 | protected void onCreate(Bundle savedInstanceState) { 24 | super.onCreate(savedInstanceState); 25 | setSubTitile(getString(R.string.sub_title)); 26 | } 27 | 28 | public void onDestroy() { 29 | super.onDestroy(); 30 | if (timer != null) 31 | timer.cancel(); 32 | }; 33 | 34 | @Override 35 | public boolean onCreateOptionsMenu(Menu menu) { 36 | return super.onCreateOptionsMenu(menu); 37 | } 38 | 39 | @Override 40 | public String getMainTitle() { 41 | return getString(R.string.title); 42 | } 43 | 44 | @Override 45 | public String[] getButtonTexts() { 46 | return getResources().getStringArray(R.array.async_test_list); 47 | } 48 | 49 | @Override 50 | public Runnable getButtonClickRunnable(final int id) { 51 | // Main UI Thread 52 | // makeAsyncTask(id); 53 | return new Runnable() { 54 | @Override 55 | public void run() { 56 | // Child Thread 57 | makeAsyncTask(id); 58 | } 59 | }; 60 | } 61 | 62 | /** 63 | * 64 | * 0AsyncTask Execute 1AsyncTask Exe Runnable 65 | * 2Simple Task 3Safe Task 4Task 66 | * Cancel 5Ordered Task Executor 6CyclicBarrier 67 | * Task Executor 7Delayed Task 8Timer Task 68 | * 9Cached Task:Reduce Pressure of Server 69 | * 70 | * @param id 71 | */ 72 | private void makeAsyncTask(int id) { 73 | switch (id) { 74 | case 0: 75 | testAsyncTask(); 76 | break; 77 | case 1: 78 | testExeRunnable(); 79 | break; 80 | case 2: 81 | testSimpleTask(); 82 | break; 83 | case 3: 84 | testSafeTask(); 85 | break; 86 | case 4: 87 | test500msCancel(); 88 | break; 89 | case 5: 90 | testOrderedTaskExecutor(); 91 | break; 92 | case 6: 93 | testCyclicBarrierExecutor(); 94 | break; 95 | case 7: 96 | SimpleTask s1 = getTask(1); 97 | TaskExecutor.startDelayedTask(s1, 2, TimeUnit.SECONDS); 98 | break; 99 | case 8: 100 | if (timer != null) 101 | timer.cancel(); 102 | timer = TaskExecutor.startTimerTask(new Runnable() { 103 | 104 | @Override 105 | public void run() { 106 | Log.i(TAG, "bong!"); 107 | } 108 | }, 3000, 1000); 109 | break; 110 | case 9: 111 | // Cached Task 在不需要频繁刷新数据的场景,正确实用它,可极大减轻服务器压力 112 | testCachedAsyncTask(); 113 | default: 114 | break; 115 | } 116 | } 117 | 118 | private void testCachedAsyncTask() { 119 | // 超时时间暂设置为10秒(实际可能时间比较长):第一次无缓存,取自网络。 120 | new SimpleCachedTask(LiteAsyncSamplesActivity.this, 121 | "getUserInfo", 10, TimeUnit.SECONDS) { 122 | @Override 123 | protected User doConnectNetwork() { 124 | // execute...! 125 | Log.i(TAG, " 1 connect to network now.."); 126 | return mockhttpGetUserInfo(); 127 | } 128 | }.execute(); 129 | Log.i(TAG, "first call"); 130 | 131 | SystemClock.sleep(6000); 132 | Log.i(TAG, "sleep 6000ms, second call"); 133 | // sleep 6s , 未超时,数据将取自本地缓存。 134 | new SimpleCachedTask(LiteAsyncSamplesActivity.this, 135 | "getUserInfo", 10, TimeUnit.SECONDS) { 136 | @Override 137 | protected User doConnectNetwork() { 138 | // noooo execute...! 139 | Log.i(TAG, " 2 connect to network now.. 你将看不到这行日志。因为未超时它不会被执行"); 140 | return mockhttpGetUserInfo(); 141 | } 142 | }.execute(); 143 | SystemClock.sleep(6000); 144 | Log.i(TAG, "sleep 6000ms again, third call"); 145 | // sleep 12s , 已超时,数据将取自本地网络。 146 | new SimpleCachedTask(LiteAsyncSamplesActivity.this, 147 | "getUserInfo", 10, TimeUnit.SECONDS) { 148 | @Override 149 | protected User doConnectNetwork() { 150 | // execute...! 151 | Log.i(TAG, " 3 connect to network now.."); 152 | Log.i(TAG, " 3 取自互联网(虚拟)"); 153 | return mockhttpGetUserInfo(); 154 | } 155 | }.execute(); 156 | } 157 | 158 | private void testAsyncTask() { 159 | // execute 160 | AsyncTask task = new AsyncTask() { 161 | @Override 162 | protected Void doInBackground(Void... params) { 163 | Log.i("One AsyncTask execute "); 164 | return null; 165 | } 166 | }; 167 | task.execute(); 168 | SystemClock.sleep(300); 169 | 170 | // 较大量并发 execute allowing loss 171 | for (int i = 0; i < 66; i++) { 172 | final int j = i; 173 | task = new AsyncTask() { 174 | @Override 175 | protected Void doInBackground(Void... params) { 176 | Log.i("AsyncTask executeAllowingLoss " + j); 177 | SystemClock.sleep(500); 178 | return null; 179 | } 180 | }; 181 | task.executeAllowingLoss(); 182 | } 183 | } 184 | 185 | private void testExeRunnable() { 186 | // execute 187 | AsyncTask.execute(new Runnable() { 188 | @Override 189 | public void run() { 190 | Log.i("AsyncTask Runnable execute "); 191 | } 192 | }); 193 | SystemClock.sleep(300); 194 | // 较大量并发 execute allowing loss 195 | for (int i = 0; i < 66; i++) { 196 | final int j = i; 197 | AsyncTask.executeAllowingLoss(new Runnable() { 198 | @Override 199 | public void run() { 200 | Log.i("AsyncTask Runnable executeAllowingLoss " + j); 201 | SystemClock.sleep(500); 202 | } 203 | }); 204 | } 205 | } 206 | 207 | private void testSimpleTask() { 208 | SimpleTask simple = new SimpleTask() { 209 | 210 | @Override 211 | protected Integer doInBackground() { 212 | return 12345678; 213 | } 214 | 215 | @Override 216 | protected void onPostExecute(Integer result) { 217 | Log.i("SimpleTask result: " + result); 218 | } 219 | }; 220 | simple.execute(); 221 | // simple safe success 222 | SimpleSafeTask sst = new SimpleSafeTask() { 223 | 224 | @Override 225 | protected String doInBackgroundSafely() throws Exception { 226 | return "hello"; 227 | } 228 | 229 | @Override 230 | protected void onPostExecuteSafely(String result, Exception e) 231 | throws Exception { 232 | Log.i("SimpleSafeTask onPostExecuteSafely " + result 233 | + " , thread id : " + Thread.currentThread().getId()); 234 | } 235 | }; 236 | sst.execute(); 237 | Log.i("~~~~~You Will See A Lot of Exception Info ~~~~~~:"); 238 | // simple safe, error in every step 239 | SimpleSafeTask sse = new SimpleSafeTask() { 240 | 241 | @Override 242 | protected String doInBackgroundSafely() throws Exception { 243 | publishProgress(1, 2, 3); 244 | Log.i("SimpleSafeTask : doInBackground"); 245 | String s = null; 246 | s.toCharArray(); 247 | return null; 248 | } 249 | 250 | @Override 251 | protected void onPreExecuteSafely() throws Exception { 252 | Log.i("SimpleSafeTask : onPreExecuteSafely"); 253 | String s = null; 254 | s.toCharArray(); 255 | } 256 | 257 | @Override 258 | protected void onPostExecuteSafely(String result, Exception e) 259 | throws Exception { 260 | Log.i("SimpleSafeTask : onPostExecuteSafely, exception: " + e); 261 | String s = null; 262 | s.toCharArray(); 263 | } 264 | 265 | @Override 266 | protected void onProgressUpdateSafely(Object... values) 267 | throws Exception { 268 | Log.i("SimpleSafeTask : onProgressUpdateSafely"); 269 | String s = null; 270 | s.toCharArray(); 271 | } 272 | 273 | }; 274 | sse.execute(); 275 | } 276 | 277 | private void testSafeTask() { 278 | Log.i("~~~~~You Will See A Lot of Exception Info ~~~~~~:"); 279 | // safe task, but make error in every step 280 | SafeTask se = new SafeTask() { 281 | 282 | @Override 283 | protected String doInBackgroundSafely(Integer... params) { 284 | Log.i("SafeTask error doInBackgroundSafely, thread id : " 285 | + Thread.currentThread().getId()); 286 | publishProgress(1, 2, 3); 287 | String s = null; 288 | s.toCharArray(); 289 | return " Result String..."; 290 | } 291 | 292 | @Override 293 | protected void onPreExecuteSafely() { 294 | Log.i("SafeTask error onPreExecuteSafely, thread id : " 295 | + Thread.currentThread().getId()); 296 | String s = null; 297 | s.toCharArray(); 298 | } 299 | 300 | @Override 301 | protected void onPostExecuteSafely(String result, Exception e) 302 | throws Exception { 303 | Log.i("SafeTask error onPostExecuteSafely :" + result 304 | + ", thread id : " + Thread.currentThread().getId()); 305 | Log.i("SafeTask error onPostExecuteSafely Exception :" + e 306 | + ", thread id : " + Thread.currentThread().getId()); 307 | String s = null; 308 | s.toCharArray(); 309 | } 310 | 311 | @Override 312 | protected void onProgressUpdateSafely(Integer... values) { 313 | Log.i("SafeTask error onProgressUpdateSafely :" 314 | + Arrays.toString(values) + ", thread id : " 315 | + Thread.currentThread().getId()); 316 | String s = null; 317 | s.toCharArray(); 318 | } 319 | }; 320 | se.execute(); 321 | 322 | } 323 | 324 | private void test500msCancel() { 325 | SimpleTask st = new SimpleTask() { 326 | 327 | @Override 328 | protected Integer doInBackground() { 329 | try { 330 | Thread.sleep(1500); 331 | } catch (InterruptedException e) { 332 | e.printStackTrace(); 333 | } 334 | return 0; 335 | } 336 | 337 | @Override 338 | protected void onCancelled() { 339 | Log.i("SimpleTask onCancelled "); 340 | } 341 | 342 | @Override 343 | protected void onPostExecute(Integer result) { 344 | Log.i("SimpleTask execute : " + result); 345 | } 346 | }; 347 | st.execute(); 348 | SystemClock.sleep(500); 349 | st.cancel(true); 350 | } 351 | 352 | private void testOrderedTaskExecutor() { 353 | // ordered task 354 | SimpleTask s1 = getTask(1); 355 | SimpleTask s2 = getTask(2); 356 | final long startTime = System.currentTimeMillis(); 357 | SimpleTask lastTask = new SimpleTask() { 358 | @Override 359 | protected Void doInBackground() { 360 | return null; 361 | } 362 | 363 | @Override 364 | protected void onPostExecute(Void result) { 365 | Log.i("OrderedTaskExecutor use time: " 366 | + (System.currentTimeMillis() - startTime)); 367 | } 368 | }; 369 | // order: 2-1-last ,按task2-task1-lastTask的顺序执行 370 | TaskExecutor.newOrderedExecutor().put(s2).put(s1).put(lastTask).start(); 371 | } 372 | 373 | private void testCyclicBarrierExecutor() { 374 | SimpleTask task1 = getTask(1); 375 | SimpleTask task2 = getTask(2); 376 | SimpleTask task3 = getTask(3); 377 | final long startTime = System.currentTimeMillis(); 378 | SimpleTask destTask = new SimpleTask() { 379 | @Override 380 | protected String doInBackground() { 381 | return "This is the destination. You can do anything you want."; 382 | } 383 | 384 | @Override 385 | protected void onPostExecute(String result) { 386 | Log.i("CyclicBarrierExecutor use time: " 387 | + (System.currentTimeMillis() - startTime) 388 | + " , info: " + result); 389 | } 390 | }; 391 | // task 1,2,3 execute concurrently. destTask is the destination task. 392 | // 123并发执行,全部完成后,执行lastTask。 393 | TaskExecutor.newCyclicBarrierExecutor().put(task1).put(task2) 394 | .put(task3).start(destTask); 395 | task2.cancel(true); 396 | } 397 | 398 | private SimpleTask getTask(final int id) { 399 | SimpleTask simple = new SimpleTask() { 400 | 401 | @Override 402 | protected Integer doInBackground() { 403 | try { 404 | Thread.sleep(1000 * id); 405 | } catch (InterruptedException e) { 406 | } 407 | return id; 408 | } 409 | 410 | @Override 411 | protected void onCancelled() { 412 | Log.i("SimpleTask onCancelled : " + id); 413 | } 414 | 415 | @Override 416 | protected void onPostExecute(Integer result) { 417 | Log.i("SimpleTask execute : " + result); 418 | } 419 | }; 420 | return simple; 421 | } 422 | 423 | /************************************* UserInfo *****************************************************/ 424 | private User mockhttpGetUserInfo() { 425 | User user = new User(); 426 | user.api = "com.litesuits.get.user"; 427 | user.result = new BaseResponse.Result(200, "OK"); 428 | user.data = new User.UserInfo("Lucy", 28); 429 | return user; 430 | } 431 | 432 | public static class User extends BaseResponse { 433 | // private static final long serialVersionUID = -7759004942178651121L; 434 | private UserInfo data; 435 | 436 | public static class UserInfo implements Serializable { 437 | private String name; 438 | private int age; 439 | public ArrayList girl_friends; 440 | 441 | public UserInfo(String name, int age) { 442 | this.name = name; 443 | this.age = age; 444 | } 445 | 446 | @Override 447 | public String toString() { 448 | return "UserInfo [name=" + name + ", age=" + age 449 | + ", girl_friends=" + girl_friends + "]"; 450 | } 451 | 452 | } 453 | 454 | @Override 455 | public String toString() { 456 | return super.toString() + " User [data=" + data + "]"; 457 | } 458 | 459 | } 460 | 461 | public static abstract class BaseResponse implements Serializable { 462 | // private static final long serialVersionUID = 448947882360115789L; 463 | public String api; 464 | private String v; 465 | public Result result; 466 | 467 | public static class Result implements Serializable { 468 | public int code; 469 | public String message; 470 | 471 | public Result(int code, String message) { 472 | this.code = code; 473 | this.message = message; 474 | } 475 | 476 | @Override 477 | public String toString() { 478 | return "Result [code=" + code + ", message=" + message + "]"; 479 | } 480 | 481 | } 482 | 483 | @Override 484 | public String toString() { 485 | return "BaseResponse [api=" + api + ", v=" + v + ", result=" 486 | + result + "]"; 487 | } 488 | } 489 | 490 | } 491 | --------------------------------------------------------------------------------