├── .gitignore
├── .idea
├── codeStyleSettings.xml
├── compiler.xml
├── encodings.xml
├── gradle.xml
├── gradle_extensions.xml
├── inspectionProfiles
│ ├── Project_Default.xml
│ └── profiles_settings.xml
├── misc.xml
├── modules.xml
└── runConfigurations.xml
├── LICENSE
├── README.md
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── hellodaemon
├── .gitignore
├── bintrayv1.gradle
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── xdandroid
│ │ └── hellodaemon
│ │ ├── AbsWorkService.java
│ │ ├── DaemonEnv.java
│ │ ├── IntentWrapper.java
│ │ ├── JobSchedulerService.java
│ │ ├── WakeUpReceiver.java
│ │ └── WatchDogService.java
│ └── res
│ └── values
│ └── strings.xml
├── sample
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── xdandroid
│ │ └── sample
│ │ ├── App.java
│ │ ├── MainActivity.java
│ │ └── TraceServiceImpl.java
│ └── res
│ ├── layout
│ └── activity_main.xml
│ ├── mipmap-xxxhdpi
│ └── ic_launcher.png
│ └── values
│ └── strings.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 | .externalNativeBuild
10 | .idea/
11 |
--------------------------------------------------------------------------------
/.idea/codeStyleSettings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
227 |
228 |
229 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/.idea/gradle_extensions.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | <i>Task group: other<i>
10 | Displays the sub-projects of root project 'HelloDaemon'.<p><i>Task group: help<i>
11 | <i>Task group: other<i>
12 | <i>Task group: other<i>
13 | <i>Task group: other<i>
14 | <i>Task group: other<i>
15 | <i>Task group: other<i>
16 | Assembles a bundle containing the library in debug.<p><i>Task group: other<i>
17 | <i>Task group: other<i>
18 | <i>Task group: other<i>
19 | <i>Task group: other<i>
20 | Runs lint on the Debug build.<p><i>Task group: verification<i>
21 | Assembles a bundle containing the library in release.<p><i>Task group: other<i>
22 | <i>Task group: other<i>
23 | Displays the configuration model of root project 'HelloDaemon'. [incubating]<p><i>Task group: help<i>
24 | Creates a version of android.jar that's suitable for unit tests.<p><i>Task group: build<i>
25 | Displays the tasks runnable from root project 'HelloDaemon' (some of the displayed tasks may belong to subprojects).<p><i>Task group: help<i>
26 | <i>Task group: build<i>
27 | Initializes a new Gradle build. [incubating]<p><i>Task group: Build Setup<i>
28 | <i>Task group: other<i>
29 | <i>Task group: other<i>
30 | <i>Task group: other<i>
31 | <i>Task group: other<i>
32 | Runs all device checks using Device Providers and Test Servers.<p><i>Task group: verification<i>
33 | Displays all dependencies declared in root project 'HelloDaemon'.<p><i>Task group: help<i>
34 | <i>Task group: other<i>
35 | <i>Task group: other<i>
36 | Runs lint on all variants.<p><i>Task group: verification<i>
37 | <i>Task group: other<i>
38 | <i>Task group: other<i>
39 | <i>Task group: other<i>
40 | <i>Task group: other<i>
41 | Installs the 'archives' artifacts into the local Maven repository.<p><i>Task group: other<i>
42 | Assembles and tests this project and all projects it depends on.<p><i>Task group: build<i>
43 | <i>Task group: other<i>
44 | <i>Task group: other<i>
45 | Run unit tests for the release build.<p><i>Task group: verification<i>
46 | Deletes the build cache directory.<p><i>Task group: build<i>
47 | <i>Task group: other<i>
48 | <i>Task group: other<i>
49 | <i>Task group: other<i>
50 | <i>Task group: other<i>
51 | Displays the components produced by root project 'HelloDaemon'. [incubating]<p><i>Task group: help<i>
52 | <i>Task group: other<i>
53 | Assembles all Debug builds.<p><i>Task group: build<i>
54 | <i>Task group: other<i>
55 | <i>Task group: other<i>
56 | <i>Task group: other<i>
57 | <i>Task group: other<i>
58 | <i>Task group: other<i>
59 | <i>Task group: other<i>
60 | Extracts Android annotations for the debug variant into the archive file<p><i>Task group: build<i>
61 | <i>Task group: other<i>
62 | <i>Task group: other<i>
63 | <i>Task group: other<i>
64 | <i>Task group: other<i>
65 | <i>Task group: other<i>
66 | Uninstalls the Release build.<p><i>Task group: Install<i>
67 | <i>Task group: other<i>
68 | <i>Task group: other<i>
69 | <i>Task group: other<i>
70 | <i>Task group: other<i>
71 | <i>Task group: other<i>
72 | <i>Task group: other<i>
73 | <i>Task group: other<i>
74 | <i>Task group: other<i>
75 | <i>Task group: other<i>
76 | <i>Task group: other<i>
77 | <i>Task group: build<i>
78 | <i>Task group: other<i>
79 | <i>Task group: other<i>
80 | <i>Task group: other<i>
81 | Installs and runs instrumentation tests using all Device Providers.<p><i>Task group: verification<i>
82 | <i>Task group: other<i>
83 | <i>Task group: other<i>
84 | Uninstalls the android (on device) tests for the Debug build.<p><i>Task group: Install<i>
85 | Displays the insight into a specific dependency in root project 'HelloDaemon'.<p><i>Task group: help<i>
86 | <i>Task group: other<i>
87 | <i>Task group: other<i>
88 | <i>Task group: other<i>
89 | Assembles all variants of all applications and secondary packages.<p><i>Task group: build<i>
90 | <i>Task group: other<i>
91 | <i>Task group: other<i>
92 | <i>Task group: other<i>
93 | Displays a help message.<p><i>Task group: help<i>
94 | <i>Task group: build<i>
95 | Installs the android (on device) tests for the Debug build.<p><i>Task group: Install<i>
96 | <i>Task group: other<i>
97 | <i>Task group: other<i>
98 | <i>Task group: other<i>
99 | Runs all device checks on currently connected devices.<p><i>Task group: verification<i>
100 | Installs and runs the tests for debug on connected devices.<p><i>Task group: verification<i>
101 | Uninstall all applications.<p><i>Task group: Install<i>
102 | <i>Task group: other<i>
103 | Assembles all the Test applications.<p><i>Task group: build<i>
104 | <i>Task group: other<i>
105 | Extracts Android annotations for the release variant into the archive file<p><i>Task group: build<i>
106 | <i>Task group: other<i>
107 | <i>Task group: other<i>
108 | <i>Task group: other<i>
109 | <i>Task group: other<i>
110 | Publishes artifacts to bintray.com.<p><i>Task group: publishing<i>
111 | Generates Gradle wrapper files. [incubating]<p><i>Task group: Build Setup<i>
112 | <i>Task group: other<i>
113 | <i>Task group: other<i>
114 | <i>Task group: other<i>
115 | <i>Task group: other<i>
116 | <i>Task group: other<i>
117 | <i>Task group: other<i>
118 | Displays the Android dependencies of the project.<p><i>Task group: Android<i>
119 | <i>Task group: other<i>
120 | <i>Task group: build<i>
121 | <i>Task group: other<i>
122 | <i>Task group: other<i>
123 | <i>Task group: other<i>
124 | <i>Task group: other<i>
125 | <i>Task group: other<i>
126 | <i>Task group: other<i>
127 | <i>Task group: other<i>
128 | <i>Task group: other<i>
129 | <i>Task group: other<i>
130 | <i>Task group: other<i>
131 | <i>Task group: other<i>
132 | <i>Task group: other<i>
133 | <i>Task group: other<i>
134 | Run unit tests for all variants.<p><i>Task group: verification<i>
135 | <i>Task group: other<i>
136 | <i>Task group: other<i>
137 | <i>Task group: other<i>
138 | <i>Task group: other<i>
139 | <i>Task group: other<i>
140 | Runs all checks.<p><i>Task group: verification<i>
141 | <i>Task group: other<i>
142 | Run unit tests for the debug build.<p><i>Task group: verification<i>
143 | Installs and runs instrumentation tests for all flavors on connected devices.<p><i>Task group: verification<i>
144 | <i>Task group: other<i>
145 | <i>Task group: other<i>
146 | Installs the Debug build.<p><i>Task group: Install<i>
147 | <i>Task group: other<i>
148 | <i>Task group: other<i>
149 | Generates IDEA project file (IPR)<p><i>Task group: other<i>
150 | <i>Task group: build<i>
151 | <i>Task group: other<i>
152 | <i>Task group: other<i>
153 | <i>Task group: other<i>
154 | <i>Task group: other<i>
155 | Runs lint on the Release build.<p><i>Task group: verification<i>
156 | Assembles and tests this project and all projects that depend on it.<p><i>Task group: build<i>
157 | <i>Task group: other<i>
158 | Generates IDEA project files (IML, IPR, IWS)<p><i>Task group: IDE<i>
159 | Displays the dependent components of components in root project 'HelloDaemon'. [incubating]<p><i>Task group: help<i>
160 | Displays all buildscript dependencies declared in root project 'HelloDaemon'.<p><i>Task group: help<i>
161 | <i>Task group: other<i>
162 | <i>Task group: other<i>
163 | Prints out all the source sets defined in this project.<p><i>Task group: Android<i>
164 | <i>Task group: other<i>
165 | <i>Task group: other<i>
166 | <i>Task group: other<i>
167 | <i>Task group: other<i>
168 | <i>Task group: other<i>
169 | <i>Task group: other<i>
170 | Generates IDEA module files (IML)<p><i>Task group: other<i>
171 | Cleans IDEA project files (IML, IPR)<p><i>Task group: IDE<i>
172 | <i>Task group: other<i>
173 | Runs lint on just the fatal issues in the release build.<p><i>Task group: other<i>
174 | Generates an IDEA workspace file (IWS)<p><i>Task group: other<i>
175 | Uninstalls the Debug build.<p><i>Task group: Install<i>
176 | Displays the signing info for each variant.<p><i>Task group: Android<i>
177 | <i>Task group: other<i>
178 | <i>Task group: other<i>
179 | <i>Task group: other<i>
180 | <i>Task group: other<i>
181 | <i>Task group: other<i>
182 | Assembles and tests this project.<p><i>Task group: build<i>
183 | Assembles all Release builds.<p><i>Task group: build<i>
184 | <i>Task group: other<i>
185 | <i>Task group: other<i>
186 | <i>Task group: other<i>
187 | <i>Task group: other<i>
188 | <i>Task group: other<i>
189 | Displays the properties of root project 'HelloDaemon'.<p><i>Task group: help<i>
190 | <i>Task group: other<i>
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 | HelloDaemon
202 | https://opensource.org/licenses/MIT
203 | https://github.com/xingda920813/HelloDaemon
204 | 570396198@qq.com
205 | com.xdandroid
206 | 1.0.3
207 | hellodaemon
208 | xingda920813
209 | Da Xing
210 | hellodaemon
211 | The MIT License (MIT)
212 | [MIT]
213 | maven
214 | Android service daemon using JobScheduler.
215 | https://github.com/xingda920813/HelloDaemon.git
216 | <i>Task group: other<i>
217 | Displays the sub-projects of project ':hellodaemon'.<p><i>Task group: help<i>
218 | <i>Task group: other<i>
219 | <i>Task group: other<i>
220 | <i>Task group: other<i>
221 | <i>Task group: other<i>
222 | <i>Task group: other<i>
223 | Assembles a bundle containing the library in debug.<p><i>Task group: other<i>
224 | <i>Task group: other<i>
225 | <i>Task group: other<i>
226 | <i>Task group: other<i>
227 | Runs lint on the Debug build.<p><i>Task group: verification<i>
228 | Assembles a bundle containing the library in release.<p><i>Task group: other<i>
229 | <i>Task group: other<i>
230 | Creates a version of android.jar that's suitable for unit tests.<p><i>Task group: build<i>
231 | Displays the configuration model of project ':hellodaemon'. [incubating]<p><i>Task group: help<i>
232 | Displays the tasks runnable from project ':hellodaemon'.<p><i>Task group: help<i>
233 | <i>Task group: build<i>
234 | <i>Task group: other<i>
235 | <i>Task group: other<i>
236 | <i>Task group: other<i>
237 | <i>Task group: other<i>
238 | Runs all device checks using Device Providers and Test Servers.<p><i>Task group: verification<i>
239 | Displays all dependencies declared in project ':hellodaemon'.<p><i>Task group: help<i>
240 | <i>Task group: other<i>
241 | <i>Task group: other<i>
242 | Runs lint on all variants.<p><i>Task group: verification<i>
243 | <i>Task group: other<i>
244 | <i>Task group: other<i>
245 | <i>Task group: other<i>
246 | Installs the 'archives' artifacts into the local Maven repository.<p><i>Task group: other<i>
247 | Assembles and tests this project and all projects it depends on.<p><i>Task group: build<i>
248 | <i>Task group: other<i>
249 | <i>Task group: other<i>
250 | Run unit tests for the release build.<p><i>Task group: verification<i>
251 | Deletes the build cache directory.<p><i>Task group: build<i>
252 | <i>Task group: other<i>
253 | <i>Task group: other<i>
254 | <i>Task group: other<i>
255 | <i>Task group: other<i>
256 | Displays the components produced by project ':hellodaemon'. [incubating]<p><i>Task group: help<i>
257 | <i>Task group: other<i>
258 | Assembles all Debug builds.<p><i>Task group: build<i>
259 | <i>Task group: other<i>
260 | <i>Task group: other<i>
261 | <i>Task group: other<i>
262 | <i>Task group: other<i>
263 | <i>Task group: other<i>
264 | Extracts Android annotations for the debug variant into the archive file<p><i>Task group: build<i>
265 | <i>Task group: other<i>
266 | <i>Task group: other<i>
267 | <i>Task group: other<i>
268 | <i>Task group: other<i>
269 | <i>Task group: other<i>
270 | <i>Task group: other<i>
271 | <i>Task group: other<i>
272 | <i>Task group: other<i>
273 | <i>Task group: other<i>
274 | <i>Task group: other<i>
275 | <i>Task group: other<i>
276 | <i>Task group: other<i>
277 | <i>Task group: other<i>
278 | <i>Task group: other<i>
279 | <i>Task group: build<i>
280 | <i>Task group: other<i>
281 | <i>Task group: other<i>
282 | <i>Task group: other<i>
283 | Installs and runs instrumentation tests using all Device Providers.<p><i>Task group: verification<i>
284 | <i>Task group: other<i>
285 | <i>Task group: other<i>
286 | Uninstalls the android (on device) tests for the Debug build.<p><i>Task group: Install<i>
287 | Displays the insight into a specific dependency in project ':hellodaemon'.<p><i>Task group: help<i>
288 | <i>Task group: other<i>
289 | <i>Task group: other<i>
290 | <i>Task group: other<i>
291 | Assembles all variants of all applications and secondary packages.<p><i>Task group: build<i>
292 | <i>Task group: other<i>
293 | <i>Task group: other<i>
294 | Displays a help message.<p><i>Task group: help<i>
295 | <i>Task group: build<i>
296 | Installs the android (on device) tests for the Debug build.<p><i>Task group: Install<i>
297 | <i>Task group: other<i>
298 | <i>Task group: other<i>
299 | <i>Task group: other<i>
300 | Runs all device checks on currently connected devices.<p><i>Task group: verification<i>
301 | Installs and runs the tests for debug on connected devices.<p><i>Task group: verification<i>
302 | Uninstall all applications.<p><i>Task group: Install<i>
303 | <i>Task group: other<i>
304 | Assembles all the Test applications.<p><i>Task group: build<i>
305 | <i>Task group: other<i>
306 | Extracts Android annotations for the release variant into the archive file<p><i>Task group: build<i>
307 | <i>Task group: other<i>
308 | Publishes artifacts to bintray.com.<p><i>Task group: publishing<i>
309 | <i>Task group: other<i>
310 | <i>Task group: other<i>
311 | <i>Task group: other<i>
312 | <i>Task group: other<i>
313 | <i>Task group: other<i>
314 | <i>Task group: other<i>
315 | Displays the Android dependencies of the project.<p><i>Task group: Android<i>
316 | <i>Task group: other<i>
317 | <i>Task group: build<i>
318 | <i>Task group: other<i>
319 | <i>Task group: other<i>
320 | <i>Task group: other<i>
321 | <i>Task group: other<i>
322 | <i>Task group: other<i>
323 | <i>Task group: other<i>
324 | <i>Task group: other<i>
325 | <i>Task group: other<i>
326 | <i>Task group: other<i>
327 | <i>Task group: other<i>
328 | <i>Task group: other<i>
329 | <i>Task group: other<i>
330 | Run unit tests for all variants.<p><i>Task group: verification<i>
331 | <i>Task group: other<i>
332 | <i>Task group: other<i>
333 | <i>Task group: other<i>
334 | <i>Task group: other<i>
335 | <i>Task group: other<i>
336 | Runs all checks.<p><i>Task group: verification<i>
337 | <i>Task group: other<i>
338 | Run unit tests for the debug build.<p><i>Task group: verification<i>
339 | Installs and runs instrumentation tests for all flavors on connected devices.<p><i>Task group: verification<i>
340 | <i>Task group: other<i>
341 | <i>Task group: other<i>
342 | <i>Task group: build<i>
343 | <i>Task group: other<i>
344 | <i>Task group: other<i>
345 | <i>Task group: other<i>
346 | <i>Task group: other<i>
347 | Runs lint on the Release build.<p><i>Task group: verification<i>
348 | Assembles and tests this project and all projects that depend on it.<p><i>Task group: build<i>
349 | <i>Task group: other<i>
350 | Generates IDEA project files (IML, IPR, IWS)<p><i>Task group: IDE<i>
351 | Displays the dependent components of components in project ':hellodaemon'. [incubating]<p><i>Task group: help<i>
352 | Displays all buildscript dependencies declared in project ':hellodaemon'.<p><i>Task group: help<i>
353 | <i>Task group: other<i>
354 | <i>Task group: other<i>
355 | Prints out all the source sets defined in this project.<p><i>Task group: Android<i>
356 | <i>Task group: other<i>
357 | <i>Task group: other<i>
358 | <i>Task group: other<i>
359 | <i>Task group: other<i>
360 | <i>Task group: other<i>
361 | <i>Task group: other<i>
362 | Generates IDEA module files (IML)<p><i>Task group: other<i>
363 | Cleans IDEA project files (IML, IPR)<p><i>Task group: IDE<i>
364 | <i>Task group: other<i>
365 | Displays the signing info for each variant.<p><i>Task group: Android<i>
366 | <i>Task group: other<i>
367 | <i>Task group: other<i>
368 | Deletes the build directory.<p><i>Task group: build<i>
369 | <i>Task group: other<i>
370 | <i>Task group: other<i>
371 | Assembles and tests this project.<p><i>Task group: build<i>
372 | Assembles all Release builds.<p><i>Task group: build<i>
373 | <i>Task group: other<i>
374 | <i>Task group: other<i>
375 | <i>Task group: other<i>
376 | <i>Task group: other<i>
377 | <i>Task group: other<i>
378 | Displays the properties of project ':hellodaemon'.<p><i>Task group: help<i>
379 | ## Internal use, do not manually configure ##
380 | ## Internal use, do not manually configure ##
381 | ## Internal use, do not manually configure ##
382 | ## Internal use, do not manually configure ##
383 | ## Internal use, do not manually configure ##
384 | ## Internal use, do not manually configure ##
385 | ## Internal use, do not manually configure ##
386 | ## Internal use, do not manually configure ##
387 | ## Internal use, do not manually configure ##
388 | ## Internal use, do not manually configure ##
389 | ## Internal use, do not manually configure ##
390 | ## Internal use, do not manually configure ##
391 | ## Internal use, do not manually configure ##
392 | ## Internal use, do not manually configure ##
393 | ## Internal use, do not manually configure ##
394 | ## Internal use, do not manually configure ##
395 | ## Internal use, do not manually configure ##
396 | ## Internal use, do not manually configure ##
397 | ## Internal use, do not manually configure ##
398 | ## Internal use, do not manually configure ##
399 | The Jacoco agent to use to get coverage data.
400 | The Jacoco ant tasks to use to get execute Gradle tasks.
401 | Classpath for the annotation processor for 'androidTest'.
402 | Classpath for compiling the androidTest sources.
403 | Classpath for the 'androidTest' Jack plugins.
404 | Classpath for only compiling the androidTest sources.
405 | Classpath only used when publishing 'androidTest'.
406 | Link to a wear app to embed for object 'androidTest'.
407 | Classpath for the annotation processor for 'main'.
408 | Configuration for archive artifacts.
409 | Classpath for compiling the main sources.
410 | Classpath for the annotation processor for 'debug'.
411 | Classpath for compiling the debug sources.
412 | Classpath for the 'debug' Jack plugins.
413 | Classpath for only compiling the debug sources.
414 | Classpath only used when publishing 'debug'.
415 | Link to a wear app to embed for object 'debug'.
416 | Configuration for default artifacts.
417 | Configuration for default mapping artifacts.
418 | Metadata for the produced APKs.
419 | Classpath for the 'main' Jack plugins.
420 | Classpath for only compiling the main sources.
421 | Classpath only used when publishing 'main'.
422 | Classpath for the annotation processor for 'release'.
423 | Classpath for compiling the release sources.
424 | Classpath for the 'release' Jack plugins.
425 | Classpath for only compiling the release sources.
426 | Classpath only used when publishing 'release'.
427 | Link to a wear app to embed for object 'release'.
428 | Classpath for the annotation processor for 'test'.
429 | Classpath for compiling the test sources.
430 | Classpath for the annotation processor for 'testDebug'.
431 | Classpath for compiling the testDebug sources.
432 | Classpath for the 'testDebug' Jack plugins.
433 | Classpath for only compiling the testDebug sources.
434 | Classpath only used when publishing 'testDebug'.
435 | Link to a wear app to embed for object 'testDebug'.
436 | Classpath for the 'test' Jack plugins.
437 | Classpath for only compiling the test sources.
438 | Classpath only used when publishing 'test'.
439 | Classpath for the annotation processor for 'testRelease'.
440 | Classpath for compiling the testRelease sources.
441 | Classpath for the 'testRelease' Jack plugins.
442 | Classpath for only compiling the testRelease sources.
443 | Classpath only used when publishing 'testRelease'.
444 | Link to a wear app to embed for object 'testRelease'.
445 | Link to a wear app to embed for object 'test'.
446 | Link to a wear app to embed for object 'main'.
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 | Displays the sub-projects of project ':sample'.<p><i>Task group: help<i>
457 | <i>Task group: other<i>
458 | <i>Task group: other<i>
459 | <i>Task group: other<i>
460 | <i>Task group: other<i>
461 | <i>Task group: other<i>
462 | Runs lint on the Debug build.<p><i>Task group: verification<i>
463 | <i>Task group: other<i>
464 | Creates a version of android.jar that's suitable for unit tests.<p><i>Task group: build<i>
465 | Displays the configuration model of project ':sample'. [incubating]<p><i>Task group: help<i>
466 | Displays the tasks runnable from project ':sample'.<p><i>Task group: help<i>
467 | <i>Task group: build<i>
468 | <i>Task group: other<i>
469 | Runs all device checks using Device Providers and Test Servers.<p><i>Task group: verification<i>
470 | Displays all dependencies declared in project ':sample'.<p><i>Task group: help<i>
471 | <i>Task group: other<i>
472 | <i>Task group: other<i>
473 | Runs lint on all variants.<p><i>Task group: verification<i>
474 | <i>Task group: other<i>
475 | <i>Task group: other<i>
476 | <i>Task group: other<i>
477 | <i>Task group: other<i>
478 | Assembles and tests this project and all projects it depends on.<p><i>Task group: build<i>
479 | <i>Task group: other<i>
480 | <i>Task group: other<i>
481 | Run unit tests for the release build.<p><i>Task group: verification<i>
482 | Deletes the build cache directory.<p><i>Task group: build<i>
483 | <i>Task group: other<i>
484 | <i>Task group: other<i>
485 | <i>Task group: other<i>
486 | Displays the components produced by project ':sample'. [incubating]<p><i>Task group: help<i>
487 | <i>Task group: other<i>
488 | Assembles all Debug builds.<p><i>Task group: build<i>
489 | <i>Task group: other<i>
490 | <i>Task group: other<i>
491 | <i>Task group: other<i>
492 | <i>Task group: other<i>
493 | <i>Task group: other<i>
494 | <i>Task group: other<i>
495 | <i>Task group: other<i>
496 | <i>Task group: other<i>
497 | <i>Task group: other<i>
498 | Uninstalls the Release build.<p><i>Task group: Install<i>
499 | <i>Task group: other<i>
500 | <i>Task group: other<i>
501 | <i>Task group: other<i>
502 | <i>Task group: other<i>
503 | <i>Task group: other<i>
504 | <i>Task group: other<i>
505 | <i>Task group: other<i>
506 | <i>Task group: build<i>
507 | <i>Task group: other<i>
508 | <i>Task group: other<i>
509 | <i>Task group: other<i>
510 | Installs and runs instrumentation tests using all Device Providers.<p><i>Task group: verification<i>
511 | <i>Task group: other<i>
512 | <i>Task group: other<i>
513 | Uninstalls the android (on device) tests for the Debug build.<p><i>Task group: Install<i>
514 | Displays the insight into a specific dependency in project ':sample'.<p><i>Task group: help<i>
515 | <i>Task group: other<i>
516 | <i>Task group: other<i>
517 | <i>Task group: other<i>
518 | Assembles all variants of all applications and secondary packages.<p><i>Task group: build<i>
519 | <i>Task group: other<i>
520 | <i>Task group: other<i>
521 | Displays a help message.<p><i>Task group: help<i>
522 | <i>Task group: build<i>
523 | Installs the android (on device) tests for the Debug build.<p><i>Task group: Install<i>
524 | <i>Task group: other<i>
525 | <i>Task group: other<i>
526 | <i>Task group: other<i>
527 | Runs all device checks on currently connected devices.<p><i>Task group: verification<i>
528 | Installs and runs the tests for debug on connected devices.<p><i>Task group: verification<i>
529 | Uninstall all applications.<p><i>Task group: Install<i>
530 | <i>Task group: other<i>
531 | Assembles all the Test applications.<p><i>Task group: build<i>
532 | <i>Task group: other<i>
533 | <i>Task group: other<i>
534 | <i>Task group: other<i>
535 | <i>Task group: other<i>
536 | <i>Task group: other<i>
537 | <i>Task group: other<i>
538 | <i>Task group: other<i>
539 | <i>Task group: other<i>
540 | <i>Task group: other<i>
541 | <i>Task group: other<i>
542 | Displays the Android dependencies of the project.<p><i>Task group: Android<i>
543 | <i>Task group: other<i>
544 | <i>Task group: build<i>
545 | <i>Task group: other<i>
546 | <i>Task group: other<i>
547 | <i>Task group: other<i>
548 | <i>Task group: other<i>
549 | <i>Task group: other<i>
550 | <i>Task group: other<i>
551 | <i>Task group: other<i>
552 | <i>Task group: other<i>
553 | <i>Task group: other<i>
554 | <i>Task group: other<i>
555 | <i>Task group: other<i>
556 | <i>Task group: other<i>
557 | Run unit tests for all variants.<p><i>Task group: verification<i>
558 | <i>Task group: other<i>
559 | <i>Task group: other<i>
560 | <i>Task group: other<i>
561 | <i>Task group: other<i>
562 | <i>Task group: other<i>
563 | Runs all checks.<p><i>Task group: verification<i>
564 | <i>Task group: other<i>
565 | Run unit tests for the debug build.<p><i>Task group: verification<i>
566 | Installs and runs instrumentation tests for all flavors on connected devices.<p><i>Task group: verification<i>
567 | <i>Task group: other<i>
568 | <i>Task group: other<i>
569 | Installs the Debug build.<p><i>Task group: Install<i>
570 | <i>Task group: other<i>
571 | <i>Task group: other<i>
572 | <i>Task group: build<i>
573 | <i>Task group: other<i>
574 | <i>Task group: other<i>
575 | <i>Task group: other<i>
576 | <i>Task group: other<i>
577 | Runs lint on the Release build.<p><i>Task group: verification<i>
578 | Assembles and tests this project and all projects that depend on it.<p><i>Task group: build<i>
579 | <i>Task group: other<i>
580 | Generates IDEA project files (IML, IPR, IWS)<p><i>Task group: IDE<i>
581 | Displays the dependent components of components in project ':sample'. [incubating]<p><i>Task group: help<i>
582 | Displays all buildscript dependencies declared in project ':sample'.<p><i>Task group: help<i>
583 | <i>Task group: other<i>
584 | <i>Task group: other<i>
585 | Prints out all the source sets defined in this project.<p><i>Task group: Android<i>
586 | <i>Task group: other<i>
587 | <i>Task group: other<i>
588 | <i>Task group: other<i>
589 | <i>Task group: other<i>
590 | <i>Task group: other<i>
591 | Generates IDEA module files (IML)<p><i>Task group: other<i>
592 | Cleans IDEA project files (IML, IPR)<p><i>Task group: IDE<i>
593 | <i>Task group: other<i>
594 | Runs lint on just the fatal issues in the release build.<p><i>Task group: other<i>
595 | Uninstalls the Debug build.<p><i>Task group: Install<i>
596 | Displays the signing info for each variant.<p><i>Task group: Android<i>
597 | <i>Task group: other<i>
598 | <i>Task group: other<i>
599 | Deletes the build directory.<p><i>Task group: build<i>
600 | <i>Task group: other<i>
601 | <i>Task group: other<i>
602 | Assembles and tests this project.<p><i>Task group: build<i>
603 | Assembles all Release builds.<p><i>Task group: build<i>
604 | <i>Task group: other<i>
605 | <i>Task group: other<i>
606 | <i>Task group: other<i>
607 | <i>Task group: other<i>
608 | <i>Task group: other<i>
609 | Displays the properties of project ':sample'.<p><i>Task group: help<i>
610 | ## Internal use, do not manually configure ##
611 | ## Internal use, do not manually configure ##
612 | ## Internal use, do not manually configure ##
613 | ## Internal use, do not manually configure ##
614 | ## Internal use, do not manually configure ##
615 | ## Internal use, do not manually configure ##
616 | ## Internal use, do not manually configure ##
617 | ## Internal use, do not manually configure ##
618 | ## Internal use, do not manually configure ##
619 | ## Internal use, do not manually configure ##
620 | ## Internal use, do not manually configure ##
621 | ## Internal use, do not manually configure ##
622 | ## Internal use, do not manually configure ##
623 | ## Internal use, do not manually configure ##
624 | ## Internal use, do not manually configure ##
625 | ## Internal use, do not manually configure ##
626 | ## Internal use, do not manually configure ##
627 | ## Internal use, do not manually configure ##
628 | ## Internal use, do not manually configure ##
629 | ## Internal use, do not manually configure ##
630 | The Jacoco agent to use to get coverage data.
631 | The Jacoco ant tasks to use to get execute Gradle tasks.
632 | Classpath for the annotation processor for 'androidTest'.
633 | Classpath packaged with the compiled 'androidTest' classes.
634 | Classpath for compiling the androidTest sources.
635 | Classpath for the 'androidTest' Jack plugins.
636 | Classpath for only compiling the androidTest sources.
637 | Link to a wear app to embed for object 'androidTest'.
638 | Classpath for the annotation processor for 'main'.
639 | Classpath packaged with the compiled 'main' classes.
640 | Configuration for archive artifacts.
641 | Classpath for compiling the main sources.
642 | Classpath for the annotation processor for 'debug'.
643 | Classpath packaged with the compiled 'debug' classes.
644 | Classpath for compiling the debug sources.
645 | Classpath for the 'debug' Jack plugins.
646 | Classpath for only compiling the debug sources.
647 | Link to a wear app to embed for object 'debug'.
648 | Configuration for default artifacts.
649 | Configuration for default mapping artifacts.
650 | Metadata for the produced APKs.
651 | Classpath for the 'main' Jack plugins.
652 | Classpath for only compiling the main sources.
653 | Classpath for the annotation processor for 'release'.
654 | Classpath packaged with the compiled 'release' classes.
655 | Classpath for compiling the release sources.
656 | Classpath for the 'release' Jack plugins.
657 | Classpath for only compiling the release sources.
658 | Link to a wear app to embed for object 'release'.
659 | Classpath for the annotation processor for 'test'.
660 | Classpath packaged with the compiled 'test' classes.
661 | Classpath for compiling the test sources.
662 | Classpath for the annotation processor for 'testDebug'.
663 | Classpath packaged with the compiled 'testDebug' classes.
664 | Classpath for compiling the testDebug sources.
665 | Classpath for the 'testDebug' Jack plugins.
666 | Classpath for only compiling the testDebug sources.
667 | Link to a wear app to embed for object 'testDebug'.
668 | Classpath for the 'test' Jack plugins.
669 | Classpath for only compiling the test sources.
670 | Classpath for the annotation processor for 'testRelease'.
671 | Classpath packaged with the compiled 'testRelease' classes.
672 | Classpath for compiling the testRelease sources.
673 | Classpath for the 'testRelease' Jack plugins.
674 | Classpath for only compiling the testRelease sources.
675 | Link to a wear app to embed for object 'testRelease'.
676 | Link to a wear app to embed for object 'test'.
677 | Link to a wear app to embed for object 'main'.
678 |
679 |
680 |
681 |
682 |
683 |
684 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/.idea/inspectionProfiles/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 | Android
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Da Xing
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HelloDaemon
2 |
3 |
4 |
5 | ### Android 服务保活/常驻 (Android Service Daemon)
6 |
7 | #### 建议只在App的核心功能需要保活/常驻时使用。
8 |
9 | #### 本示例中使用的保活方法部分来源于下面的博客和库。启动前台服务而不显示通知来自于D-clock的AndroidDaemonService,对其他的一些非native层保活方法进行了实现。
10 |
11 | [Android 进程常驻(2)----细数利用android系统机制的保活手段](http://blog.csdn.net/marswin89/article/details/50890708)
12 |
13 | [D-clock / AndroidDaemonService](https://github.com/D-clock/AndroidDaemonService)
14 |
15 | ## 实现了上面 2 个链接中的大多数保活思路 :
16 |
17 | #### 1. 将Service设置为前台服务而不显示通知
18 |
19 | > D-clock :
20 | >
21 | 思路一:API < 18,启动前台Service时直接传入new Notification();
22 | >
23 | 思路二:API >= 18,同时启动两个id相同的前台Service,然后再将后启动的Service做stop处理;
24 |
25 | //启动前台服务而不显示通知的漏洞已在 API Level 25 修复,大快人心!
26 |
27 | 前台服务相对于后台服务的优势,除了优先级的提升以外,还有一点:
28 |
29 | 在最近任务列表中划掉卡片时,前台服务不会停止;
30 |
31 | (更新:经过测试,发现只是对于AOSP/CM/国际上对Framework层改动较小的Android系统是成立的;EMUI/MIUI等未加入白名单的情况下,划掉卡片,前台服务也会停止;加入白名单后划掉卡片的行为与国际厂商的系统相似。)
32 |
33 | 而后台服务会停止,并在稍后重新启动(onStartCommand 返回 START_STICKY 时)。
34 |
35 | 前台服务和后台服务被划掉卡片时,回调的都是 onTaskRemoved 方法。
36 |
37 | onDestroy 方法只在 设置 -> 开发者选项 -> 正在运行的服务 里停止服务时才会回调。
38 |
39 | #### 2. 在 Service 的 onStartCommand 方法里返回 START_STICKY
40 |
41 | #### 3. 覆盖 Service 的 onDestroy/onTaskRemoved 方法, 保存数据到磁盘, 然后重新拉起服务
42 |
43 | #### 4. 监听 8 种系统广播 :
44 |
45 | CONNECTIVITY\_CHANGE, USER\_PRESENT, ACTION\_POWER\_CONNECTED, ACTION\_POWER\_DISCONNECTED, BOOT\_COMPLETED, PACKAGE\_ADDED, PACKAGE\_REMOVED.
46 |
47 | 在网络连接改变, 用户屏幕解锁, 电源连接 / 断开, 系统启动完成, 安装 / 卸载软件包时拉起 Service.
48 |
49 | Service 内部做了判断,若 Service 已在运行,不会重复启动.
50 |
51 | #### 5. 开启守护服务 : 定时检查服务是否在运行,如果不在运行就拉起来
52 |
53 | #### 6. 守护 Service 组件的启用状态, 使其不被 MAT 等工具禁用
54 |
55 | 详见上面的 2 个链接。
56 |
57 | ## 增加实现 :
58 |
59 | #### \+ 守护服务 : Android 5.0 及以上版本使用 JobScheduler,效果比 AlarmManager 好
60 |
61 | 使用 JobScheduler, Android 系统能自动拉起被 Force Stop 的 Package,而 AlarmManager 无法拉起.
62 |
63 | Android 4.4 及以下版本使用 AlarmManager.
64 |
65 | #### \+ 使用定时 Observable : 避免 Android 定制系统 JobScheduler / AlarmManager 唤醒间隔不稳定的情况
66 |
67 | #### \+ 增加停止服务并取消定时唤醒的快捷方法
68 |
69 | #### \+ 增加在不需要服务运行时取消 Job / Alarm / Subscription 的快捷方法 (广播 Action)
70 |
71 | #### \+ 增强对国产机型的适配 : 防止华为机型按返回键回到桌面再锁屏后几秒钟进程被杀
72 |
73 | 测试机型 : 华为 荣耀6 Plus (EMUI 4.0 Android 6.0), 应用未加入白名单.
74 |
75 | >
76 | 观察到 :
77 | >
78 | 在未加入白名单的情况下,按Back键回到桌面再锁屏后几秒钟即会杀掉进程;
79 | >
80 | 但是按Home键返回桌面的话,即使锁屏,也不会杀掉进程。
81 |
82 | (更新:经过测试,在EMUI系统上,『即使锁屏,也不会杀掉进程』只对App的卡片还在多任务屏幕的第一屏时有效,一旦被挤到第二页及以后,锁屏后几秒钟即会杀掉进程;加入白名单后,回到桌面再锁屏后不会杀进程。)
83 |
84 | 因此,重写了onBackPressed方法,使其只是返回到桌面,而不是将当前Activity finish/destroy掉。
85 |
86 | 测试机型 : 红米1S 4G (MIUI 8 Android 4.4.2), 应用未加入白名单.
87 |
88 | >
89 | 观察到 :
90 | >
91 | 在未加入白名单的情况下,回到桌面再锁屏后不会杀进程;
92 | >
93 | 但划掉卡片,进程死亡并不再启动;加入白名单后,划掉卡片,服务不会停止,与CM的行为相似。
94 |
95 | 可以看出,若不想使用Native保活,引导用户加入白名单可能是比较可行的方法。
96 |
97 | #### \+ 用 Intent 跳转
98 |
99 | - Android Doze 模式
100 | - 华为 自启管理
101 | - 华为 锁屏清理
102 | - 小米 自启动管理
103 | - 小米 神隐模式
104 | - 三星 5.0/5.1 自启动应用程序管理
105 | - 三星 6.0+ 未监视的应用程序管理
106 | - 魅族 自启动管理
107 | - 魅族 待机耗电管理
108 | - Oppo 自启动管理
109 | - Vivo 后台高耗电
110 | - 金立 应用自启
111 | - 金立 绿色后台
112 | - 乐视 自启动管理
113 | - 乐视 应用保护
114 | - 酷派 自启动管理
115 | - 联想 后台管理
116 | - 联想 后台耗电优化
117 | - 中兴 自启管理
118 | - 中兴 锁屏加速受保护应用
119 |
120 | 配合 android.support.v7.AlertDialog 引导用户将 App 加入白名单.
121 |
122 | #### \+ 守护服务和BroadcastReceiver运行在:watch子进程中,与主进程分离
123 |
124 | #### \+ 工作服务运行在主进程中,免去与服务通信需使用AIDL或其他IPC方式的麻烦
125 |
126 | 参考了 Poweramp, 启动的前台服务与 UI 运行在同一进程中。
127 |
128 | #### \+ 做了防止重复启动Service的处理,可以任意调用startService(Intent i)
129 |
130 | 若服务还在运行,就什么也不做;若服务不在运行就拉起来。
131 |
132 | #### \+ 在子线程中运行定时任务,处理了运行前检查和销毁时保存的问题
133 |
134 | 开始任务前,先检查磁盘中是否有上次销毁时保存的数据;定期将数据保存到磁盘。
135 |
136 | ## 引入
137 |
138 | ### 1. 添加二进制
139 |
140 | build.gradle 中添加
141 |
142 | ```
143 | compile 'com.xdandroid:hellodaemon:+'
144 | ```
145 |
146 | ### 2. 继承 AbsWorkService, 实现 6 个抽象方法
147 |
148 | ```
149 | /**
150 | * 是否 任务完成, 不再需要服务运行?
151 | * @return 应当停止服务, true; 应当启动服务, false; 无法判断, null.
152 | */
153 | Boolean shouldStopService();
154 |
155 | /**
156 | * 任务是否正在运行?
157 | * @return 任务正在运行, true; 任务当前不在运行, false; 无法判断, null.
158 | */
159 | Boolean isWorkRunning();
160 |
161 | void startWork();
162 |
163 | void stopWork();
164 |
165 | //Service.onBind(Intent intent)
166 | @Nullable IBinder onBind(Intent intent, Void unused);
167 |
168 | //服务被杀时调用, 可以在这里面保存数据.
169 | void onServiceKilled();
170 | ```
171 |
172 | 别忘了在 Manifest 中注册这个 Service.
173 |
174 | ### 3. 自定义 Application
175 |
176 | 在 Application 的 `onCreate()` 中, 调用
177 |
178 | ```
179 | DaemonEnv.initialize(
180 | Context app, //Application Context.
181 | Class extends AbsWorkService> serviceClass, //刚才创建的 Service 对应的 Class 对象.
182 | @Nullable Integer wakeUpInterval); //定时唤醒的时间间隔(ms), 默认 6 分钟.
183 |
184 | Context.startService(new Intent(Context app, Class extends AbsWorkService> serviceClass));
185 | ```
186 |
187 | 别忘了在 Manifest 中通过 android:name 使用这个自定义的 Application.
188 |
189 | ### 4. API 说明
190 |
191 | #### 启动 Service:
192 |
193 | ```
194 | Context.startService(new Intent(Context c, Class extends AbsWorkService> serviceClass))
195 | ```
196 |
197 | #### 停止 Service:
198 |
199 | 在 ? extends AbsWorkService 中, 添加 `stopService()` 方法:
200 |
201 | 1.操作自己维护的 flag, 使 `shouldStopService()` 返回 `true`;
202 |
203 | 2.调用自己的方法或第三方 SDK 提供的 API, 停止任务;
204 |
205 | 3.调用 ```AbsWorkService.cancelJobAlarmSub()``` 取消 Job / Alarm / Subscription.
206 |
207 | 需要停止服务时, 调用 ? extends AbsWorkService 上的 `stopService()` 即可.
208 |
209 | #### 处理白名单:
210 |
211 | 以下 API 全部位于 IntentWrapper 中:
212 |
213 | ```
214 | List getIntentWrapperList();
215 |
216 | //弹出 android.support.v7.AlertDialog, 引导用户将 App 加入白名单.
217 | void whiteListMatters(Activity a, String reason);
218 |
219 | //防止华为机型未加入白名单时按返回键回到桌面再锁屏后几秒钟进程被杀.
220 | //重写 MainActivity.onBackPressed(), 只保留对以下 API 的调用.
221 | void onBackPressed(Activity a);
222 | ```
223 |
224 | #### 为节省用户的电量, 当不再需要服务运行时, 可以调用 ```AbsWorkService.cancelJobAlarmSub()``` 取消定时唤醒的 Job / Alarm / Subscription, 并调用 `stopService()` 停止服务.
225 |
226 | 详见代码及注释。
227 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 | }
8 | dependencies {
9 | classpath 'com.android.tools.build:gradle:3.0.1'
10 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.4'
11 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.4.1'
12 | }
13 | }
14 |
15 | allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | task clean(type: Delete) {
23 | delete rootProject.buildDir
24 | }
25 |
26 | task javadoc(type: Javadoc) {
27 | options.encoding = "utf-8"
28 | }
29 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Dfile.encoding=UTF-8
2 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingda920813/HelloDaemon/e57d51a20c57610b37ad50c097315fac988b97fd/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
2 | distributionBase=GRADLE_USER_HOME
3 | distributionPath=wrapper/dists
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
10 | DEFAULT_JVM_OPTS=""
11 |
12 | APP_NAME="Gradle"
13 | APP_BASE_NAME=`basename "$0"`
14 |
15 | # Use the maximum available, or set MAX_FD != -1 to use that value.
16 | MAX_FD="maximum"
17 |
18 | warn ( ) {
19 | echo "$*"
20 | }
21 |
22 | die ( ) {
23 | echo
24 | echo "$*"
25 | echo
26 | exit 1
27 | }
28 |
29 | # OS specific support (must be 'true' or 'false').
30 | cygwin=false
31 | msys=false
32 | darwin=false
33 | case "`uname`" in
34 | CYGWIN* )
35 | cygwin=true
36 | ;;
37 | Darwin* )
38 | darwin=true
39 | ;;
40 | MINGW* )
41 | msys=true
42 | ;;
43 | esac
44 |
45 | # Attempt to set APP_HOME
46 | # Resolve links: $0 may be a link
47 | PRG="$0"
48 | # Need this for relative symlinks.
49 | while [ -h "$PRG" ] ; do
50 | ls=`ls -ld "$PRG"`
51 | link=`expr "$ls" : '.*-> \(.*\)$'`
52 | if expr "$link" : '/.*' > /dev/null; then
53 | PRG="$link"
54 | else
55 | PRG=`dirname "$PRG"`"/$link"
56 | fi
57 | done
58 | SAVED="`pwd`"
59 | cd "`dirname \"$PRG\"`/" >/dev/null
60 | APP_HOME="`pwd -P`"
61 | cd "$SAVED" >/dev/null
62 |
63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
64 |
65 | # Determine the Java command to use to start the JVM.
66 | if [ -n "$JAVA_HOME" ] ; then
67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
68 | # IBM's JDK on AIX uses strange locations for the executables
69 | JAVACMD="$JAVA_HOME/jre/sh/java"
70 | else
71 | JAVACMD="$JAVA_HOME/bin/java"
72 | fi
73 | if [ ! -x "$JAVACMD" ] ; then
74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
75 |
76 | Please set the JAVA_HOME variable in your environment to match the
77 | location of your Java installation."
78 | fi
79 | else
80 | JAVACMD="java"
81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
82 |
83 | Please set the JAVA_HOME variable in your environment to match the
84 | location of your Java installation."
85 | fi
86 |
87 | # Increase the maximum file descriptors if we can.
88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
89 | MAX_FD_LIMIT=`ulimit -H -n`
90 | if [ $? -eq 0 ] ; then
91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
92 | MAX_FD="$MAX_FD_LIMIT"
93 | fi
94 | ulimit -n $MAX_FD
95 | if [ $? -ne 0 ] ; then
96 | warn "Could not set maximum file descriptor limit: $MAX_FD"
97 | fi
98 | else
99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
100 | fi
101 | fi
102 |
103 | # For Darwin, add options to specify how the application appears in the dock
104 | if $darwin; then
105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
106 | fi
107 |
108 | # For Cygwin, switch paths to Windows format before running java
109 | if $cygwin ; then
110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
112 | JAVACMD=`cygpath --unix "$JAVACMD"`
113 |
114 | # We build the pattern for arguments to be converted via cygpath
115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
116 | SEP=""
117 | for dir in $ROOTDIRSRAW ; do
118 | ROOTDIRS="$ROOTDIRS$SEP$dir"
119 | SEP="|"
120 | done
121 | OURCYGPATTERN="(^($ROOTDIRS))"
122 | # Add a user-defined pattern to the cygpath arguments
123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
125 | fi
126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
127 | i=0
128 | for arg in "$@" ; do
129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
131 |
132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
134 | else
135 | eval `echo args$i`="\"$arg\""
136 | fi
137 | i=$((i+1))
138 | done
139 | case $i in
140 | (0) set -- ;;
141 | (1) set -- "$args0" ;;
142 | (2) set -- "$args0" "$args1" ;;
143 | (3) set -- "$args0" "$args1" "$args2" ;;
144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
150 | esac
151 | fi
152 |
153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
154 | function splitJvmOpts() {
155 | JVM_OPTS=("$@")
156 | }
157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
159 |
160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
161 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
12 | set DEFAULT_JVM_OPTS=
13 |
14 | set DIRNAME=%~dp0
15 | if "%DIRNAME%" == "" set DIRNAME=.
16 | set APP_BASE_NAME=%~n0
17 | set APP_HOME=%DIRNAME%
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windowz variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 | if "%@eval[2+2]" == "4" goto 4NT_args
53 |
54 | :win9xME_args
55 | @rem Slurp the command line arguments.
56 | set CMD_LINE_ARGS=
57 | set _SKIP=2
58 |
59 | :win9xME_args_slurp
60 | if "x%~1" == "x" goto execute
61 |
62 | set CMD_LINE_ARGS=%*
63 | goto execute
64 |
65 | :4NT_args
66 | @rem Get arguments from the 4NT Shell from JP Software
67 | set CMD_LINE_ARGS=%$
68 |
69 | :execute
70 | @rem Setup the command line
71 |
72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
73 |
74 | @rem Execute Gradle
75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
76 |
77 | :end
78 | @rem End local scope for the variables with windows NT shell
79 | if "%ERRORLEVEL%"=="0" goto mainEnd
80 |
81 | :fail
82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
83 | rem the _cmd.exe /c_ return code!
84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
85 | exit /b 1
86 |
87 | :mainEnd
88 | if "%OS%"=="Windows_NT" endlocal
89 |
90 | :omega
91 |
--------------------------------------------------------------------------------
/hellodaemon/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/hellodaemon/bintrayv1.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.jfrog.bintray'
2 |
3 | version = libraryVersion
4 |
5 | if (project.hasProperty("android")) { // Android libraries
6 | task sourcesJar(type: Jar) {
7 | classifier = 'sources'
8 | from android.sourceSets.main.java.srcDirs
9 | }
10 |
11 | task javadoc(type: Javadoc) {
12 | source = android.sourceSets.main.java.srcDirs
13 | classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
14 | options.encoding = "utf-8"
15 | }
16 | } else { // Java libraries
17 | task sourcesJar(type: Jar, dependsOn: classes) {
18 | classifier = 'sources'
19 | from sourceSets.main.allSource
20 | }
21 | }
22 |
23 | task javadocJar(type: Jar, dependsOn: javadoc) {
24 | classifier = 'javadoc'
25 | from javadoc.destinationDir
26 | }
27 |
28 | artifacts {
29 | archives javadocJar
30 | archives sourcesJar
31 | }
32 |
33 | // Bintray
34 | Properties properties = new Properties()
35 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
36 |
37 | bintray {
38 | user = properties.getProperty("bintray.user")
39 | key = properties.getProperty("bintray.apikey")
40 |
41 | configurations = ['archives']
42 | pkg {
43 | repo = bintrayRepo
44 | name = bintrayName
45 | desc = libraryDescription
46 | websiteUrl = siteUrl
47 | vcsUrl = gitUrl
48 | licenses = allLicenses
49 | publish = true
50 | publicDownloadNumbers = true
51 | version {
52 | desc = libraryDescription
53 | gpg {
54 | sign = true //Determines whether to GPG sign the files. The default is false
55 | passphrase = properties.getProperty("bintray.gpg.password")
56 | //Optional. The passphrase for GPG signing'
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/hellodaemon/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | ext {
4 | bintrayRepo = 'maven'
5 | bintrayName = 'hellodaemon'
6 |
7 | publishedGroupId = 'com.xdandroid'
8 | libraryName = 'HelloDaemon'
9 | artifact = 'hellodaemon'
10 |
11 | libraryDescription = 'Android service daemon using JobScheduler.'
12 |
13 | siteUrl = 'https://github.com/xingda920813/HelloDaemon'
14 | gitUrl = 'https://github.com/xingda920813/HelloDaemon.git'
15 |
16 | libraryVersion = '1.2.2'
17 |
18 | developerId = 'xingda920813'
19 | developerName = 'Da Xing'
20 | developerEmail = '570396198@qq.com'
21 |
22 | licenseName = 'The MIT License (MIT)'
23 | licenseUrl = 'https://opensource.org/licenses/MIT'
24 | allLicenses = ["MIT"]
25 | }
26 |
27 | android {
28 | compileSdkVersion 26
29 | buildToolsVersion "27.0.2"
30 |
31 | defaultConfig {
32 | minSdkVersion 14
33 | targetSdkVersion 27
34 | versionCode 11
35 | versionName "1.2.2"
36 | }
37 | buildTypes {
38 | release {
39 | minifyEnabled false
40 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
41 | }
42 | }
43 | lintOptions {
44 | abortOnError false
45 | }
46 | }
47 |
48 | dependencies {
49 | api 'io.reactivex.rxjava2:rxjava:2.+'
50 | api 'com.android.support:support-annotations:+'
51 | }
52 |
53 | // Place it at the end of the file
54 | apply from: 'https://raw.githubusercontent.com/nuuneoi/JCenter/master/installv1.gradle'
55 | apply from: 'bintrayv1.gradle'
56 |
--------------------------------------------------------------------------------
/hellodaemon/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 C:\Users\XingDa\AppData\Local\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 |
19 | # Uncomment this to preserve the line number information for
20 | # debugging stack traces.
21 | #-keepattributes SourceFile,LineNumberTable
22 |
23 | # If you keep the line number information, uncomment this to
24 | # hide the original source file name.
25 | #-renamesourcefileattribute SourceFile
26 |
--------------------------------------------------------------------------------
/hellodaemon/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
42 |
43 |
46 |
47 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/hellodaemon/src/main/java/com/xdandroid/hellodaemon/AbsWorkService.java:
--------------------------------------------------------------------------------
1 | package com.xdandroid.hellodaemon;
2 |
3 | import android.app.*;
4 | import android.content.*;
5 | import android.content.pm.*;
6 | import android.os.*;
7 | import android.support.annotation.*;
8 |
9 | public abstract class AbsWorkService extends Service {
10 |
11 | protected static final int HASH_CODE = 1;
12 |
13 | protected boolean mFirstStarted = true;
14 |
15 | /**
16 | * 用于在不需要服务运行的时候取消 Job / Alarm / Subscription.
17 | */
18 | public static void cancelJobAlarmSub() {
19 | if (!DaemonEnv.sInitialized) return;
20 | DaemonEnv.sApp.sendBroadcast(new Intent(WakeUpReceiver.ACTION_CANCEL_JOB_ALARM_SUB));
21 | }
22 |
23 | /**
24 | * 是否 任务完成, 不再需要服务运行?
25 | * @return 应当停止服务, true; 应当启动服务, false; 无法判断, 什么也不做, null.
26 | */
27 | public abstract Boolean shouldStopService(Intent intent, int flags, int startId);
28 | public abstract void startWork(Intent intent, int flags, int startId);
29 | public abstract void stopWork(Intent intent, int flags, int startId);
30 | /**
31 | * 任务是否正在运行?
32 | * @return 任务正在运行, true; 任务当前不在运行, false; 无法判断, 什么也不做, null.
33 | */
34 | public abstract Boolean isWorkRunning(Intent intent, int flags, int startId);
35 | @Nullable public abstract IBinder onBind(Intent intent, Void alwaysNull);
36 | public abstract void onServiceKilled(Intent rootIntent);
37 |
38 | /**
39 | * 1.防止重复启动,可以任意调用 DaemonEnv.startServiceMayBind(Class serviceClass);
40 | * 2.利用漏洞启动前台服务而不显示通知;
41 | * 3.在子线程中运行定时任务,处理了运行前检查和销毁时保存的问题;
42 | * 4.启动守护服务;
43 | * 5.守护 Service 组件的启用状态, 使其不被 MAT 等工具禁用.
44 | */
45 | protected int onStart(Intent intent, int flags, int startId) {
46 |
47 | //启动守护服务,运行在:watch子进程中
48 | DaemonEnv.startServiceMayBind(WatchDogService.class);
49 |
50 | //业务逻辑: 实际使用时,根据需求,将这里更改为自定义的条件,判定服务应当启动还是停止 (任务是否需要运行)
51 | Boolean shouldStopService = shouldStopService(intent, flags, startId);
52 | if (shouldStopService != null) {
53 | if (shouldStopService) stopService(intent, flags, startId); else startService(intent, flags, startId);
54 | }
55 |
56 | if (mFirstStarted) {
57 | mFirstStarted = false;
58 | //启动前台服务而不显示通知的漏洞已在 API Level 25 修复,大快人心!
59 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
60 | //利用漏洞在 API Level 17 及以下的 Android 系统中,启动前台服务而不显示通知
61 | startForeground(HASH_CODE, new Notification());
62 | //利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知
63 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
64 | DaemonEnv.startServiceSafely(new Intent(getApplication(), WorkNotificationService.class));
65 | }
66 | getPackageManager().setComponentEnabledSetting(new ComponentName(getPackageName(), WatchDogService.class.getName()),
67 | PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
68 | }
69 |
70 | return START_STICKY;
71 | }
72 |
73 | void startService(Intent intent, int flags, int startId) {
74 | //检查服务是否不需要运行
75 | Boolean shouldStopService = shouldStopService(intent, flags, startId);
76 | if (shouldStopService != null && shouldStopService) return;
77 | //若还没有取消订阅,说明任务仍在运行,为防止重复启动,直接 return
78 | Boolean workRunning = isWorkRunning(intent, flags, startId);
79 | if (workRunning != null && workRunning) return;
80 | //业务逻辑
81 | startWork(intent, flags, startId);
82 | }
83 |
84 | /**
85 | * 停止服务并取消定时唤醒
86 | *
87 | * 停止服务使用取消订阅的方式实现,而不是调用 Context.stopService(Intent name)。因为:
88 | * 1.stopService 会调用 Service.onDestroy(),而 AbsWorkService 做了保活处理,会把 Service 再拉起来;
89 | * 2.我们希望 AbsWorkService 起到一个类似于控制台的角色,即 AbsWorkService 始终运行 (无论任务是否需要运行),
90 | * 而是通过 onStart() 里自定义的条件,来决定服务是否应当启动或停止。
91 | */
92 | void stopService(Intent intent, int flags, int startId) {
93 | //取消对任务的订阅
94 | stopWork(intent, flags, startId);
95 | //取消 Job / Alarm / Subscription
96 | cancelJobAlarmSub();
97 | }
98 |
99 | @Override
100 | public int onStartCommand(Intent intent, int flags, int startId) {
101 | return onStart(intent, flags, startId);
102 | }
103 |
104 | @Nullable
105 | @Override
106 | public IBinder onBind(Intent intent) {
107 | onStart(intent, 0, 0);
108 | return onBind(intent, null);
109 | }
110 |
111 | protected void onEnd(Intent rootIntent) {
112 | onServiceKilled(rootIntent);
113 | if (!DaemonEnv.sInitialized) return;
114 | DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass);
115 | DaemonEnv.startServiceMayBind(WatchDogService.class);
116 | }
117 |
118 | /**
119 | * 最近任务列表中划掉卡片时回调
120 | */
121 | @Override
122 | public void onTaskRemoved(Intent rootIntent) {
123 | onEnd(rootIntent);
124 | }
125 |
126 | /**
127 | * 设置-正在运行中停止服务时回调
128 | */
129 | @Override
130 | public void onDestroy() {
131 | onEnd(null);
132 | }
133 |
134 | public static class WorkNotificationService extends Service {
135 |
136 | /**
137 | * 利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知
138 | */
139 | @Override
140 | public int onStartCommand(Intent intent, int flags, int startId) {
141 | startForeground(AbsWorkService.HASH_CODE, new Notification());
142 | stopSelf();
143 | return START_STICKY;
144 | }
145 |
146 | @Override
147 | public IBinder onBind(Intent intent) {
148 | return null;
149 | }
150 | }
151 | }
152 |
--------------------------------------------------------------------------------
/hellodaemon/src/main/java/com/xdandroid/hellodaemon/DaemonEnv.java:
--------------------------------------------------------------------------------
1 | package com.xdandroid.hellodaemon;
2 |
3 | import android.app.*;
4 | import android.content.*;
5 | import android.os.*;
6 | import android.support.annotation.*;
7 |
8 | import java.util.*;
9 |
10 | public final class DaemonEnv {
11 |
12 | private DaemonEnv() {}
13 |
14 | public static final int DEFAULT_WAKE_UP_INTERVAL = 6 * 60 * 1000;
15 | private static final int MINIMAL_WAKE_UP_INTERVAL = 3 * 60 * 1000;
16 |
17 | static Context sApp;
18 | static Class extends AbsWorkService> sServiceClass;
19 | private static int sWakeUpInterval = DEFAULT_WAKE_UP_INTERVAL;
20 | static boolean sInitialized;
21 |
22 | static final Map, ServiceConnection> BIND_STATE_MAP = new HashMap<>();
23 |
24 | /**
25 | * @param app Application Context.
26 | * @param wakeUpInterval 定时唤醒的时间间隔(ms).
27 | */
28 | public static void initialize(@NonNull Context app, @NonNull Class extends AbsWorkService> serviceClass, @Nullable Integer wakeUpInterval) {
29 | sApp = app;
30 | sServiceClass = serviceClass;
31 | if (wakeUpInterval != null) sWakeUpInterval = wakeUpInterval;
32 | sInitialized = true;
33 | }
34 |
35 | public static void startServiceMayBind(@NonNull final Class extends Service> serviceClass) {
36 | if (!sInitialized) return;
37 | final Intent i = new Intent(sApp, serviceClass);
38 | startServiceSafely(i);
39 | ServiceConnection bound = BIND_STATE_MAP.get(serviceClass);
40 | if (bound == null) sApp.bindService(i, new ServiceConnection() {
41 | @Override
42 | public void onServiceConnected(ComponentName name, IBinder service) {
43 | BIND_STATE_MAP.put(serviceClass, this);
44 | }
45 |
46 | @Override
47 | public void onServiceDisconnected(ComponentName name) {
48 | BIND_STATE_MAP.remove(serviceClass);
49 | startServiceSafely(i);
50 | if (!sInitialized) return;
51 | sApp.bindService(i, this, Context.BIND_AUTO_CREATE);
52 | }
53 |
54 | @Override
55 | public void onBindingDied(ComponentName name) {
56 | onServiceDisconnected(name);
57 | }
58 | }, Context.BIND_AUTO_CREATE);
59 | }
60 |
61 | static void startServiceSafely(Intent i) {
62 | if (!sInitialized) return;
63 | try { sApp.startService(i); } catch (Exception ignored) {}
64 | }
65 |
66 | static int getWakeUpInterval() {
67 | return Math.max(sWakeUpInterval, MINIMAL_WAKE_UP_INTERVAL);
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/hellodaemon/src/main/java/com/xdandroid/hellodaemon/IntentWrapper.java:
--------------------------------------------------------------------------------
1 | package com.xdandroid.hellodaemon;
2 |
3 | import android.app.*;
4 | import android.content.*;
5 | import android.content.pm.*;
6 | import android.net.*;
7 | import android.os.*;
8 | import android.provider.*;
9 | import android.support.annotation.NonNull;
10 |
11 | import java.util.*;
12 |
13 | public class IntentWrapper {
14 |
15 | //Android 7.0+ Doze 模式
16 | protected static final int DOZE = 98;
17 | //华为 自启管理
18 | protected static final int HUAWEI = 99;
19 | //华为 锁屏清理
20 | protected static final int HUAWEI_GOD = 100;
21 | //小米 自启动管理
22 | protected static final int XIAOMI = 101;
23 | //小米 神隐模式
24 | protected static final int XIAOMI_GOD = 102;
25 | //三星 5.0/5.1 自启动应用程序管理
26 | protected static final int SAMSUNG_L = 103;
27 | //魅族 自启动管理
28 | protected static final int MEIZU = 104;
29 | //魅族 待机耗电管理
30 | protected static final int MEIZU_GOD = 105;
31 | //Oppo 自启动管理
32 | protected static final int OPPO = 106;
33 | //三星 6.0+ 未监视的应用程序管理
34 | protected static final int SAMSUNG_M = 107;
35 | //Oppo 自启动管理(旧版本系统)
36 | protected static final int OPPO_OLD = 108;
37 | //Vivo 后台高耗电
38 | protected static final int VIVO_GOD = 109;
39 | //金立 应用自启
40 | protected static final int GIONEE = 110;
41 | //乐视 自启动管理
42 | protected static final int LETV = 111;
43 | //乐视 应用保护
44 | protected static final int LETV_GOD = 112;
45 | //酷派 自启动管理
46 | protected static final int COOLPAD = 113;
47 | //联想 后台管理
48 | protected static final int LENOVO = 114;
49 | //联想 后台耗电优化
50 | protected static final int LENOVO_GOD = 115;
51 | //中兴 自启管理
52 | protected static final int ZTE = 116;
53 | //中兴 锁屏加速受保护应用
54 | protected static final int ZTE_GOD = 117;
55 |
56 | protected static List sIntentWrapperList;
57 |
58 | public static List getIntentWrapperList() {
59 | if (sIntentWrapperList == null) {
60 |
61 | if (!DaemonEnv.sInitialized) return new ArrayList<>();
62 |
63 | sIntentWrapperList = new ArrayList<>();
64 |
65 | //Android 7.0+ Doze 模式
66 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
67 | PowerManager pm = (PowerManager) DaemonEnv.sApp.getSystemService(Context.POWER_SERVICE);
68 | boolean ignoringBatteryOptimizations = pm.isIgnoringBatteryOptimizations(DaemonEnv.sApp.getPackageName());
69 | if (!ignoringBatteryOptimizations) {
70 | Intent dozeIntent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
71 | dozeIntent.setData(Uri.parse("package:" + DaemonEnv.sApp.getPackageName()));
72 | sIntentWrapperList.add(new IntentWrapper(dozeIntent, DOZE));
73 | }
74 | }
75 |
76 | //华为 自启管理
77 | Intent huaweiIntent = new Intent();
78 | huaweiIntent.setAction("huawei.intent.action.HSM_BOOTAPP_MANAGER");
79 | sIntentWrapperList.add(new IntentWrapper(huaweiIntent, HUAWEI));
80 |
81 | //华为 锁屏清理
82 | Intent huaweiGodIntent = new Intent();
83 | huaweiGodIntent.setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"));
84 | sIntentWrapperList.add(new IntentWrapper(huaweiGodIntent, HUAWEI_GOD));
85 |
86 | //小米 自启动管理
87 | Intent xiaomiIntent = new Intent();
88 | xiaomiIntent.setAction("miui.intent.action.OP_AUTO_START");
89 | xiaomiIntent.addCategory(Intent.CATEGORY_DEFAULT);
90 | sIntentWrapperList.add(new IntentWrapper(xiaomiIntent, XIAOMI));
91 |
92 | //小米 神隐模式
93 | Intent xiaomiGodIntent = new Intent();
94 | xiaomiGodIntent.setComponent(new ComponentName("com.miui.powerkeeper", "com.miui.powerkeeper.ui.HiddenAppsConfigActivity"));
95 | xiaomiGodIntent.putExtra("package_name", DaemonEnv.sApp.getPackageName());
96 | xiaomiGodIntent.putExtra("package_label", getApplicationName());
97 | sIntentWrapperList.add(new IntentWrapper(xiaomiGodIntent, XIAOMI_GOD));
98 |
99 | //三星 5.0/5.1 自启动应用程序管理
100 | Intent samsungLIntent = DaemonEnv.sApp.getPackageManager().getLaunchIntentForPackage("com.samsung.android.sm");
101 | if (samsungLIntent != null) sIntentWrapperList.add(new IntentWrapper(samsungLIntent, SAMSUNG_L));
102 |
103 | //三星 6.0+ 未监视的应用程序管理
104 | Intent samsungMIntent = new Intent();
105 | samsungMIntent.setComponent(new ComponentName("com.samsung.android.sm_cn", "com.samsung.android.sm.ui.battery.BatteryActivity"));
106 | sIntentWrapperList.add(new IntentWrapper(samsungMIntent, SAMSUNG_M));
107 |
108 | //魅族 自启动管理
109 | Intent meizuIntent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
110 | meizuIntent.addCategory(Intent.CATEGORY_DEFAULT);
111 | meizuIntent.putExtra("packageName", DaemonEnv.sApp.getPackageName());
112 | sIntentWrapperList.add(new IntentWrapper(meizuIntent, MEIZU));
113 |
114 | //魅族 待机耗电管理
115 | Intent meizuGodIntent = new Intent();
116 | meizuGodIntent.setComponent(new ComponentName("com.meizu.safe", "com.meizu.safe.powerui.PowerAppPermissionActivity"));
117 | sIntentWrapperList.add(new IntentWrapper(meizuGodIntent, MEIZU_GOD));
118 |
119 | //Oppo 自启动管理
120 | Intent oppoIntent = new Intent();
121 | oppoIntent.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity"));
122 | sIntentWrapperList.add(new IntentWrapper(oppoIntent, OPPO));
123 |
124 | //Oppo 自启动管理(旧版本系统)
125 | Intent oppoOldIntent = new Intent();
126 | oppoOldIntent.setComponent(new ComponentName("com.color.safecenter", "com.color.safecenter.permission.startup.StartupAppListActivity"));
127 | sIntentWrapperList.add(new IntentWrapper(oppoOldIntent, OPPO_OLD));
128 |
129 | //Vivo 后台高耗电
130 | Intent vivoGodIntent = new Intent();
131 | vivoGodIntent.setComponent(new ComponentName("com.vivo.abe", "com.vivo.applicationbehaviorengine.ui.ExcessivePowerManagerActivity"));
132 | sIntentWrapperList.add(new IntentWrapper(vivoGodIntent, VIVO_GOD));
133 |
134 | //金立 应用自启
135 | Intent gioneeIntent = new Intent();
136 | gioneeIntent.setComponent(new ComponentName("com.gionee.softmanager", "com.gionee.softmanager.MainActivity"));
137 | sIntentWrapperList.add(new IntentWrapper(gioneeIntent, GIONEE));
138 |
139 | //乐视 自启动管理
140 | Intent letvIntent = new Intent();
141 | letvIntent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity"));
142 | sIntentWrapperList.add(new IntentWrapper(letvIntent, LETV));
143 |
144 | //乐视 应用保护
145 | Intent letvGodIntent = new Intent();
146 | letvGodIntent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.BackgroundAppManageActivity"));
147 | sIntentWrapperList.add(new IntentWrapper(letvGodIntent, LETV_GOD));
148 |
149 | //酷派 自启动管理
150 | Intent coolpadIntent = new Intent();
151 | coolpadIntent.setComponent(new ComponentName("com.yulong.android.security", "com.yulong.android.seccenter.tabbarmain"));
152 | sIntentWrapperList.add(new IntentWrapper(coolpadIntent, COOLPAD));
153 |
154 | //联想 后台管理
155 | Intent lenovoIntent = new Intent();
156 | lenovoIntent.setComponent(new ComponentName("com.lenovo.security", "com.lenovo.security.purebackground.PureBackgroundActivity"));
157 | sIntentWrapperList.add(new IntentWrapper(lenovoIntent, LENOVO));
158 |
159 | //联想 后台耗电优化
160 | Intent lenovoGodIntent = new Intent();
161 | lenovoGodIntent.setComponent(new ComponentName("com.lenovo.powersetting", "com.lenovo.powersetting.ui.Settings$HighPowerApplicationsActivity"));
162 | sIntentWrapperList.add(new IntentWrapper(lenovoGodIntent, LENOVO_GOD));
163 |
164 | //中兴 自启管理
165 | Intent zteIntent = new Intent();
166 | zteIntent.setComponent(new ComponentName("com.zte.heartyservice", "com.zte.heartyservice.autorun.AppAutoRunManager"));
167 | sIntentWrapperList.add(new IntentWrapper(zteIntent, ZTE));
168 |
169 | //中兴 锁屏加速受保护应用
170 | Intent zteGodIntent = new Intent();
171 | zteGodIntent.setComponent(new ComponentName("com.zte.heartyservice", "com.zte.heartyservice.setting.ClearAppSettingsActivity"));
172 | sIntentWrapperList.add(new IntentWrapper(zteGodIntent, ZTE_GOD));
173 | }
174 | return sIntentWrapperList;
175 | }
176 |
177 | protected static String sApplicationName;
178 |
179 | public static String getApplicationName() {
180 | if (sApplicationName == null) {
181 | if (!DaemonEnv.sInitialized) return "";
182 | PackageManager pm;
183 | ApplicationInfo ai;
184 | try {
185 | pm = DaemonEnv.sApp.getPackageManager();
186 | ai = pm.getApplicationInfo(DaemonEnv.sApp.getPackageName(), 0);
187 | sApplicationName = pm.getApplicationLabel(ai).toString();
188 | } catch (PackageManager.NameNotFoundException e) {
189 | e.printStackTrace();
190 | sApplicationName = DaemonEnv.sApp.getPackageName();
191 | }
192 | }
193 | return sApplicationName;
194 | }
195 |
196 | /**
197 | * 处理白名单.
198 | * @return 弹过框的 IntentWrapper.
199 | */
200 | @NonNull
201 | public static List whiteListMatters(final Activity a, String reason) {
202 | List showed = new ArrayList<>();
203 | if (reason == null) reason = "核心服务的持续运行";
204 | List intentWrapperList = getIntentWrapperList();
205 | for (final IntentWrapper iw : intentWrapperList) {
206 | //如果本机上没有能处理这个Intent的Activity,说明不是对应的机型,直接忽略进入下一次循环。
207 | if (!iw.doesActivityExists()) continue;
208 | switch (iw.type) {
209 | case DOZE:
210 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
211 | PowerManager pm = (PowerManager) a.getSystemService(Context.POWER_SERVICE);
212 | if (pm.isIgnoringBatteryOptimizations(a.getPackageName())) break;
213 | new AlertDialog.Builder(a)
214 | .setCancelable(false)
215 | .setTitle("需要忽略 " + getApplicationName() + " 的电池优化")
216 | .setMessage(reason + "需要 " + getApplicationName() + " 加入到电池优化的忽略名单。\n\n" +
217 | "请点击『确定』,在弹出的『忽略电池优化』对话框中,选择『是』。")
218 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
219 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
220 | })
221 | .show();
222 | showed.add(iw);
223 | }
224 | break;
225 | case HUAWEI:
226 | new AlertDialog.Builder(a)
227 | .setCancelable(false)
228 | .setTitle("需要允许 " + getApplicationName() + " 自动启动")
229 | .setMessage(reason + "需要允许 " + getApplicationName() + " 的自动启动。\n\n" +
230 | "请点击『确定』,在弹出的『自启管理』中,将 " + getApplicationName() + " 对应的开关打开。")
231 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
232 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
233 | })
234 | .show();
235 | showed.add(iw);
236 | break;
237 | case ZTE_GOD:
238 | case HUAWEI_GOD:
239 | new AlertDialog.Builder(a)
240 | .setCancelable(false)
241 | .setTitle(getApplicationName() + " 需要加入锁屏清理白名单")
242 | .setMessage(reason + "需要 " + getApplicationName() + " 加入到锁屏清理白名单。\n\n" +
243 | "请点击『确定』,在弹出的『锁屏清理』列表中,将 " + getApplicationName() + " 对应的开关打开。")
244 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
245 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
246 | })
247 | .show();
248 | showed.add(iw);
249 | break;
250 | case XIAOMI_GOD:
251 | new AlertDialog.Builder(a)
252 | .setCancelable(false)
253 | .setTitle("需要关闭 " + getApplicationName() + " 的神隐模式")
254 | .setMessage(reason + "需要关闭 " + getApplicationName() + " 的神隐模式。\n\n" +
255 | "请点击『确定』,在弹出的 " + getApplicationName() + " 神隐模式设置中,选择『无限制』,然后选择『允许定位』。")
256 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
257 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
258 | })
259 | .show();
260 | showed.add(iw);
261 | break;
262 | case SAMSUNG_L:
263 | new AlertDialog.Builder(a)
264 | .setCancelable(false)
265 | .setTitle("需要允许 " + getApplicationName() + " 的自启动")
266 | .setMessage(reason + "需要 " + getApplicationName() + " 在屏幕关闭时继续运行。\n\n" +
267 | "请点击『确定』,在弹出的『智能管理器』中,点击『内存』,选择『自启动应用程序』选项卡,将 " + getApplicationName() + " 对应的开关打开。")
268 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
269 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
270 | })
271 | .show();
272 | showed.add(iw);
273 | break;
274 | case SAMSUNG_M:
275 | new AlertDialog.Builder(a)
276 | .setCancelable(false)
277 | .setTitle("需要允许 " + getApplicationName() + " 的自启动")
278 | .setMessage(reason + "需要 " + getApplicationName() + " 在屏幕关闭时继续运行。\n\n" +
279 | "请点击『确定』,在弹出的『电池』页面中,点击『未监视的应用程序』->『添加应用程序』,勾选 " + getApplicationName() + ",然后点击『完成』。")
280 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
281 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
282 | })
283 | .show();
284 | showed.add(iw);
285 | break;
286 | case MEIZU:
287 | new AlertDialog.Builder(a)
288 | .setCancelable(false)
289 | .setTitle("需要允许 " + getApplicationName() + " 保持后台运行")
290 | .setMessage(reason + "需要允许 " + getApplicationName() + " 保持后台运行。\n\n" +
291 | "请点击『确定』,在弹出的应用信息界面中,将『后台管理』选项更改为『保持后台运行』。")
292 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
293 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
294 | })
295 | .show();
296 | showed.add(iw);
297 | break;
298 | case MEIZU_GOD:
299 | new AlertDialog.Builder(a)
300 | .setCancelable(false)
301 | .setTitle(getApplicationName() + " 需要在待机时保持运行")
302 | .setMessage(reason + "需要 " + getApplicationName() + " 在待机时保持运行。\n\n" +
303 | "请点击『确定』,在弹出的『待机耗电管理』中,将 " + getApplicationName() + " 对应的开关打开。")
304 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
305 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
306 | })
307 | .show();
308 | showed.add(iw);
309 | break;
310 | case ZTE:
311 | case LETV:
312 | case XIAOMI:
313 | case OPPO:
314 | case OPPO_OLD:
315 | new AlertDialog.Builder(a)
316 | .setCancelable(false)
317 | .setTitle("需要允许 " + getApplicationName() + " 的自启动")
318 | .setMessage(reason + "需要 " + getApplicationName() + " 加入到自启动白名单。\n\n" +
319 | "请点击『确定』,在弹出的『自启动管理』中,将 " + getApplicationName() + " 对应的开关打开。")
320 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
321 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
322 | })
323 | .show();
324 | showed.add(iw);
325 | break;
326 | case COOLPAD:
327 | new AlertDialog.Builder(a)
328 | .setCancelable(false)
329 | .setTitle("需要允许 " + getApplicationName() + " 的自启动")
330 | .setMessage(reason + "需要允许 " + getApplicationName() + " 的自启动。\n\n" +
331 | "请点击『确定』,在弹出的『酷管家』中,找到『软件管理』->『自启动管理』,取消勾选 " + getApplicationName() + ",将 " + getApplicationName() + " 的状态改为『已允许』。")
332 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
333 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
334 | })
335 | .show();
336 | showed.add(iw);
337 | break;
338 | case VIVO_GOD:
339 | new AlertDialog.Builder(a)
340 | .setCancelable(false)
341 | .setTitle("需要允许 " + getApplicationName() + " 的后台运行")
342 | .setMessage(reason + "需要允许 " + getApplicationName() + " 在后台高耗电时运行。\n\n" +
343 | "请点击『确定』,在弹出的『后台高耗电』中,将 " + getApplicationName() + " 对应的开关打开。")
344 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
345 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
346 | })
347 | .show();
348 | showed.add(iw);
349 | break;
350 | case GIONEE:
351 | new AlertDialog.Builder(a)
352 | .setCancelable(false)
353 | .setTitle(getApplicationName() + " 需要加入应用自启和绿色后台白名单")
354 | .setMessage(reason + "需要允许 " + getApplicationName() + " 的自启动和后台运行。\n\n" +
355 | "请点击『确定』,在弹出的『系统管家』中,分别找到『应用管理』->『应用自启』和『绿色后台』->『清理白名单』,将 " + getApplicationName() + " 添加到白名单。")
356 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
357 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
358 | })
359 | .show();
360 | showed.add(iw);
361 | break;
362 | case LETV_GOD:
363 | new AlertDialog.Builder(a)
364 | .setCancelable(false)
365 | .setTitle("需要禁止 " + getApplicationName() + " 被自动清理")
366 | .setMessage(reason + "需要禁止 " + getApplicationName() + " 被自动清理。\n\n" +
367 | "请点击『确定』,在弹出的『应用保护』中,将 " + getApplicationName() + " 对应的开关关闭。")
368 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
369 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
370 | })
371 | .show();
372 | showed.add(iw);
373 | break;
374 | case LENOVO:
375 | new AlertDialog.Builder(a)
376 | .setCancelable(false)
377 | .setTitle("需要允许 " + getApplicationName() + " 的后台运行")
378 | .setMessage(reason + "需要允许 " + getApplicationName() + " 的后台自启、后台 GPS 和后台运行。\n\n" +
379 | "请点击『确定』,在弹出的『后台管理』中,分别找到『后台自启』、『后台 GPS』和『后台运行』,将 " + getApplicationName() + " 对应的开关打开。")
380 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
381 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
382 | })
383 | .show();
384 | showed.add(iw);
385 | break;
386 | case LENOVO_GOD:
387 | new AlertDialog.Builder(a)
388 | .setCancelable(false)
389 | .setTitle("需要关闭 " + getApplicationName() + " 的后台耗电优化")
390 | .setMessage(reason + "需要关闭 " + getApplicationName() + " 的后台耗电优化。\n\n" +
391 | "请点击『确定』,在弹出的『后台耗电优化』中,将 " + getApplicationName() + " 对应的开关关闭。")
392 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
393 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
394 | })
395 | .show();
396 | showed.add(iw);
397 | break;
398 | }
399 | }
400 | return showed;
401 | }
402 |
403 | /**
404 | * 防止华为机型未加入白名单时按返回键回到桌面再锁屏后几秒钟进程被杀
405 | */
406 | public static void onBackPressed(Activity a) {
407 | Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
408 | launcherIntent.addCategory(Intent.CATEGORY_HOME);
409 | a.startActivity(launcherIntent);
410 | }
411 |
412 | protected Intent intent;
413 | protected int type;
414 |
415 | protected IntentWrapper(Intent intent, int type) {
416 | this.intent = intent;
417 | this.type = type;
418 | }
419 |
420 | /**
421 | * 判断本机上是否有能处理当前Intent的Activity
422 | */
423 | protected boolean doesActivityExists() {
424 | if (!DaemonEnv.sInitialized) return false;
425 | PackageManager pm = DaemonEnv.sApp.getPackageManager();
426 | List list = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
427 | return list != null && list.size() > 0;
428 | }
429 |
430 | /**
431 | * 安全地启动一个Activity
432 | */
433 | protected void startActivitySafely(Activity activityContext) {
434 | try { activityContext.startActivity(intent); } catch (Exception e) { e.printStackTrace(); }
435 | }
436 | }
437 |
--------------------------------------------------------------------------------
/hellodaemon/src/main/java/com/xdandroid/hellodaemon/JobSchedulerService.java:
--------------------------------------------------------------------------------
1 | package com.xdandroid.hellodaemon;
2 |
3 | import android.annotation.*;
4 | import android.app.job.*;
5 | import android.os.*;
6 |
7 | /**
8 | * Android 5.0+ 使用的 JobScheduler.
9 | * 运行在 :watch 子进程中.
10 | */
11 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
12 | public class JobSchedulerService extends JobService {
13 |
14 | @Override
15 | public boolean onStartJob(JobParameters params) {
16 | if (!DaemonEnv.sInitialized) return false;
17 | DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass);
18 | return false;
19 | }
20 |
21 | @Override
22 | public boolean onStopJob(JobParameters params) {
23 | return false;
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/hellodaemon/src/main/java/com/xdandroid/hellodaemon/WakeUpReceiver.java:
--------------------------------------------------------------------------------
1 | package com.xdandroid.hellodaemon;
2 |
3 | import android.content.*;
4 |
5 | public class WakeUpReceiver extends BroadcastReceiver {
6 |
7 | /**
8 | * 向 WakeUpReceiver 发送带有此 Action 的广播, 即可在不需要服务运行的时候取消 Job / Alarm / Subscription.
9 | */
10 | protected static final String ACTION_CANCEL_JOB_ALARM_SUB = "com.xdandroid.hellodaemon.CANCEL_JOB_ALARM_SUB";
11 |
12 | /**
13 | * 监听 8 种系统广播 :
14 | * CONNECTIVITY\_CHANGE, USER\_PRESENT, ACTION\_POWER\_CONNECTED, ACTION\_POWER\_DISCONNECTED,
15 | * BOOT\_COMPLETED, MEDIA\_MOUNTED, PACKAGE\_ADDED, PACKAGE\_REMOVED.
16 | * 在网络连接改变, 用户屏幕解锁, 电源连接 / 断开, 系统启动完成, 挂载 SD 卡, 安装 / 卸载软件包时拉起 Service.
17 | * Service 内部做了判断,若 Service 已在运行,不会重复启动.
18 | * 运行在:watch子进程中.
19 | */
20 | @Override
21 | public void onReceive(Context context, Intent intent) {
22 | if (intent != null && ACTION_CANCEL_JOB_ALARM_SUB.equals(intent.getAction())) {
23 | WatchDogService.cancelJobAlarmSub();
24 | return;
25 | }
26 | if (!DaemonEnv.sInitialized) return;
27 | DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass);
28 | }
29 |
30 | public static class WakeUpAutoStartReceiver extends BroadcastReceiver {
31 |
32 | @Override
33 | public void onReceive(Context context, Intent intent) {
34 | if (!DaemonEnv.sInitialized) return;
35 | DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass);
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/hellodaemon/src/main/java/com/xdandroid/hellodaemon/WatchDogService.java:
--------------------------------------------------------------------------------
1 | package com.xdandroid.hellodaemon;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.Notification;
5 | import android.app.PendingIntent;
6 | import android.app.Service;
7 | import android.app.job.JobInfo;
8 | import android.app.job.JobScheduler;
9 | import android.content.ComponentName;
10 | import android.content.Intent;
11 | import android.content.pm.PackageManager;
12 | import android.os.Build;
13 | import android.os.IBinder;
14 |
15 | import java.util.concurrent.TimeUnit;
16 |
17 | import io.reactivex.*;
18 | import io.reactivex.disposables.Disposable;
19 | import io.reactivex.functions.Consumer;
20 |
21 | public class WatchDogService extends Service {
22 |
23 | protected static final int HASH_CODE = 2;
24 |
25 | protected static Disposable sDisposable;
26 | protected static PendingIntent sPendingIntent;
27 |
28 | /**
29 | * 守护服务,运行在:watch子进程中
30 | */
31 | protected final int onStart(Intent intent, int flags, int startId) {
32 |
33 | if (!DaemonEnv.sInitialized) return START_STICKY;
34 |
35 | if (sDisposable != null && !sDisposable.isDisposed()) return START_STICKY;
36 |
37 | if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
38 | startForeground(HASH_CODE, new Notification());
39 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
40 | DaemonEnv.startServiceSafely(new Intent(DaemonEnv.sApp, WatchDogNotificationService.class));
41 | }
42 |
43 | //定时检查 AbsWorkService 是否在运行,如果不在运行就把它拉起来
44 | //Android 5.0+ 使用 JobScheduler,效果比 AlarmManager 好
45 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
46 | JobInfo.Builder builder = new JobInfo.Builder(HASH_CODE, new ComponentName(DaemonEnv.sApp, JobSchedulerService.class));
47 | builder.setPeriodic(DaemonEnv.getWakeUpInterval());
48 | //Android 7.0+ 增加了一项针对 JobScheduler 的新限制,最小间隔只能是下面设定的数字
49 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) builder.setPeriodic(JobInfo.getMinPeriodMillis(), JobInfo.getMinFlexMillis());
50 | builder.setPersisted(true);
51 | JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
52 | scheduler.schedule(builder.build());
53 | } else {
54 | //Android 4.4- 使用 AlarmManager
55 | AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
56 | Intent i = new Intent(DaemonEnv.sApp, DaemonEnv.sServiceClass);
57 | sPendingIntent = PendingIntent.getService(DaemonEnv.sApp, HASH_CODE, i, PendingIntent.FLAG_UPDATE_CURRENT);
58 | am.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + DaemonEnv.getWakeUpInterval(), DaemonEnv.getWakeUpInterval(), sPendingIntent);
59 | }
60 |
61 | //使用定时 Observable,避免 Android 定制系统 JobScheduler / AlarmManager 唤醒间隔不稳定的情况
62 | sDisposable = Observable
63 | .interval(DaemonEnv.getWakeUpInterval(), TimeUnit.MILLISECONDS)
64 | .subscribe(new Consumer() {
65 | @Override
66 | public void accept(Long aLong) throws Exception {
67 | DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass);
68 | }
69 | }, new Consumer() {
70 | @Override
71 | public void accept(Throwable throwable) throws Exception {
72 | throwable.printStackTrace();
73 | }
74 | });
75 |
76 | //守护 Service 组件的启用状态, 使其不被 MAT 等工具禁用
77 | getPackageManager().setComponentEnabledSetting(new ComponentName(getPackageName(), DaemonEnv.sServiceClass.getName()),
78 | PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
79 |
80 | return START_STICKY;
81 | }
82 |
83 | @Override
84 | public final int onStartCommand(Intent intent, int flags, int startId) {
85 | return onStart(intent, flags, startId);
86 | }
87 |
88 | @Override
89 | public final IBinder onBind(Intent intent) {
90 | onStart(intent, 0, 0);
91 | return null;
92 | }
93 |
94 | protected void onEnd(Intent rootIntent) {
95 | if (!DaemonEnv.sInitialized) return;
96 | DaemonEnv.startServiceMayBind(DaemonEnv.sServiceClass);
97 | DaemonEnv.startServiceMayBind(WatchDogService.class);
98 | }
99 |
100 | /**
101 | * 最近任务列表中划掉卡片时回调
102 | */
103 | @Override
104 | public void onTaskRemoved(Intent rootIntent) {
105 | onEnd(rootIntent);
106 | }
107 |
108 | /**
109 | * 设置-正在运行中停止服务时回调
110 | */
111 | @Override
112 | public void onDestroy() {
113 | onEnd(null);
114 | }
115 |
116 | /**
117 | * 用于在不需要服务运行的时候取消 Job / Alarm / Subscription.
118 | *
119 | * 因 WatchDogService 运行在 :watch 子进程, 请勿在主进程中直接调用此方法.
120 | * 而是向 WakeUpReceiver 发送一个 Action 为 WakeUpReceiver.ACTION_CANCEL_JOB_ALARM_SUB 的广播.
121 | */
122 | public static void cancelJobAlarmSub() {
123 | if (!DaemonEnv.sInitialized) return;
124 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
125 | JobScheduler scheduler = (JobScheduler) DaemonEnv.sApp.getSystemService(JOB_SCHEDULER_SERVICE);
126 | scheduler.cancel(HASH_CODE);
127 | } else {
128 | AlarmManager am = (AlarmManager) DaemonEnv.sApp.getSystemService(ALARM_SERVICE);
129 | if (sPendingIntent != null) am.cancel(sPendingIntent);
130 | }
131 | if (sDisposable != null) sDisposable.dispose();
132 | }
133 |
134 | public static class WatchDogNotificationService extends Service {
135 |
136 | /**
137 | * 利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知
138 | * 运行在:watch子进程中
139 | */
140 | @Override
141 | public int onStartCommand(Intent intent, int flags, int startId) {
142 | startForeground(WatchDogService.HASH_CODE, new Notification());
143 | stopSelf();
144 | return START_STICKY;
145 | }
146 |
147 | @Override
148 | public IBinder onBind(Intent intent) {
149 | return null;
150 | }
151 | }
152 | }
153 |
--------------------------------------------------------------------------------
/hellodaemon/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | HelloDaemon
3 |
4 |
--------------------------------------------------------------------------------
/sample/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/sample/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 26
5 | buildToolsVersion "27.0.2"
6 | defaultConfig {
7 | applicationId "com.xdandroid.sample"
8 | minSdkVersion 14
9 | targetSdkVersion 27
10 | versionCode 1
11 | versionName "1.0.0"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | }
17 | }
18 | compileOptions {
19 | targetCompatibility 1.8
20 | sourceCompatibility 1.8
21 | }
22 | }
23 |
24 | dependencies {
25 | api 'io.reactivex.rxjava2:rxjava:2.+'
26 | api project(':hellodaemon')
27 | }
28 |
--------------------------------------------------------------------------------
/sample/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 C:\Users\XingDa\AppData\Local\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 |
--------------------------------------------------------------------------------
/sample/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/xdandroid/sample/App.java:
--------------------------------------------------------------------------------
1 | package com.xdandroid.sample;
2 |
3 | import android.app.*;
4 |
5 | import com.xdandroid.hellodaemon.*;
6 |
7 | public class App extends Application {
8 |
9 | @Override
10 | public void onCreate() {
11 | super.onCreate();
12 | //需要在 Application 的 onCreate() 中调用一次 DaemonEnv.initialize()
13 | DaemonEnv.initialize(this, TraceServiceImpl.class, DaemonEnv.DEFAULT_WAKE_UP_INTERVAL);
14 | TraceServiceImpl.sShouldStopService = false;
15 | DaemonEnv.startServiceMayBind(TraceServiceImpl.class);
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/xdandroid/sample/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.xdandroid.sample;
2 |
3 | import android.app.*;
4 | import android.os.*;
5 | import android.view.*;
6 |
7 | import com.xdandroid.hellodaemon.*;
8 |
9 | public class MainActivity extends Activity {
10 |
11 | protected void onCreate(Bundle b) {
12 | super.onCreate(b);
13 | setContentView(R.layout.activity_main);
14 | }
15 |
16 | public void onClick(View v) {
17 | switch (v.getId()) {
18 | case R.id.btn_start:
19 | TraceServiceImpl.sShouldStopService = false;
20 | DaemonEnv.startServiceMayBind(TraceServiceImpl.class);
21 | break;
22 | case R.id.btn_white:
23 | IntentWrapper.whiteListMatters(this, "轨迹跟踪服务的持续运行");
24 | break;
25 | case R.id.btn_stop:
26 | TraceServiceImpl.stopService();
27 | break;
28 | }
29 | }
30 |
31 | //防止华为机型未加入白名单时按返回键回到桌面再锁屏后几秒钟进程被杀
32 | public void onBackPressed() {
33 | IntentWrapper.onBackPressed(this);
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/sample/src/main/java/com/xdandroid/sample/TraceServiceImpl.java:
--------------------------------------------------------------------------------
1 | package com.xdandroid.sample;
2 |
3 | import android.content.*;
4 | import android.os.*;
5 |
6 | import com.xdandroid.hellodaemon.*;
7 |
8 | import java.util.concurrent.*;
9 |
10 | import io.reactivex.*;
11 | import io.reactivex.disposables.*;
12 |
13 | public class TraceServiceImpl extends AbsWorkService {
14 |
15 | //是否 任务完成, 不再需要服务运行?
16 | public static boolean sShouldStopService;
17 | public static Disposable sDisposable;
18 |
19 | public static void stopService() {
20 | //我们现在不再需要服务运行了, 将标志位置为 true
21 | sShouldStopService = true;
22 | //取消对任务的订阅
23 | if (sDisposable != null) sDisposable.dispose();
24 | //取消 Job / Alarm / Subscription
25 | cancelJobAlarmSub();
26 | }
27 |
28 | /**
29 | * 是否 任务完成, 不再需要服务运行?
30 | * @return 应当停止服务, true; 应当启动服务, false; 无法判断, 什么也不做, null.
31 | */
32 | @Override
33 | public Boolean shouldStopService(Intent intent, int flags, int startId) {
34 | return sShouldStopService;
35 | }
36 |
37 | @Override
38 | public void startWork(Intent intent, int flags, int startId) {
39 | System.out.println("检查磁盘中是否有上次销毁时保存的数据");
40 | sDisposable = Observable
41 | .interval(3, TimeUnit.SECONDS)
42 | //取消任务时取消定时唤醒
43 | .doOnDispose(() -> {
44 | System.out.println("保存数据到磁盘。");
45 | cancelJobAlarmSub();
46 | })
47 | .subscribe(count -> {
48 | System.out.println("每 3 秒采集一次数据... count = " + count);
49 | if (count > 0 && count % 18 == 0) System.out.println("保存数据到磁盘。 saveCount = " + (count / 18 - 1));
50 | });
51 | }
52 |
53 | @Override
54 | public void stopWork(Intent intent, int flags, int startId) {
55 | stopService();
56 | }
57 |
58 | /**
59 | * 任务是否正在运行?
60 | * @return 任务正在运行, true; 任务当前不在运行, false; 无法判断, 什么也不做, null.
61 | */
62 | @Override
63 | public Boolean isWorkRunning(Intent intent, int flags, int startId) {
64 | //若还没有取消订阅, 就说明任务仍在运行.
65 | return sDisposable != null && !sDisposable.isDisposed();
66 | }
67 |
68 | @Override
69 | public IBinder onBind(Intent intent, Void v) {
70 | return null;
71 | }
72 |
73 | @Override
74 | public void onServiceKilled(Intent rootIntent) {
75 | System.out.println("保存数据到磁盘。");
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/sample/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
22 |
23 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xingda920813/HelloDaemon/e57d51a20c57610b37ad50c097315fac988b97fd/sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/sample/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Sample
3 |
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':hellodaemon', ':sample'
2 |
--------------------------------------------------------------------------------