├── Sample ├── .gitignore ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── layout │ │ │ └── activity_main.xml │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ └── drawable │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ └── com │ │ │ └── sunfusheng │ │ │ └── daemon │ │ │ └── sample │ │ │ ├── App.java │ │ │ ├── MainActivity.java │ │ │ └── HeartBeatService.java │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── DaemonService ├── .gitignore ├── src │ └── main │ │ ├── res │ │ └── values │ │ │ └── strings.xml │ │ ├── aidl │ │ └── com │ │ │ └── sunfusheng │ │ │ └── daemon │ │ │ └── DaemonAidl.aidl │ │ ├── java │ │ └── com │ │ │ └── sunfusheng │ │ │ └── daemon │ │ │ ├── DaemonReceiver.java │ │ │ ├── JobSchedulerService.java │ │ │ ├── DaemonUtil.java │ │ │ ├── DaemonHolder.java │ │ │ ├── AbsHeartBeatService.java │ │ │ └── DaemonService.java │ │ └── AndroidManifest.xml ├── build.gradle └── proguard-rules.pro ├── settings.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .idea ├── caches │ └── build_file_checksums.ser ├── encodings.xml ├── runConfigurations.xml ├── gradle.xml ├── codeStyles │ └── Project.xml └── misc.xml ├── .gitignore ├── gradle.properties ├── README.md ├── gradlew.bat └── gradlew /Sample/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /DaemonService/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':Sample', ':DaemonService' 2 | -------------------------------------------------------------------------------- /Sample/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | DaemonService 3 | 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /DaemonService/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | DaemonService 3 | 4 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/Sample/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/Sample/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/Sample/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/Sample/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/Sample/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/Sample/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/Sample/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/Sample/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/Sample/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sunfusheng/DaemonService/HEAD/Sample/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /DaemonService/src/main/aidl/com/sunfusheng/daemon/DaemonAidl.aidl: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.daemon; 2 | 3 | interface DaemonAidl { 4 | void startService(); 5 | void stopService(); 6 | } 7 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /Sample/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Aug 01 14:23:23 CST 2018 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.4-all.zip 7 | -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Sample/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /Sample/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /Sample/src/main/java/com/sunfusheng/daemon/sample/App.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.daemon.sample; 2 | 3 | import android.app.Application; 4 | import android.util.Log; 5 | 6 | import com.sunfusheng.daemon.DaemonHolder; 7 | import com.sunfusheng.daemon.DaemonUtil; 8 | 9 | /** 10 | * @author sunfusheng on 2018/8/1. 11 | */ 12 | public class App extends Application { 13 | private static final String TAG = "---> App"; 14 | 15 | @Override 16 | public void onCreate() { 17 | super.onCreate(); 18 | Log.d(TAG, "onCreate() 【" + DaemonUtil.getProcessName(this) + "】"); 19 | DaemonHolder.init(this, HeartBeatService.class); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /DaemonService/src/main/java/com/sunfusheng/daemon/DaemonReceiver.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.daemon; 2 | 3 | import android.content.BroadcastReceiver; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.util.Log; 7 | 8 | /** 9 | * @author sunfusheng on 2018/8/3. 10 | */ 11 | public class DaemonReceiver extends BroadcastReceiver { 12 | private static final String TAG = "---> DaemonReceiver"; 13 | 14 | @Override 15 | public void onReceive(Context context, Intent intent) { 16 | if (intent != null) { 17 | Log.d(TAG, "onReceive() action: " + intent.getAction()); 18 | } 19 | DaemonHolder.startService(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 18 | 19 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx1536m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | -------------------------------------------------------------------------------- /DaemonService/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | minSdkVersion 14 7 | targetSdkVersion 27 8 | versionCode 1 9 | versionName "1.0" 10 | } 11 | 12 | compileOptions { 13 | sourceCompatibility JavaVersion.VERSION_1_8 14 | targetCompatibility JavaVersion.VERSION_1_8 15 | } 16 | 17 | buildTypes { 18 | release { 19 | minifyEnabled false 20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 21 | } 22 | } 23 | } 24 | 25 | dependencies { 26 | implementation fileTree(dir: 'libs', include: ['*.jar']) 27 | implementation 'com.android.support:appcompat-v7:27.1.1' 28 | } 29 | -------------------------------------------------------------------------------- /Sample/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /DaemonService/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /Sample/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 27 5 | defaultConfig { 6 | applicationId "com.sunfusheng.daemon.sample" 7 | minSdkVersion 14 8 | targetSdkVersion 27 9 | versionCode 1 10 | versionName "1.0" 11 | } 12 | 13 | compileOptions { 14 | sourceCompatibility JavaVersion.VERSION_1_8 15 | targetCompatibility JavaVersion.VERSION_1_8 16 | } 17 | 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | } 25 | 26 | dependencies { 27 | implementation fileTree(dir: 'libs', include: ['*.jar']) 28 | implementation 'com.android.support:appcompat-v7:27.1.1' 29 | implementation project(':DaemonService') 30 | } 31 | -------------------------------------------------------------------------------- /Sample/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /Sample/src/main/java/com/sunfusheng/daemon/sample/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.daemon.sample; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.view.View; 6 | import android.widget.TextView; 7 | 8 | import com.sunfusheng.daemon.DaemonHolder; 9 | 10 | public class MainActivity extends AppCompatActivity { 11 | private TextView vText; 12 | 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_main); 17 | vText = findViewById(R.id.text); 18 | } 19 | 20 | public void onClick(View v) { 21 | switch (v.getId()) { 22 | case R.id.startService: 23 | DaemonHolder.startService(); 24 | break; 25 | case R.id.stopService: 26 | DaemonHolder.stopService(); 27 | break; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Sample/src/main/java/com/sunfusheng/daemon/sample/HeartBeatService.java: -------------------------------------------------------------------------------- 1 | package com.sunfusheng.daemon.sample; 2 | 3 | import android.os.Looper; 4 | import android.util.Log; 5 | 6 | import com.sunfusheng.daemon.AbsHeartBeatService; 7 | 8 | /** 9 | * @author sunfusheng on 2018/8/3. 10 | */ 11 | public class HeartBeatService extends AbsHeartBeatService { 12 | private static final String TAG = "---> HeartBeatService"; 13 | private static final android.os.Handler mainThreadHandler = new android.os.Handler(Looper.getMainLooper()); 14 | 15 | @Override 16 | public void onStartService() { 17 | Log.d(TAG, "onStartService()"); 18 | } 19 | 20 | @Override 21 | public void onStopService() { 22 | Log.e(TAG, "onStopService()"); 23 | } 24 | 25 | @Override 26 | public long getDelayExecutedMillis() { 27 | return 0; 28 | } 29 | 30 | @Override 31 | public long getHeartBeatMillis() { 32 | return 30 * 1000; 33 | } 34 | 35 | @Override 36 | public void onHeartBeat() { 37 | Log.d(TAG, "onHeartBeat()"); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # DaemonService 2 | Android端心跳服务与进程保活 3 | 4 | ### 使用 5 | 6 | 继承AbsHeartBeatService抽象心跳服务,在onHeartBeat()中处理自己的任务,具体保活策略不需要关心 7 | 8 | ```java 9 | public class HeartBeatService extends AbsHeartBeatService { 10 | 11 | @Override 12 | public void onStartService() { 13 | } 14 | 15 | @Override 16 | public void onStopService() { 17 | } 18 | 19 | @Override 20 | public long getHeartBeatMillis() { 21 | return 30 * 1000; 22 | } 23 | 24 | @Override 25 | public void onHeartBeat() { 26 | } 27 | } 28 | ``` 29 | 30 | 在Manifest中注册服务 31 | 32 | ```xml 33 | 34 | ``` 35 | 36 | 初始化并启动服务 37 | 38 | ```java 39 | DaemonHolder.init(this, HeartBeatService.class); 40 | ``` 41 | 42 | ### 实现思想 43 | 44 | 实现进程保活,暂时实现了双进程守护、JobService检测与拉起、进程死亡AlarmManager定时拉起、 45 | 广播监听(网络变化、开机等),同时通过Timer和TimerTask实现心跳服务。 46 | 47 | #### 1、双进程守护 48 | 49 | 双进程即本地进程和远程进程,看两个类: 50 | AbsHeartBeatService:本地进程,抽象的心跳服务 51 | DaemonService:远程进程,即守护进程 52 | 启动本地服务后会启动远程进程的服务并绑定远程服务,同时远程服务也会绑定本地进程的服务, 53 | 任何一个服务停止都会得到另一个进程的Binder通知,即刻被拉起,实现进程保活的一种方式 54 | 55 | #### 2、JobService检测与拉起 56 | 57 | Android5.0以上可以使用JobService来做定时任务,定时检测本地进程的服务是否在运行,参考JobSchedulerService, 58 | 但是个别深度定制的ROM厂商屏蔽了JobService,比如小米手机。 59 | 60 | #### 3、进程死亡AlarmManager定时拉起 61 | 62 | AlarmManager是提供一种访问系统闹钟服务的方式,允许你去设置在将来的某个时间点去执行你的应用程序。 63 | 当你的闹钟时间到时,在它上面注册的一个意图(Intent)将会被系统以广播发出,然后自动启动目标程序,如果它没有正在运行。 64 | 所以,不管是我们的本地进程还是我们的远程进程,如果他们死了,就在死的时候定一个被拉活的闹钟,等待复活。 65 | 66 | #### 4、广播监听 67 | 68 | 动态广播监听:网络变化、开屏、锁屏、解锁、点击Home键 69 | 静态广播监听:开机、连接电源、断开电源、安装应用、卸载应用 -------------------------------------------------------------------------------- /Sample/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 12 | 13 |