├── .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 [](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 | 
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 [](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 | 
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 |
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 |
--------------------------------------------------------------------------------