├── .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 | 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 | 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 | 26 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 19 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Android 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 52 | 53 | 54 | 55 | 56 | 57 | 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 | 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 serviceClass, //刚才创建的 Service 对应的 Class 对象. 182 | @Nullable Integer wakeUpInterval); //定时唤醒的时间间隔(ms), 默认 6 分钟. 183 | 184 | Context.startService(new Intent(Context app, Class 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 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 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 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 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 |