├── .gitignore ├── LICENSE ├── README.md ├── README_CN.md ├── art └── flow.png ├── build.gradle ├── demo ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── zhaiyifan │ │ └── demo │ │ ├── DemoApplication.java │ │ └── MainActivity.java │ └── res │ ├── layout │ └── activity_main.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ └── ic_launcher.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── gradle.properties ├── init-java ├── .gitignore ├── build.gradle └── src │ └── main │ └── java │ └── cn │ └── zhaiyifan │ └── init │ ├── Flow.java │ ├── ILog.java │ ├── Init.java │ ├── LogImpl.java │ ├── ProcessUtils.java │ ├── Status.java │ ├── Task.java │ └── Wave.java ├── init ├── .gitignore ├── build.gradle ├── gradle-mvn-push.gradle ├── gradle.properties ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── cn │ │ └── zhaiyifan │ │ └── init │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ └── java │ └── cn │ └── zhaiyifan │ └── init │ ├── Flow.java │ ├── ILog.java │ ├── Init.java │ ├── LogImpl.java │ ├── ProcessUtils.java │ ├── Status.java │ ├── Task.java │ └── Wave.java └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | 15 | # Gradle files 16 | .gradle/ 17 | build/ 18 | 19 | # Local configuration file (sdk path, etc) 20 | local.properties 21 | 22 | # Proguard folder generated by Eclipse 23 | proguard/ 24 | 25 | # Log Files 26 | *.log 27 | 28 | # intellij workspace 29 | .idea/ 30 | *.iml 31 | gradle/ 32 | gradlew* -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 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 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Init [![Maven Central](https://maven-badges.herokuapp.com/maven-central/cn.zhaiyifan/init/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/cn.zhaiyifan/init) 2 | Init helps app schedule complex task-group like initialization with type, priority and multi-process (you know that every process will run application's onCreate), thus improves efficiency. 3 | 4 | It is originally designed for application initialization, but not confined to that, and can be applied to any complex task-group(like initialization procedure). 5 | 6 | The library does not depend on any third-party library, it depends on Android in the case of Context and Log, and mostly depends on Java concurrent. 7 | 8 | It's named Init for the sake of its original inspiration. 9 | 10 | See [Wiki](https://github.com/markzhai/init/wiki) for detailed usage. 11 | 12 | [For Chinese 中文戳这里](https://github.com/markzhai/init/blob/master/README_CN.md) 13 | 14 | # How 15 | 16 | The task-group(like initialization procedure) can be abstracted to flow, wave and task. 17 | 18 | ![flow](art/flow.png "how it works") 19 | 20 | Flow is a coarse-grained concept, normally we have only one flow, but under certain condition, we may have several flow like patch flow, fake UI flow(to make user feel faster), broadcast flow, etc. 21 | 22 | Each wave can be started only when all blocked task in last wave finished, and all tasks belongs tothe wave will started at the same time(if no delay set). 23 | 24 | As for task, they can be divided into two types 25 | 1. Blocked task, blue tasks in the picture. 26 | 2. Asynchronous task, can be 27 | - completely asynchronous or across several waves like the green task. 28 | - asynchronous task chain, like the two red tasks. 29 | 30 | # Usage 31 | 32 | ```gradle 33 | dependencies { 34 | compile 'cn.zhaiyifan:init:1.0.1' 35 | } 36 | ``` 37 | 38 | ```java 39 | public class DemoApplication extends Application { 40 | 41 | @Override 42 | public void onCreate() { 43 | super.onCreate(); 44 | 45 | // The library needs application context to get process information. 46 | Init.init(this); 47 | // Init.init(this, logProxy) enables custom log component 48 | 49 | Task task1 = new Task("task1") { 50 | 51 | @Override 52 | protected void start() { 53 | doSomeThing(); 54 | } 55 | 56 | // when runs on process which makes the method return true 57 | @Override 58 | public boolean runOnProcess(String processName) { 59 | return processName.equals("cn.zhaiyifan.demo"); 60 | } 61 | }; 62 | 63 | // Create a task which is not blocked with 300 milliseconds delay. 64 | Task task2 = new Task("task2", false, 300) { 65 | 66 | @Override 67 | protected void start() { 68 | doSomeThing(); 69 | } 70 | }; 71 | 72 | // Create more tasks named task3, task4, task5, etc. 73 | ... 74 | 75 | // This is like the two red tasks in the flow picture, task5 depends on task4. 76 | task5.setParentTask(task4); 77 | 78 | Flow flow = new Flow("flow"); 79 | // Add task1 and task2 to wave1 80 | flow.addTask(1, task1) 81 | .addTask(1, task2) 82 | // Add task3 and task4 to wave2 83 | .addTask(2, task3) 84 | .addTask(2, task4) 85 | // Add task5 and task4 to wave3, task5 can be started only after task4 finished 86 | .addTask(3, task5); 87 | 88 | Init.start(flow); 89 | } 90 | ``` 91 | 92 | Let's have a look at log, we can see that the initialization which may take up to 2700ms only run 1307ms now. 93 | ```log 94 | 10-04 18:53:54.789 646-666/cn.zhaiyifan.init I/Task: task2 runs 500 95 | 10-04 18:53:55.289 646-665/cn.zhaiyifan.init I/Task: task1 runs 1000 96 | 10-04 18:53:55.591 646-741/cn.zhaiyifan.init I/Task: task3 runs 300 97 | 10-04 18:53:55.592 646-646/cn.zhaiyifan.init I/Flow: flow runs 1307 98 | 10-04 18:53:55.990 646-740/cn.zhaiyifan.init I/Task: task4 runs 700 99 | 10-04 18:53:56.191 646-783/cn.zhaiyifan.init I/Task: task5 runs 200 100 | ``` 101 | 102 | See demo project for more details. 103 | 104 | Useful api: 105 | ```java 106 | // to set thread pool size 107 | Init.setThreadPoolSize(...) 108 | 109 | // cancel a started flow 110 | Init.cancel(...) 111 | 112 | // get flow status 113 | Init.getFlowStatus(...) 114 | 115 | // get specific task status 116 | flow.getTaskStatus(taskName) 117 | 118 | // set timeout, may not be useful for application init, but can be used for other init operation 119 | flow.setTimeout(5000) 120 | 121 | etc. 122 | ``` 123 | 124 | # Why this 125 | Imagine how we initialize a large application like Facebook, QQ, Wechat, etc, we will face sth like: 126 | 127 | ```java 128 | public class XXXApplication { 129 | 130 | // for multi-dex apps 131 | @Override 132 | protected void attachBaseContext(Context base) { 133 | // log init 134 | ... 135 | // eventbus init... 136 | ... 137 | // global variables init 138 | ... 139 | // process related 140 | String processName = ... 141 | boolean isMainProcess = ... 142 | ProcessInit.attachBaseContext(this, processName, isMainProcess); 143 | } 144 | 145 | @Override 146 | protected void onCreate() { 147 | // process related 148 | String processName = ... 149 | boolean isMainProcess = ... 150 | 151 | // CrashHandler, SafeMode, plugin, image manager, database, download, update, etc init 152 | 153 | ProcessInit.onCreate(this, processName, isMainProcess); 154 | } 155 | 156 | } 157 | 158 | public class ProcessInit { 159 | ... 160 | public static void onCreate(Application application, boolean isMainProcess, String processName) { 161 | if (isMainProcess) { 162 | 163 | } 164 | } else if (processName.contains(PUSH_PROCESS_SUFFIX)) { 165 | ... 166 | } else if (processName.contains(WEB_PROCESS_SUFFIX)) { 167 | ... 168 | } else if (processName.contains(MUSIC_PROCESS_SUFFIX)) { 169 | ... 170 | } else { 171 | ... 172 | } 173 | ... 174 | } 175 | ``` 176 | 177 | You see how complicated the initialization can be when the application grows, some operation should be after the other, and some can be done parallel, and some... Then we need to change the implementation of every init, to make some asynchronous, which makes code dirty and hard to understand. 178 | 179 | How to make it simpler? I came up with this library. 180 | 181 | # Roadmap 182 | - 1.0 *October - A workable solution to principles mentioned above* DONE 183 | - 1.1 **In this year - Support more complex init flow** WIP 184 | - 2.0 Ability to reverse initialization code using this library to flow picture. 185 | 186 | # Contribute 187 | Contribution is welcomed, you can create an issue or directly make a pull request. -------------------------------------------------------------------------------- /README_CN.md: -------------------------------------------------------------------------------- 1 | # Init [![Maven Central](https://maven-badges.herokuapp.com/maven-central/cn.zhaiyifan/init/badge.svg?style=flat)](https://maven-badges.herokuapp.com/maven-central/cn.zhaiyifan/init) 2 | Init帮助Android应用调度复杂的任务流(如应用初始化流程),如下一节图示的那种任务流,处理类型、优先级、多进程(像是每个进程都会执行application的onCreate),任务依赖,提高应用启动效率。 3 | 4 | 尽管Init设计的初衷是为了应用(application)初始化,但并不局限于此,它可以于应用在任何复杂的初始化流程。 5 | 6 | Init不依赖于任何第三方库,使用Java concurrent并部分依赖于Android SDK(Context, Log),所以理论上也可以在简单修改后直接用于Java工程。 7 | 8 | # How 9 | 10 | 初始化流程被抽象为flow、wave和task。 11 | 12 | ![flow](art/flow.png "how it works") 13 | 14 | flow是一个粗粒度概念,通常一个应用只有一个flow,但某些情况下我们可能拥有多个flow,像是patch flow,broadcast flow,fake UI flow等等,可以把它们都交给Init处理。 15 | 16 | 每个wave只有在上一wave的所有阻塞task完成后才能开始,而所有属于该wave的task会一起开始执行(除非被赋予了delay)。 17 | 18 | 至于task,在本库中属于原子性操作,他们可以被分为2大类型 19 | 1. 阻塞task,即图中的蓝色任务。 20 | 2. 异步task,又可以被分为 21 | - 完全异步或者横跨若干个wave后才需要阻塞,像图中的绿色task。 22 | - 异步链,像图中的红色task。 23 | 24 | # 使用 25 | 26 | ```gradle 27 | dependencies { 28 | compile 'cn.zhaiyifan:init:1.0.1' 29 | } 30 | ``` 31 | 32 | ```java 33 | public class DemoApplication extends Application { 34 | 35 | @Override 36 | public void onCreate() { 37 | super.onCreate(); 38 | 39 | // Init需要应用context来获得进程相关信息 40 | Init.init(this); 41 | // 可以使用自定义的log离开输出Init的Log,logProxy需要实现cn.zhaiyifan.appinit.ILog接口 42 | // Init.init(this, logProxy) 43 | 44 | // 默认Task,延迟0,且阻塞下一波task的执行,参数字符串可以用来追踪任务执行状态 45 | Task task1 = new Task("task1") { 46 | 47 | @Override 48 | protected void start() { 49 | doSomeThing(); 50 | } 51 | 52 | // 仅在返回true的时候才会在对应进程执行 53 | @Override 54 | public boolean runOnProcess(String processName) { 55 | return processName.equals("cn.zhaiyifan.demo"); 56 | } 57 | }; 58 | 59 | // 创建一个task,非阻塞,且延时300毫秒执行 60 | Task task2 = new Task("task2", false, 300) { 61 | 62 | @Override 63 | protected void start() { 64 | doSomeThing(); 65 | } 66 | }; 67 | 68 | // 类似地,创建更多task,如task3、task4等等 69 | 70 | // 创建一个有名flow 71 | Flow flow = new Flow("flow"); 72 | // 往flow添加刚才创建的task, 第一个参数是wave序号,会从小到大执行每个wave的task 73 | flow.addTask(1, task1).addTask(1, task2).addTask(2, task3).addTask(2, task4); 74 | // 启动flow,开始初始化 75 | Init.start(flow); 76 | } 77 | ``` 78 | 79 | 看一下log,可以发现原来一个串行执行需要2700毫秒的任务,在我们的安排下,现在只需要1307毫秒就可以结束。 80 | ```log 81 | 10-04 18:53:54.789 646-666/cn.zhaiyifan.init I/Task: task2 runs 500 82 | 10-04 18:53:55.289 646-665/cn.zhaiyifan.init I/Task: task1 runs 1000 83 | 10-04 18:53:55.591 646-741/cn.zhaiyifan.init I/Task: task3 runs 300 84 | 10-04 18:53:55.592 646-646/cn.zhaiyifan.init I/Flow: flow runs 1307 85 | 10-04 18:53:55.990 646-740/cn.zhaiyifan.init I/Task: task4 runs 700 86 | 10-04 18:53:56.191 646-783/cn.zhaiyifan.init I/Task: task5 runs 200 87 | ``` 88 | 89 | Useful api: 90 | ```java 91 | // 设置线程池大小 92 | Init.setThreadPoolSize(...) 93 | 94 | // 取消一个已经开始的flow 95 | Init.cancel(...) 96 | 97 | // 获得flow状态 98 | Init.getFlowStatus(...) 99 | 100 | // 获得特定的task状态 101 | flow.getTaskStatus(taskName) 102 | 103 | // 设置超时限制 104 | flow.setTimeout(5000) 105 | 106 | 等等 107 | ``` 108 | 109 | 更多详情请见demo工程。 110 | 111 | # 为什么需要Init 112 | 想象一下我们是怎么去初始化一个大型应用像是支付宝、QQ、微信、空间等的,我们会面对像是下面这种代码: 113 | 114 | ```java 115 | public class XXXApplication { 116 | 117 | // for multi-dex apps 118 | @Override 119 | protected void attachBaseContext(Context base) { 120 | // log init 121 | ... 122 | // eventbus init... 123 | ... 124 | // global variables init 125 | ... 126 | // process related 127 | String processName = ... 128 | boolean isMainProcess = ... 129 | ProcessInit.attachBaseContext(this, processName, isMainProcess); 130 | } 131 | 132 | @Override 133 | protected void onCreate() { 134 | // process related 135 | String processName = ... 136 | boolean isMainProcess = ... 137 | 138 | // CrashHandler, SafeMode, plugin, image manager, database, download, update, etc init 139 | 140 | ProcessInit.onCreate(this, processName, isMainProcess); 141 | } 142 | 143 | } 144 | 145 | public class ProcessInit { 146 | ... 147 | public static void onCreate(Application application, boolean isMainProcess, String processName) { 148 | if (isMainProcess) { 149 | 150 | } 151 | } else if (processName.contains(PUSH_PROCESS_SUFFIX)) { 152 | ... 153 | } else if (processName.contains(WEB_PROCESS_SUFFIX)) { 154 | ... 155 | } else if (processName.contains(MUSIC_PROCESS_SUFFIX)) { 156 | ... 157 | } else { 158 | ... 159 | } 160 | ... 161 | } 162 | ``` 163 | 164 | 你看到了当一个应用越来越大以后初始化能是一件多么复杂的事情,有些操作必须在另一个之后,而又有一些可以并行执行,又有的操作又需要在一个异步操作完成后才能执行......于是我们就得把每个独立的操作进行修改,有的改成异步,有的则阻塞在另一个操作后,使得代码杂乱且难以维护。 165 | 166 | 怎么可以使它变得简单呢?Init就是来帮助你做这个事的。 167 | 168 | # 路线图 169 | - 1.0 *10月 - 一个实现上述概念的可运行库* 已完成 170 | - 1.1 **2015年内 - 支持更复杂的初始化flow** 进行中 171 | - 2.0 或许明年 - 从使用本库的代码可以直接逆向出初始化flow的图 172 | 173 | # Contribute 174 | 任何贡献都是受欢迎的,你可以创建一个issue或者直接发一个pull请求。 -------------------------------------------------------------------------------- /art/flow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markzhai/init/e45c5e7f5bed13d197605d5e1342a32dc4f5aac0/art/flow.png -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | jcenter() 4 | } 5 | dependencies { 6 | classpath 'com.android.tools.build:gradle:1.3.0' 7 | } 8 | } 9 | 10 | allprojects { 11 | repositories { 12 | jcenter() 13 | } 14 | } -------------------------------------------------------------------------------- /demo/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.1" 6 | 7 | defaultConfig { 8 | applicationId "cn.zhaiyifan.init" 9 | minSdkVersion 8 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | compile 'com.android.support:appcompat-v7:23.0.1' 25 | compile project(':init') 26 | } -------------------------------------------------------------------------------- /demo/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /demo/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /demo/src/main/java/cn/zhaiyifan/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.demo; 2 | 3 | import android.app.Application; 4 | 5 | import cn.zhaiyifan.init.Init; 6 | import cn.zhaiyifan.init.Flow; 7 | import cn.zhaiyifan.init.Task; 8 | 9 | public class DemoApplication extends Application { 10 | 11 | @Override 12 | public void onCreate() { 13 | super.onCreate(); 14 | 15 | // The library needs application context to get process information. 16 | Init.init(this); 17 | // Init.init(this, logProxy) enables custom log component 18 | 19 | Task task1 = new Task("task1") { 20 | 21 | @Override 22 | protected void start() { 23 | try { 24 | Thread.sleep(1000); 25 | } catch (InterruptedException e) { 26 | e.printStackTrace(); 27 | } 28 | } 29 | 30 | @Override 31 | public boolean runOnProcess(String processName) { 32 | return processName.equals("cn.zhaiyifan.init"); 33 | } 34 | 35 | @Override 36 | protected void onResult() { 37 | } 38 | }; 39 | 40 | Task task2 = new Task("task2", false, 5) { 41 | 42 | @Override 43 | protected void start() { 44 | try { 45 | Thread.sleep(500); 46 | } catch (InterruptedException e) { 47 | e.printStackTrace(); 48 | } 49 | } 50 | }; 51 | 52 | Task task3 = new Task("task3", true) { 53 | 54 | @Override 55 | protected void start() { 56 | try { 57 | Thread.sleep(300); 58 | } catch (InterruptedException e) { 59 | e.printStackTrace(); 60 | } 61 | } 62 | }; 63 | 64 | Task task4 = new Task("task4", false) { 65 | 66 | @Override 67 | protected void start() { 68 | try { 69 | Thread.sleep(700); 70 | } catch (InterruptedException e) { 71 | e.printStackTrace(); 72 | } 73 | } 74 | }; 75 | 76 | Task task5 = new Task("task5", false) { 77 | @Override 78 | protected void start() { 79 | try { 80 | Thread.sleep(200); 81 | } catch (InterruptedException e) { 82 | e.printStackTrace(); 83 | } 84 | } 85 | }; 86 | task5.setParentTask(task4); 87 | 88 | Flow flow = new Flow("flow"); 89 | flow.addTask(1, task1) 90 | .addTask(1, task2) 91 | .addTask(2, task3) 92 | .addTask(2, task4) 93 | .addTask(3, task5); 94 | 95 | Init.start(flow); 96 | } 97 | } -------------------------------------------------------------------------------- /demo/src/main/java/cn/zhaiyifan/demo/MainActivity.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.demo; 2 | 3 | import android.support.v7.app.AppCompatActivity; 4 | import android.os.Bundle; 5 | import android.view.Menu; 6 | import android.view.MenuItem; 7 | 8 | public class MainActivity extends AppCompatActivity { 9 | 10 | @Override 11 | protected void onCreate(Bundle savedInstanceState) { 12 | super.onCreate(savedInstanceState); 13 | setContentView(R.layout.activity_main); 14 | } 15 | 16 | @Override 17 | public boolean onCreateOptionsMenu(Menu menu) { 18 | // Inflate the menu; this adds items to the action bar if it is present. 19 | getMenuInflater().inflate(R.menu.menu_main, menu); 20 | return true; 21 | } 22 | 23 | @Override 24 | public boolean onOptionsItemSelected(MenuItem item) { 25 | // Handle action bar item clicks here. The action bar will 26 | // automatically handle clicks on the Home/Up button, so long 27 | // as you specify a parent activity in AndroidManifest.xml. 28 | int id = item.getItemId(); 29 | 30 | //noinspection SimplifiableIfStatement 31 | if (id == R.id.action_settings) { 32 | return true; 33 | } 34 | 35 | return super.onOptionsItemSelected(item); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /demo/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /demo/src/main/res/menu/menu_main.xml: -------------------------------------------------------------------------------- 1 | 4 | 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markzhai/init/e45c5e7f5bed13d197605d5e1342a32dc4f5aac0/demo/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markzhai/init/e45c5e7f5bed13d197605d5e1342a32dc4f5aac0/demo/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markzhai/init/e45c5e7f5bed13d197605d5e1342a32dc4f5aac0/demo/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/markzhai/init/e45c5e7f5bed13d197605d5e1342a32dc4f5aac0/demo/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /demo/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /demo/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AppInit 3 | 4 | Hello world! 5 | Settings 6 | 7 | -------------------------------------------------------------------------------- /demo/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /init-java/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /init-java/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'java' 2 | 3 | dependencies { 4 | compile fileTree(dir: 'libs', include: ['*.jar']) 5 | } -------------------------------------------------------------------------------- /init-java/src/main/java/cn/zhaiyifan/init/Flow.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.concurrent.Callable; 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.concurrent.Future; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | *

A coarse-grained concept, normally an app has only one flow, complex apps can have flow other 12 | * than init flow, like patch, broadcast, etc.

13 | * Created by mark.zhai on 2015/10/2. 14 | */ 15 | public class Flow { 16 | private static final String TAG = "Flow"; 17 | private static final long DEFAULT_FLOW_TIMEOUT = 3000; 18 | 19 | private Map mWaveMap; 20 | private Map mTaskToWaveMap; 21 | 22 | private int mFlowStatus = Status.STATUS_UNKNOWN; 23 | 24 | private String mName; 25 | private long mTimeout = DEFAULT_FLOW_TIMEOUT; 26 | private boolean mCancel = false; 27 | 28 | /** 29 | * Constructor 30 | * 31 | * @param flowName flow name 32 | */ 33 | public Flow(String flowName) { 34 | mName = flowName; 35 | mFlowStatus = Status.STATUS_PENDING_START; 36 | 37 | mWaveMap = new HashMap<>(); 38 | mTaskToWaveMap = new HashMap<>(); 39 | } 40 | 41 | /** 42 | * Add task to this flow. 43 | * 44 | * @param waveSeq Which wave sequence to add. 45 | * @param task task 46 | * @return Flow 47 | */ 48 | public Flow addTask(int waveSeq, Task task) { 49 | if (task != null) { 50 | Wave wave = mWaveMap.get(waveSeq); 51 | if (wave == null) { 52 | wave = new Wave(waveSeq, ProcessUtils.myProcessName()); 53 | mWaveMap.put(waveSeq, wave); 54 | } 55 | wave.addTask(task); 56 | mTaskToWaveMap.put(task.getName(), waveSeq); 57 | } 58 | return this; 59 | } 60 | 61 | /** 62 | * Set timeout to this flow. 63 | * 64 | * @param timeout timeout in milliseconds 65 | */ 66 | public void setTimeout(long timeout) { 67 | mTimeout = timeout; 68 | } 69 | 70 | /** 71 | * Start flow, return when all blocked tasks completed. 72 | */ 73 | public synchronized void start() { 74 | if (mFlowStatus != Status.STATUS_PENDING_START) { 75 | throw new RuntimeException("Error! Flow has already started."); 76 | } 77 | 78 | long startTime = System.currentTimeMillis(); 79 | 80 | ExecutorService threadPool = Init.getThreadPool(); 81 | 82 | Callable flowTask = new Callable() { 83 | @Override 84 | public Boolean call() throws Exception { 85 | for (Map.Entry entry : mWaveMap.entrySet()) { 86 | if (mCancel) { 87 | return false; 88 | } 89 | Wave wave = entry.getValue(); 90 | wave.start(); 91 | } 92 | return true; 93 | } 94 | }; 95 | 96 | Future initTask = threadPool.submit(flowTask); 97 | 98 | try { 99 | initTask.get(mTimeout, TimeUnit.MILLISECONDS); 100 | } catch (Exception e) { 101 | LogImpl.w(TAG, "timeout for flow: " + getName()); 102 | } 103 | 104 | long endTime = System.currentTimeMillis(); 105 | LogImpl.i(TAG, getName() + " runs " + (endTime - startTime)); 106 | 107 | mFlowStatus = Status.STATUS_EXECUTING; 108 | } 109 | 110 | /** 111 | * cannot guarantee immediately cancel 112 | */ 113 | public void cancel() { 114 | mCancel = true; 115 | } 116 | 117 | /** 118 | * Get flow status. 119 | * 120 | * @return status 121 | */ 122 | public int getFlowStatus() { 123 | return mFlowStatus; 124 | } 125 | 126 | /** 127 | * Get task status. 128 | * 129 | * @param taskName task name 130 | * @return status 131 | */ 132 | public int getTaskStatus(String taskName) { 133 | Integer waveSeq = mTaskToWaveMap.get(taskName); 134 | Wave wave = mWaveMap.get(waveSeq); 135 | if (wave != null) { 136 | return wave.getTaskStatus(taskName); 137 | } else { 138 | return Status.STATUS_UNKNOWN; 139 | } 140 | } 141 | 142 | /** 143 | * Get name of the flow. 144 | * 145 | * @return flow name. 146 | */ 147 | public String getName() { 148 | return mName; 149 | } 150 | } -------------------------------------------------------------------------------- /init-java/src/main/java/cn/zhaiyifan/init/ILog.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | /** 4 | *

Log interface for application to implement so that the library can use application's custom 5 | * Log, like file or report

6 | * Created by mark.zhai on 15/10/3. 7 | */ 8 | public interface ILog { 9 | 10 | /** 11 | * Send an information level log message, used by time-related log. 12 | * 13 | * @param tag Used to identify the source of a log message. It usually identifies 14 | * the class or activity where the log call occurs. 15 | * @param msg The message you would like logged. 16 | */ 17 | void info(String tag, String msg); 18 | 19 | 20 | /** 21 | * Send an warning level log message, used by exception-related log. 22 | * 23 | * @param tag Used to identify the source of a log message. It usually identifies 24 | * the class or activity where the log call occurs. 25 | * @param msg The message you would like logged. 26 | */ 27 | void warn(String tag, String msg); 28 | } -------------------------------------------------------------------------------- /init-java/src/main/java/cn/zhaiyifan/init/Init.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.concurrent.ExecutorService; 6 | import java.util.concurrent.Executors; 7 | 8 | /** 9 | *

Entry to add, start and manage init flow.

10 | * Created by mark.zhai on 2015/10/2. 11 | */ 12 | public class Init { 13 | private static final int DEFAULT_THREAD_POOL_SIZE = 8; 14 | 15 | private static Map sFlowMap = new HashMap<>(); 16 | private static int mThreadPoolSize = DEFAULT_THREAD_POOL_SIZE; 17 | 18 | /** 19 | * Init with context and log class. 20 | * 21 | * @param logProxy log class implements {@link ILog} 22 | */ 23 | public static void init(ILog logProxy) { 24 | LogImpl.setLogProxy(logProxy); 25 | } 26 | 27 | public static void addFlow(Flow flow) { 28 | sFlowMap.put(flow.getName(), flow); 29 | } 30 | 31 | public static void addFlow(Map flowMap) { 32 | sFlowMap.putAll(flowMap); 33 | } 34 | 35 | /** 36 | * Set thread pool size used by tasks. 37 | * 38 | * @param size thread pool size, value less or equal than 0 will produce a cached thread pool. 39 | */ 40 | public static void setThreadPoolSize(int size) { 41 | mThreadPoolSize = size; 42 | } 43 | 44 | public static Flow getFlow(String flowName) { 45 | Flow flow = sFlowMap.get(flowName); 46 | return flow != null ? flow : new Flow(flowName); 47 | } 48 | 49 | /** 50 | * start flow. 51 | * 52 | * @param flowName flow key, should be unique for each flow. 53 | */ 54 | public static void start(String flowName) { 55 | Flow flow = sFlowMap.get(flowName); 56 | if (flow != null) { 57 | flow.start(); 58 | } 59 | } 60 | 61 | /** 62 | * start flow. 63 | */ 64 | public static void start(Flow flow) { 65 | flow.start(); 66 | } 67 | 68 | /** 69 | * Cancel the flow. 70 | * 71 | * @param flowName flow key, should be unique for each flow. 72 | */ 73 | public static void cancel(String flowName) { 74 | Flow flow = sFlowMap.get(flowName); 75 | if (flow != null) { 76 | flow.cancel(); 77 | } 78 | } 79 | 80 | /** 81 | * Get status of flow specified by given name, see {@link Status}. 82 | * 83 | * @param flowName flow key, should be unique for each flow. 84 | * @return flow status in {@code STATUS_UNKNOWN}, {@code STATUS_PENDING_START}, 85 | * {@code STATUS_EXECUTING} and {@code STATUS_DONE}. 86 | */ 87 | public static int getFlowStatus(String flowName) { 88 | Flow flow = sFlowMap.get(flowName); 89 | return flow != null ? flow.getFlowStatus() : Status.STATUS_UNKNOWN; 90 | } 91 | 92 | /** 93 | * Get thread pool used internally by Init library. 94 | * 95 | * @return thread pool 96 | */ 97 | public static ExecutorService getThreadPool() { 98 | if (mThreadPoolSize <= 0) { 99 | return Executors.newCachedThreadPool(); 100 | } else { 101 | return Executors.newFixedThreadPool(mThreadPoolSize); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /init-java/src/main/java/cn/zhaiyifan/init/LogImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | public class LogImpl implements ILog { 4 | private static ILog mLogProxy = null; 5 | private static LogImpl mLogger = null; 6 | 7 | public static void i(String tag, String message) { 8 | if (mLogger == null) { 9 | mLogger = new LogImpl(); 10 | } 11 | mLogger.info(tag, message); 12 | } 13 | 14 | public static void w(String tag, String message) { 15 | if (mLogger == null) { 16 | mLogger = new LogImpl(); 17 | } 18 | mLogger.warn(tag, message); 19 | } 20 | 21 | public static void setLogProxy(ILog proxy) { 22 | mLogProxy = proxy; 23 | } 24 | 25 | @Override 26 | public void info(String tag, String msg) { 27 | if (mLogProxy != null) { 28 | mLogProxy.info(tag, msg); 29 | } else { 30 | System.out.println(tag + ": " + msg); 31 | } 32 | } 33 | 34 | @Override 35 | public void warn(String tag, String msg) { 36 | if (mLogProxy != null) { 37 | mLogProxy.warn(tag, msg); 38 | } else { 39 | System.out.println(tag + ": " + msg); 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /init-java/src/main/java/cn/zhaiyifan/init/ProcessUtils.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import java.lang.management.ManagementFactory; 4 | 5 | class ProcessUtils { 6 | public static String myProcessName() { 7 | return ManagementFactory.getRuntimeMXBean().getName(); 8 | } 9 | } -------------------------------------------------------------------------------- /init-java/src/main/java/cn/zhaiyifan/init/Status.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | /** 4 | *

Status of flow.

5 | * Created by mark.zhai on 2015/10/2. 6 | */ 7 | public class Status { 8 | 9 | /** 10 | * Unknown status 11 | */ 12 | public final static int STATUS_UNKNOWN = 0; 13 | 14 | /** 15 | * Pending for start status 16 | */ 17 | public final static int STATUS_PENDING_START = 1; 18 | 19 | /** 20 | * Executing status 21 | */ 22 | public final static int STATUS_EXECUTING = 0; 23 | 24 | /** 25 | * Done status 26 | */ 27 | public final static int STATUS_DONE = 0; 28 | 29 | } -------------------------------------------------------------------------------- /init-java/src/main/java/cn/zhaiyifan/init/Task.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import java.util.concurrent.CountDownLatch; 4 | 5 | /** 6 | *

Atomic abstraction in Init, usually a single-purpose operation.

7 | * Created by mark.zhai on 2015/10/2. 8 | */ 9 | public abstract class Task implements Runnable { 10 | 11 | private static final String TAG = "Task"; 12 | 13 | private String mTaskName; 14 | private CountDownLatch mDoneSignal; 15 | private boolean mIsBlocked = true; 16 | private long mDelay = 0; 17 | private int mStatus = Status.STATUS_PENDING_START; 18 | 19 | // for asynchronous task chain 20 | private Task mParentTask; 21 | private Task mChildTask; 22 | 23 | /** 24 | * Constructor 25 | * 26 | * @param name task name 27 | */ 28 | public Task(String name) { 29 | mTaskName = name; 30 | } 31 | 32 | /** 33 | * Constructor 34 | * 35 | * @param name task name 36 | * @param delay task delay 37 | */ 38 | public Task(String name, long delay) { 39 | mTaskName = name; 40 | mDelay = delay; 41 | } 42 | 43 | /** 44 | * Constructor 45 | * 46 | * @param name task name 47 | * @param isBlocked if task is blocked 48 | */ 49 | public Task(String name, boolean isBlocked) { 50 | mTaskName = name; 51 | mIsBlocked = isBlocked; 52 | } 53 | 54 | /** 55 | * Constructor 56 | * 57 | * @param name task name 58 | * @param isBlocked if task is blocked 59 | * @param delay task delay 60 | */ 61 | public Task(String name, boolean isBlocked, long delay) { 62 | mTaskName = name; 63 | mIsBlocked = isBlocked; 64 | mDelay = delay; 65 | } 66 | 67 | /** 68 | * Normally should not override it 69 | */ 70 | @Override 71 | public void run() { 72 | if (mDelay > 0) { 73 | try { 74 | Thread.sleep(mDelay); 75 | } catch (InterruptedException e) { 76 | LogImpl.w(TAG, getName() + ": " + e.getMessage()); 77 | } 78 | } 79 | if (mParentTask != null) { 80 | synchronized (this) { 81 | try { 82 | wait(); 83 | } catch (InterruptedException e) { 84 | LogImpl.w(TAG, getName() + ": " + e.getMessage()); 85 | } 86 | } 87 | } 88 | mStatus = Status.STATUS_EXECUTING; 89 | 90 | long startTime = System.currentTimeMillis(); 91 | 92 | start(); 93 | 94 | long endTime = System.currentTimeMillis(); 95 | LogImpl.i(TAG, getName() + " runs " + (endTime - startTime)); 96 | 97 | if (mDoneSignal != null) { 98 | mDoneSignal.countDown(); 99 | } 100 | if (mChildTask != null) { 101 | synchronized (mChildTask) { 102 | mChildTask.notify(); 103 | } 104 | } 105 | mStatus = Status.STATUS_DONE; 106 | } 107 | 108 | /** 109 | * Run task. 110 | */ 111 | protected abstract void start(); 112 | 113 | /** 114 | * Returns true if the task is blocked, by default returns true. 115 | */ 116 | public boolean isBlocked() { 117 | return mIsBlocked; 118 | } 119 | 120 | /** 121 | * Set done signal. 122 | * 123 | * @param doneSignal CountDownLatch signal 124 | */ 125 | public void setDoneSignal(CountDownLatch doneSignal) { 126 | this.mDoneSignal = doneSignal; 127 | } 128 | 129 | /** 130 | * Set parent task which blocks this task until it finished. 131 | * 132 | * @param task parent task 133 | */ 134 | public void setParentTask(Task task) { 135 | mParentTask = task; 136 | task.setChildTask(this); 137 | } 138 | 139 | void setChildTask(Task task) { 140 | mChildTask = task; 141 | } 142 | 143 | /** 144 | * Returns delay of the task in milliseconds, by default returns 0. 145 | */ 146 | public long getDelay() { 147 | return mDelay; 148 | } 149 | 150 | /** 151 | * Get task status 152 | * 153 | * @return status 154 | */ 155 | public int getStatus() { 156 | return mStatus; 157 | } 158 | 159 | /** 160 | * Determine task's process, by default returns true, which means run on all processes. 161 | * 162 | * @param processName process name 163 | * @return whether given processName should run the task. 164 | */ 165 | public boolean runOnProcess(String processName) { 166 | return true; 167 | } 168 | 169 | /** 170 | * Get name of task. 171 | * 172 | * @return Task's name 173 | */ 174 | public String getName() { 175 | return mTaskName; 176 | } 177 | } -------------------------------------------------------------------------------- /init-java/src/main/java/cn/zhaiyifan/init/Wave.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | import java.util.concurrent.CountDownLatch; 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.concurrent.Executors; 8 | import java.util.concurrent.TimeUnit; 9 | 10 | /** 11 | *

A wave is made up of several tasks.

12 | * Created by mark.zhai on 2015/10/2. 13 | */ 14 | public class Wave { 15 | private static final String TAG = "Wave"; 16 | private static final long DEFAULT_WAVE_TIMEOUT = 1500; 17 | 18 | private List mTaskList = new LinkedList<>(); 19 | 20 | private String mProcessName; 21 | private int mSequence; 22 | private long mTimeout = DEFAULT_WAVE_TIMEOUT; 23 | private int mStatus = Status.STATUS_UNKNOWN; 24 | 25 | public Wave(int sequence, String processName) { 26 | mSequence = sequence; 27 | mProcessName = processName; 28 | mStatus = Status.STATUS_PENDING_START; 29 | } 30 | 31 | /** 32 | * Add task. 33 | */ 34 | public Wave addTask(Task task) { 35 | mTaskList.add(task); 36 | return this; 37 | } 38 | 39 | /** 40 | * Get wave status. 41 | * 42 | * @return status 43 | */ 44 | public int getStatus() { 45 | return mStatus; 46 | } 47 | 48 | /** 49 | * Get task status. 50 | * 51 | * @param taskName task name 52 | * @return status 53 | */ 54 | public int getTaskStatus(String taskName) { 55 | for (Task task : mTaskList) { 56 | if (task.getName().equals(taskName)) { 57 | return task.getStatus(); 58 | } 59 | } 60 | return Status.STATUS_UNKNOWN; 61 | } 62 | 63 | /** 64 | * Set timeout to this wave. 65 | * 66 | * @param timeout timeout in milliseconds 67 | */ 68 | public Wave setTimeout(long timeout) { 69 | mTimeout = timeout; 70 | return this; 71 | } 72 | 73 | public void start() { 74 | mStatus = Status.STATUS_EXECUTING; 75 | List blockTaskList = new LinkedList<>(); 76 | ExecutorService threadPool = Init.getThreadPool(); 77 | 78 | for (Task task : mTaskList) { 79 | if (task.runOnProcess(mProcessName)) { 80 | if (task.isBlocked()) { 81 | blockTaskList.add(task); 82 | } else { 83 | // just start it 84 | threadPool.submit(task); 85 | } 86 | } 87 | } 88 | 89 | if (blockTaskList.size() > 0) { 90 | CountDownLatch doneSignal = new CountDownLatch(blockTaskList.size()); 91 | 92 | for (Task blockTask : blockTaskList) { 93 | blockTask.setDoneSignal(doneSignal); 94 | threadPool.submit(blockTask); 95 | } 96 | 97 | try { 98 | doneSignal.await(mTimeout, TimeUnit.MILLISECONDS); 99 | } catch (InterruptedException e) { 100 | LogImpl.w(TAG, "Wave " + mSequence + "await interrupted. " + e.getMessage()); 101 | } 102 | } 103 | mStatus = Status.STATUS_DONE; 104 | } 105 | } -------------------------------------------------------------------------------- /init/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /init/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply from: 'gradle-mvn-push.gradle' 3 | 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.1" 7 | 8 | defaultConfig { 9 | minSdkVersion 8 10 | targetSdkVersion 23 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | } -------------------------------------------------------------------------------- /init/gradle-mvn-push.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2013 Chris Banes 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 | 17 | apply plugin: 'maven' 18 | apply plugin: 'signing' 19 | 20 | def isReleaseBuild() { 21 | return VERSION_NAME.contains("SNAPSHOT") == false 22 | } 23 | 24 | def getReleaseRepositoryUrl() { 25 | return hasProperty('RELEASE_REPOSITORY_URL') ? RELEASE_REPOSITORY_URL 26 | : "https://oss.sonatype.org/service/local/staging/deploy/maven2/" 27 | } 28 | 29 | def getSnapshotRepositoryUrl() { 30 | return hasProperty('SNAPSHOT_REPOSITORY_URL') ? SNAPSHOT_REPOSITORY_URL 31 | : "https://oss.sonatype.org/content/repositories/snapshots/" 32 | } 33 | 34 | def getRepositoryUsername() { 35 | return hasProperty('NEXUS_USERNAME') ? NEXUS_USERNAME : "" 36 | } 37 | 38 | def getRepositoryPassword() { 39 | return hasProperty('NEXUS_PASSWORD') ? NEXUS_PASSWORD : "" 40 | } 41 | 42 | afterEvaluate { project -> 43 | uploadArchives { 44 | repositories { 45 | mavenDeployer { 46 | beforeDeployment { MavenDeployment deployment -> signing.signPom(deployment) } 47 | 48 | pom.groupId = GROUP 49 | pom.artifactId = POM_ARTIFACT_ID 50 | pom.version = VERSION_NAME 51 | 52 | repository(url: getReleaseRepositoryUrl()) { 53 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 54 | } 55 | snapshotRepository(url: getSnapshotRepositoryUrl()) { 56 | authentication(userName: getRepositoryUsername(), password: getRepositoryPassword()) 57 | } 58 | 59 | pom.project { 60 | name POM_NAME 61 | packaging POM_PACKAGING 62 | description POM_DESCRIPTION 63 | url POM_URL 64 | 65 | scm { 66 | url POM_SCM_URL 67 | connection POM_SCM_CONNECTION 68 | developerConnection POM_SCM_DEV_CONNECTION 69 | } 70 | 71 | licenses { 72 | license { 73 | name POM_LICENCE_NAME 74 | url POM_LICENCE_URL 75 | distribution POM_LICENCE_DIST 76 | } 77 | } 78 | 79 | developers { 80 | developer { 81 | id POM_DEVELOPER_ID 82 | name POM_DEVELOPER_NAME 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | 90 | signing { 91 | required { isReleaseBuild() && gradle.taskGraph.hasTask("uploadArchives") } 92 | sign configurations.archives 93 | } 94 | 95 | task androidJavadocs(type: Javadoc) { 96 | options.encoding = "utf-8" 97 | source = android.sourceSets.main.java.srcDirs 98 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator)) 99 | } 100 | 101 | task androidJavadocsJar(type: Jar, dependsOn: androidJavadocs) { 102 | classifier = 'javadoc' 103 | from androidJavadocs.destinationDir 104 | } 105 | 106 | task androidSourcesJar(type: Jar) { 107 | classifier = 'sources' 108 | from android.sourceSets.main.java.sourceFiles 109 | } 110 | 111 | artifacts { 112 | archives androidSourcesJar 113 | archives androidJavadocsJar 114 | } 115 | } -------------------------------------------------------------------------------- /init/gradle.properties: -------------------------------------------------------------------------------- 1 | POM_NAME=Android Init Library 2 | POM_ARTIFACT_ID=init 3 | POM_PACKAGING=aar 4 | VERSION_NAME=1.0.1 5 | VERSION_CODE=2 6 | GROUP=cn.zhaiyifan 7 | 8 | POM_DESCRIPTION=Init helps Android apps schedule initialization of application, with type, priority and multi-process, tidy magic code for every process, and improves efficiency of application start. 9 | POM_URL=https://github.com/markzhai/init 10 | POM_SCM_URL=https://github.com/markzhai/init 11 | POM_SCM_CONNECTION=scm:https://github.com/markzhai/init.git 12 | POM_SCM_DEV_CONNECTION=scm:https://github.com/markzhai/init.git 13 | POM_LICENCE_NAME=The Apache Software License, Version 2.0 14 | POM_LICENCE_URL=http://www.apache.org/licenses/LICENSE-2.0.txt 15 | POM_LICENCE_DIST=repo 16 | POM_DEVELOPER_ID=markzhai 17 | POM_DEVELOPER_NAME=markzhai 18 | POM_DEVELOPER_URL=http://blog.zhaiyifan.cn/2015/10/04/init/ -------------------------------------------------------------------------------- /init/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in D:\Android\android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /init/src/androidTest/java/cn/zhaiyifan/init/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /init/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /init/src/main/java/cn/zhaiyifan/init/Flow.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import android.util.SparseArray; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.concurrent.Callable; 8 | import java.util.concurrent.ExecutorService; 9 | import java.util.concurrent.Future; 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | *

A coarse-grained concept, normally an app has only one flow, complex apps can have flow other 14 | * than init flow, like patch, broadcast, etc.

15 | * Created by mark.zhai on 2015/10/2. 16 | */ 17 | public class Flow { 18 | private static final String TAG = "Flow"; 19 | private static final long DEFAULT_FLOW_TIMEOUT = 3000; 20 | 21 | private SparseArray mWaveArray; 22 | private Map mTaskToWaveMap; 23 | 24 | private int mFlowStatus = Status.STATUS_UNKNOWN; 25 | 26 | private String mName; 27 | private long mTimeout = DEFAULT_FLOW_TIMEOUT; 28 | private boolean mCancel = false; 29 | 30 | /** 31 | * Constructor 32 | * 33 | * @param flowName flow name 34 | */ 35 | public Flow(String flowName) { 36 | mName = flowName; 37 | mFlowStatus = Status.STATUS_PENDING_START; 38 | 39 | mWaveArray = new SparseArray<>(); 40 | mTaskToWaveMap = new HashMap<>(); 41 | } 42 | 43 | /** 44 | * Add task to this flow. 45 | * 46 | * @param waveSeq Which wave sequence to add. 47 | * @param task task 48 | * @return Flow 49 | */ 50 | public Flow addTask(int waveSeq, Task task) { 51 | if (task != null) { 52 | Wave wave = mWaveArray.get(waveSeq); 53 | if (wave == null) { 54 | wave = new Wave(waveSeq, ProcessUtils.myProcessName()); 55 | mWaveArray.put(waveSeq, wave); 56 | } 57 | wave.addTask(task); 58 | mTaskToWaveMap.put(task.getName(), waveSeq); 59 | } 60 | return this; 61 | } 62 | 63 | /** 64 | * Set timeout to this flow. 65 | * 66 | * @param timeout timeout in milliseconds 67 | */ 68 | public void setTimeout(long timeout) { 69 | mTimeout = timeout; 70 | } 71 | 72 | /** 73 | * Start flow, return when all blocked tasks completed. 74 | */ 75 | public synchronized void start() { 76 | if (mFlowStatus != Status.STATUS_PENDING_START) { 77 | throw new RuntimeException("Error! Flow has already started."); 78 | } 79 | 80 | long startTime = System.currentTimeMillis(); 81 | 82 | ExecutorService threadPool = Init.getThreadPool(); 83 | 84 | Callable flowTask = new Callable() { 85 | @Override 86 | public Boolean call() throws Exception { 87 | 88 | for (int i = 0, size = mWaveArray.size(); i < size; i++) { 89 | if (mCancel) { 90 | return false; 91 | } 92 | Wave wave = mWaveArray.valueAt(i); 93 | wave.start(); 94 | } 95 | return true; 96 | } 97 | }; 98 | 99 | Future initTask = threadPool.submit(flowTask); 100 | 101 | try { 102 | initTask.get(mTimeout, TimeUnit.MILLISECONDS); 103 | } catch (Exception e) { 104 | LogImpl.w(TAG, "timeout for flow: " + getName()); 105 | } 106 | 107 | long endTime = System.currentTimeMillis(); 108 | LogImpl.i(TAG, getName() + " runs " + (endTime - startTime)); 109 | 110 | mFlowStatus = Status.STATUS_EXECUTING; 111 | } 112 | 113 | /** 114 | * cannot guarantee immediately cancel 115 | */ 116 | public void cancel() { 117 | mCancel = true; 118 | } 119 | 120 | /** 121 | * Get flow status. 122 | * 123 | * @return status 124 | */ 125 | public int getFlowStatus() { 126 | return mFlowStatus; 127 | } 128 | 129 | /** 130 | * Get task status. 131 | * 132 | * @param taskName task name 133 | * @return status 134 | */ 135 | public int getTaskStatus(String taskName) { 136 | Integer waveSeq = mTaskToWaveMap.get(taskName); 137 | Wave wave = mWaveArray.get(waveSeq); 138 | if (wave != null) { 139 | return wave.getTaskStatus(taskName); 140 | } else { 141 | return Status.STATUS_UNKNOWN; 142 | } 143 | } 144 | 145 | /** 146 | * Get name of the flow. 147 | * 148 | * @return flow name. 149 | */ 150 | public String getName() { 151 | return mName; 152 | } 153 | } -------------------------------------------------------------------------------- /init/src/main/java/cn/zhaiyifan/init/ILog.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | /** 4 | *

Log interface for application to implement so that the library can use application's custom 5 | * Log, like file or report

6 | * Created by mark.zhai on 15/10/3. 7 | */ 8 | public interface ILog { 9 | 10 | /** 11 | * Send an information level log message, used by time-related log. 12 | * 13 | * @param tag Used to identify the source of a log message. It usually identifies 14 | * the class or activity where the log call occurs. 15 | * @param msg The message you would like logged. 16 | */ 17 | void info(String tag, String msg); 18 | 19 | 20 | /** 21 | * Send an warning level log message, used by exception-related log. 22 | * 23 | * @param tag Used to identify the source of a log message. It usually identifies 24 | * the class or activity where the log call occurs. 25 | * @param msg The message you would like logged. 26 | */ 27 | void warn(String tag, String msg); 28 | } -------------------------------------------------------------------------------- /init/src/main/java/cn/zhaiyifan/init/Init.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import android.content.Context; 4 | 5 | import java.util.HashMap; 6 | import java.util.Map; 7 | import java.util.concurrent.ExecutorService; 8 | import java.util.concurrent.Executors; 9 | 10 | /** 11 | *

Entry to add, start and manage init flow.

12 | * Created by mark.zhai on 2015/10/2. 13 | */ 14 | public class Init { 15 | private static final int DEFAULT_THREAD_POOL_SIZE = 8; 16 | 17 | private static Map sFlowMap = new HashMap<>(); 18 | private static Context sContext; 19 | private static int mThreadPoolSize = DEFAULT_THREAD_POOL_SIZE; 20 | 21 | private static ExecutorService mExecutorService; 22 | /** 23 | * Init with context. 24 | * 25 | * @param context application context 26 | */ 27 | public static void init(Context context) { 28 | sContext = context; 29 | } 30 | 31 | /** 32 | * Init with context and log class. 33 | * 34 | * @param context application context 35 | * @param logProxy log class implements {@link ILog} 36 | */ 37 | public static void init(Context context, ILog logProxy) { 38 | sContext = context; 39 | LogImpl.setLogProxy(logProxy); 40 | } 41 | 42 | /** 43 | * Add flow to let Init manage. 44 | * 45 | * @param flow flow which unique name 46 | */ 47 | public static void addFlow(Flow flow) { 48 | sFlowMap.put(flow.getName(), flow); 49 | } 50 | 51 | /** 52 | * Add flow map to let Init manage. 53 | * 54 | * @param flowMap map which contains flow-name to flow mapping 55 | */ 56 | public static void addFlow(Map flowMap) { 57 | sFlowMap.putAll(flowMap); 58 | } 59 | 60 | /** 61 | * Remove flow from Init. 62 | * 63 | * @param flowName flow name 64 | */ 65 | public static void removeFlow(String flowName) { 66 | sFlowMap.remove(flowName); 67 | } 68 | 69 | /** 70 | * Clear flow map. 71 | */ 72 | public static void clearFlow() { 73 | sFlowMap.clear(); 74 | } 75 | 76 | /** 77 | * Get application context for process information, package usage. 78 | * 79 | * @return application context 80 | */ 81 | public static Context getContext() { 82 | return sContext; 83 | } 84 | 85 | /** 86 | * Set thread pool size used by tasks. 87 | * 88 | * @param size thread pool size, value less or equal than 0 will produce a cached thread pool. 89 | */ 90 | public static void setThreadPoolSize(int size) { 91 | if(mThreadPoolSize != size){ 92 | mThreadPoolSize = size; 93 | createThreadPool(); 94 | } 95 | } 96 | 97 | public static Flow getFlow(String flowName) { 98 | Flow flow = sFlowMap.get(flowName); 99 | return flow != null ? flow : new Flow(flowName); 100 | } 101 | 102 | /** 103 | * start flow. 104 | * 105 | * @param flowName flow key, should be unique for each flow. 106 | */ 107 | public static void start(String flowName) { 108 | Flow flow = sFlowMap.get(flowName); 109 | if (flow != null) { 110 | flow.start(); 111 | } 112 | } 113 | 114 | /** 115 | * start flow and remove from Init management. 116 | * 117 | * @param flowName flow key, should be unique for each flow. 118 | */ 119 | public static void startAndRemove(String flowName) { 120 | Flow flow = sFlowMap.get(flowName); 121 | if (flow != null) { 122 | flow.start(); 123 | sFlowMap.remove(flowName); 124 | } 125 | } 126 | 127 | /** 128 | * start flow. 129 | */ 130 | public static void start(Flow flow) { 131 | flow.start(); 132 | } 133 | 134 | /** 135 | * Cancel the flow. 136 | * 137 | * @param flowName flow key, should be unique for each flow. 138 | */ 139 | public static void cancel(String flowName) { 140 | Flow flow = sFlowMap.get(flowName); 141 | if (flow != null) { 142 | flow.cancel(); 143 | } 144 | } 145 | 146 | /** 147 | * Get status of flow specified by given name, see {@link Status}. 148 | * 149 | * @param flowName flow key, should be unique for each flow. 150 | * @return flow status in {@code STATUS_UNKNOWN}, {@code STATUS_PENDING_START}, 151 | * {@code STATUS_EXECUTING} and {@code STATUS_DONE}. 152 | */ 153 | public static int getFlowStatus(String flowName) { 154 | Flow flow = sFlowMap.get(flowName); 155 | return flow != null ? flow.getFlowStatus() : Status.STATUS_UNKNOWN; 156 | } 157 | 158 | /** 159 | * Get thread pool used internally by Init library. 160 | * 161 | * @return thread pool 162 | */ 163 | public static ExecutorService getThreadPool() { 164 | if(mExecutorService == null){ 165 | createThreadPool(); 166 | } 167 | return mExecutorService; 168 | } 169 | 170 | /** 171 | * create a thread pool. 172 | */ 173 | private static void createThreadPool(){ 174 | if (mThreadPoolSize <= 0) { 175 | mExecutorService = Executors.newCachedThreadPool(); 176 | } else { 177 | mExecutorService = Executors.newFixedThreadPool(mThreadPoolSize); 178 | } 179 | } 180 | 181 | /** 182 | * This executor will be shutdown if it is no longer referenced and has no threads. 183 | * 184 | */ 185 | public static void releaseThreadPool() { 186 | mExecutorService = null; 187 | } 188 | } -------------------------------------------------------------------------------- /init/src/main/java/cn/zhaiyifan/init/LogImpl.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import android.util.Log; 4 | 5 | public class LogImpl implements ILog { 6 | private static ILog mLogProxy = null; 7 | private static LogImpl mLogger = null; 8 | 9 | public static void i(String tag, String message) { 10 | if (mLogger == null) { 11 | mLogger = new LogImpl(); 12 | } 13 | mLogger.info(tag, message); 14 | } 15 | 16 | public static void w(String tag, String message) { 17 | if (mLogger == null) { 18 | mLogger = new LogImpl(); 19 | } 20 | mLogger.warn(tag, message); 21 | } 22 | 23 | public static void setLogProxy(ILog proxy) { 24 | mLogProxy = proxy; 25 | } 26 | 27 | @Override 28 | public void info(String tag, String message) { 29 | if (mLogProxy != null) { 30 | mLogProxy.info(tag, message); 31 | } else { 32 | Log.i(tag, message); 33 | } 34 | } 35 | 36 | @Override 37 | public void warn(String tag, String message) { 38 | if (mLogProxy != null) { 39 | mLogProxy.warn(tag, message); 40 | } else { 41 | Log.w(tag, message); 42 | } 43 | } 44 | } -------------------------------------------------------------------------------- /init/src/main/java/cn/zhaiyifan/init/ProcessUtils.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import android.app.ActivityManager; 4 | import android.content.Context; 5 | 6 | import java.util.List; 7 | 8 | class ProcessUtils { 9 | private static volatile String sProcessName; 10 | private final static Object sNameLock = new Object(); 11 | 12 | private static volatile Boolean sMainProcess; 13 | private final static Object sMainLock = new Object(); 14 | 15 | public static String myProcessName() { 16 | if (sProcessName != null) { 17 | return sProcessName; 18 | } 19 | synchronized (sNameLock) { 20 | if (sProcessName != null) { 21 | return sProcessName; 22 | } 23 | return sProcessName = obtainProcessName(Init.getContext()); 24 | } 25 | } 26 | 27 | public static boolean isMainProcess() { 28 | if (sMainProcess != null) { 29 | return sMainProcess; 30 | } 31 | synchronized (sMainLock) { 32 | if (sMainProcess != null) { 33 | return sMainProcess; 34 | } 35 | final String processName = myProcessName(); 36 | if (processName == null) { 37 | return false; 38 | } 39 | Context context = Init.getContext(); 40 | sMainProcess = processName.equals(context.getApplicationInfo().processName); 41 | return sMainProcess; 42 | } 43 | } 44 | 45 | private static String obtainProcessName(Context context) { 46 | final int pid = android.os.Process.myPid(); 47 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE); 48 | List listTaskInfo = am.getRunningAppProcesses(); 49 | if (listTaskInfo != null && listTaskInfo.size() > 0) { 50 | for (ActivityManager.RunningAppProcessInfo processInfo : listTaskInfo) { 51 | if (processInfo != null && processInfo.pid == pid) { 52 | return processInfo.processName; 53 | } 54 | } 55 | } 56 | return null; 57 | } 58 | } -------------------------------------------------------------------------------- /init/src/main/java/cn/zhaiyifan/init/Status.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | /** 4 | *

Status of flow.

5 | * Created by mark.zhai on 2015/10/2. 6 | */ 7 | public class Status { 8 | 9 | /** 10 | * Unknown status 11 | */ 12 | public final static int STATUS_UNKNOWN = 0; 13 | 14 | /** 15 | * Pending for start status 16 | */ 17 | public final static int STATUS_PENDING_START = 1; 18 | 19 | /** 20 | * Executing status 21 | */ 22 | public final static int STATUS_EXECUTING = 0; 23 | 24 | /** 25 | * Done status 26 | */ 27 | public final static int STATUS_DONE = 0; 28 | 29 | } -------------------------------------------------------------------------------- /init/src/main/java/cn/zhaiyifan/init/Task.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import android.os.Handler; 4 | import android.os.Looper; 5 | import android.os.Message; 6 | import java.util.concurrent.CountDownLatch; 7 | 8 | /** 9 | *

Atomic abstraction in Init, usually a single-purpose operation.

10 | * Created by mark.zhai on 2015/10/2. 11 | */ 12 | public abstract class Task implements Runnable { 13 | 14 | private static final String TAG = "Task"; 15 | 16 | private String mTaskName; 17 | private CountDownLatch mDoneSignal; 18 | private boolean mIsBlocked = true; 19 | private long mDelay = 0; 20 | private int mStatus = Status.STATUS_PENDING_START; 21 | private static InternalHandler sHandler; 22 | private static final int MESSAGE_POST_RUSULT = 0x1; 23 | // for asynchronous task chain 24 | private Task mParentTask; 25 | private Task mChildTask; 26 | 27 | /** 28 | * Constructor 29 | * 30 | * @param name task name 31 | */ 32 | public Task(String name) { 33 | mTaskName = name; 34 | } 35 | 36 | /** 37 | * Constructor 38 | * 39 | * @param name task name 40 | * @param delay task delay 41 | */ 42 | public Task(String name, long delay) { 43 | mTaskName = name; 44 | mDelay = delay; 45 | } 46 | 47 | /** 48 | * Constructor 49 | * 50 | * @param name task name 51 | * @param isBlocked if task is blocked 52 | */ 53 | public Task(String name, boolean isBlocked) { 54 | mTaskName = name; 55 | mIsBlocked = isBlocked; 56 | } 57 | 58 | /** 59 | * Constructor 60 | * 61 | * @param name task name 62 | * @param isBlocked if task is blocked 63 | * @param delay task delay 64 | */ 65 | public Task(String name, boolean isBlocked, long delay) { 66 | mTaskName = name; 67 | mIsBlocked = isBlocked; 68 | mDelay = delay; 69 | } 70 | 71 | /** 72 | * Normally should not override it 73 | */ 74 | @Override 75 | public void run() { 76 | if (mDelay > 0) { 77 | try { 78 | Thread.sleep(mDelay); 79 | } catch (InterruptedException e) { 80 | LogImpl.w(TAG, getName() + ": " + e.getMessage()); 81 | } 82 | } 83 | if (mParentTask != null) { 84 | synchronized (this) { 85 | try { 86 | if(mParentTask.getStatus() != Status.STATUS_DONE) { 87 | wait(); 88 | } 89 | } catch (InterruptedException e) { 90 | LogImpl.w(TAG, getName() + ": " + e.getMessage()); 91 | } 92 | } 93 | } 94 | mStatus = Status.STATUS_EXECUTING; 95 | 96 | long startTime = System.currentTimeMillis(); 97 | 98 | start(); 99 | getHandler().obtainMessage(MESSAGE_POST_RUSULT, this).sendToTarget(); 100 | 101 | long endTime = System.currentTimeMillis(); 102 | LogImpl.i(TAG, getName() + " runs " + (endTime - startTime)); 103 | 104 | if (mDoneSignal != null) { 105 | mDoneSignal.countDown(); 106 | } 107 | if (mChildTask != null) { 108 | synchronized (mChildTask) { 109 | mChildTask.notify(); 110 | } 111 | } 112 | mStatus = Status.STATUS_DONE; 113 | } 114 | 115 | /** 116 | * This method is invoked by ui thread when the task finish. 117 | */ 118 | protected void onResult(){}; 119 | /** 120 | * Run task. 121 | */ 122 | protected abstract void start(); 123 | 124 | /** 125 | * Returns true if the task is blocked, by default returns true. 126 | */ 127 | public boolean isBlocked() { 128 | return mIsBlocked; 129 | } 130 | 131 | /** 132 | * Set done signal. 133 | * 134 | * @param doneSignal CountDownLatch signal 135 | */ 136 | public void setDoneSignal(CountDownLatch doneSignal) { 137 | this.mDoneSignal = doneSignal; 138 | } 139 | 140 | /** 141 | * Set parent task which blocks this task until it finished. 142 | * 143 | * @param task parent task 144 | */ 145 | public void setParentTask(Task task) { 146 | mParentTask = task; 147 | task.setChildTask(this); 148 | } 149 | 150 | void setChildTask(Task task) { 151 | mChildTask = task; 152 | } 153 | 154 | /** 155 | * Returns delay of the task in milliseconds, by default returns 0. 156 | */ 157 | public long getDelay() { 158 | return mDelay; 159 | } 160 | 161 | /** 162 | * Get task status 163 | * 164 | * @return status 165 | */ 166 | public int getStatus() { 167 | return mStatus; 168 | } 169 | 170 | /** 171 | * Determine task's process, by default returns true, which means run on all processes. 172 | * 173 | * @param processName process name 174 | * @return whether given processName should run the task. 175 | */ 176 | public boolean runOnProcess(String processName) { 177 | return true; 178 | } 179 | 180 | /** 181 | * Get name of task. 182 | * 183 | * @return Task's name 184 | */ 185 | public String getName() { 186 | return mTaskName; 187 | } 188 | 189 | /** 190 | * Provide a public handler for all the task; 191 | * @return a handler object 192 | */ 193 | private static Handler getHandler() { 194 | synchronized (Task.class) { 195 | if (sHandler == null) { 196 | sHandler = new InternalHandler(); 197 | } 198 | return sHandler; 199 | } 200 | } 201 | 202 | private static class InternalHandler extends Handler { 203 | public InternalHandler() { 204 | super(Looper.getMainLooper()); 205 | } 206 | @Override 207 | public void handleMessage(Message msg) { 208 | switch (msg.what) { 209 | case MESSAGE_POST_RUSULT: 210 | Task task = (Task) msg.obj; 211 | task.onResult(); 212 | break; 213 | default: 214 | break; 215 | } 216 | } 217 | } 218 | } -------------------------------------------------------------------------------- /init/src/main/java/cn/zhaiyifan/init/Wave.java: -------------------------------------------------------------------------------- 1 | package cn.zhaiyifan.init; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | import java.util.concurrent.CountDownLatch; 6 | import java.util.concurrent.ExecutorService; 7 | import java.util.concurrent.TimeUnit; 8 | 9 | /** 10 | *

A wave is made up of several tasks.

11 | * Created by mark.zhai on 2015/10/2. 12 | */ 13 | public class Wave { 14 | private static final String TAG = "Wave"; 15 | private static final long DEFAULT_WAVE_TIMEOUT = 1500; 16 | 17 | private List mTaskList = new LinkedList<>(); 18 | 19 | private String mProcessName; 20 | private int mSequence; 21 | private long mTimeout = DEFAULT_WAVE_TIMEOUT; 22 | private int mStatus = Status.STATUS_UNKNOWN; 23 | 24 | public Wave(int sequence, String processName) { 25 | mSequence = sequence; 26 | mProcessName = processName; 27 | mStatus = Status.STATUS_PENDING_START; 28 | } 29 | 30 | /** 31 | * Add task. 32 | */ 33 | public Wave addTask(Task task) { 34 | mTaskList.add(task); 35 | return this; 36 | } 37 | 38 | /** 39 | * Get wave status. 40 | * 41 | * @return status 42 | */ 43 | public int getStatus() { 44 | return mStatus; 45 | } 46 | 47 | /** 48 | * Get task status. 49 | * 50 | * @param taskName task name 51 | * @return status 52 | */ 53 | public int getTaskStatus(String taskName) { 54 | for (Task task : mTaskList) { 55 | if (task.getName().equals(taskName)) { 56 | return task.getStatus(); 57 | } 58 | } 59 | return Status.STATUS_UNKNOWN; 60 | } 61 | 62 | /** 63 | * Set timeout to this wave. 64 | * 65 | * @param timeout timeout in milliseconds 66 | */ 67 | public Wave setTimeout(long timeout) { 68 | mTimeout = timeout; 69 | return this; 70 | } 71 | 72 | public void start() { 73 | mStatus = Status.STATUS_EXECUTING; 74 | List blockTaskList = new LinkedList<>(); 75 | ExecutorService threadPool = Init.getThreadPool(); 76 | 77 | for (Task task : mTaskList) { 78 | if (task.runOnProcess(mProcessName)) { 79 | if (task.isBlocked()) { 80 | blockTaskList.add(task); 81 | } else { 82 | // just start it 83 | threadPool.submit(task); 84 | } 85 | } 86 | } 87 | 88 | if (blockTaskList.size() > 0) { 89 | CountDownLatch doneSignal = new CountDownLatch(blockTaskList.size()); 90 | 91 | for (Task blockTask : blockTaskList) { 92 | blockTask.setDoneSignal(doneSignal); 93 | threadPool.submit(blockTask); 94 | } 95 | 96 | try { 97 | doneSignal.await(mTimeout, TimeUnit.MILLISECONDS); 98 | } catch (InterruptedException e) { 99 | LogImpl.w(TAG, "Wave " + mSequence + "await interrupted. " + e.getMessage()); 100 | } 101 | } 102 | mStatus = Status.STATUS_DONE; 103 | } 104 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':demo', ':init', ':init-java' 2 | --------------------------------------------------------------------------------