├── .gitignore
├── README.md
├── app
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── shihoo
│ │ └── daemonlibrary
│ │ ├── ApkHelper.java
│ │ ├── App.java
│ │ ├── MainActivity.java
│ │ └── MainWorkService.java
│ └── res
│ ├── drawable
│ ├── ic_launcher.png
│ └── icon1.png
│ ├── layout
│ └── activity_main.xml
│ └── values
│ ├── colors.xml
│ ├── strings.xml
│ └── styles.xml
├── daemonlibrary
├── .gitignore
├── build.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── java
│ └── com
│ │ └── shihoo
│ │ └── daemon
│ │ ├── AbsServiceConnection.java
│ │ ├── DaemonEnv.java
│ │ ├── ForegroundNotificationUtils.java
│ │ ├── IntentWrapper.java
│ │ ├── singlepixel
│ │ ├── ScreenManager.java
│ │ ├── ScreenReceiverUtil.java
│ │ └── SinglePixelActivity.java
│ │ ├── sync
│ │ ├── Authenticator.java
│ │ ├── AuthenticatorService.java
│ │ ├── StubProvider.java
│ │ ├── SyncAdapter.java
│ │ └── SyncService.java
│ │ ├── watch
│ │ ├── JobSchedulerService.java
│ │ ├── PlayMusicService.java
│ │ ├── WakeUpReceiver.java
│ │ ├── WatchDogService.java
│ │ └── WatchProcessPrefHelper.java
│ │ └── work
│ │ └── AbsWorkService.java
│ └── res
│ ├── drawable
│ └── icon1.png
│ ├── raw
│ └── no_notice.mp3
│ └── values
│ ├── strings.xml
│ └── styles.xml
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches/build_file_checksums.ser
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | .DS_Store
9 | /build
10 | /captures
11 | .externalNativeBuild
12 | .idea/
13 | gradle/
14 | gradlew
15 | gradlew.bat
16 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # DaemonLibrary
2 |
3 | ## 使用方式
4 | #### Step 1. Add the JitPack repository to your build file
5 |
6 | ```
7 | allprojects {
8 | repositories {
9 | ...
10 | maven { url 'https://jitpack.io' }
11 | }
12 | }
13 | ```
14 |
15 | #### Step 2. Add the dependency
16 | [](https://jitpack.io/#ShihooWang/DaemonLibrary)
17 | ~~~
18 | dependencies {
19 | implementation 'com.github.wangshihu123:DaemonLibrary:v1.2.1'
20 | }
21 | ~~~
22 |
23 | ## Android后台保活,这里有你需要的所有姿势。2019,基于API26 Android O 最新版本。
24 | 老规矩,先上项目地址:
25 | https://github.com/wangshihu123/DaemonLibrary
26 | 结合网上各路大神及自己的项目保活实战(在此不方便透露项目),给出了最新的保活姿势。(如有雷同,纯属巧合)
27 |
28 | ## 1.为什么要做Android保活?
29 | 首先我个人并不推荐也不喜欢手机应用通过各种手段后台保活,当我们确定一定以及肯定地需要这个功能的时候,
30 | 也就只能硬着头皮去与各个手机的后台管理机制做斗争了。
31 | (一句话,产品需求呗)
32 |
33 | ## 2.故事背景
34 | 我们的需求是:开启户外运动,需要永驻后台,采集收据,使用过咕咚、悦跑圈的都知道。
35 | 但是不同的机型及厂家,让我们的应用无时无刻地在后台被杀。泪牛满面。。。
36 | 渡尽劫波兄弟在-----做IM推送的小伙伴同样有这样的情况:
37 |
38 | http://www.52im.net/forum.php?mod=viewthread&tid=429&highlight=%B1%A3%BB%EE
39 |
40 | 还有应用保活终极系列(1-3):(分析了咕咚,乐动力,悦动圈。捎带着科普了微信和QQ)
41 |
42 | http://www.52im.net/forum.php?mod=viewthread&tid=1135&highlight=%B1%A3%BB%EE
43 |
44 | 十八路神仙,各显神通。
45 |
46 |
47 | ## 3.保活总结
48 | 对于后台保活的各种手段,在网络上比比皆是,总结下来基本是如下几种:
49 | 1.开启服务,设置服务杀死重生;
50 | 2.开启服务,发送通知,设置为前台服务;
51 | 3.双进程保活;
52 | 4.检测各种系统广播启动应用;
53 | 5.息屏打开1像素点Activity;(QQ这么干的)
54 | 6.开启服务,播放无声音乐(七伤拳,定制OS出现锁屏 显示音乐播放界面,及其恶心,比如 miui);
55 | 7.优化应用内存(敲黑板,划重点);
56 | 以上这些方式在网上都可以查询到,但是因为android版本不同rom不同,不一定都能派上用场,可自行查找。
57 |
58 | 这七种方法,最优秀的无非是最后一种,但我总是不去考虑他,真是坏习惯。
59 |
60 | ## 4.保活战
61 |
62 | 在这次保活战中一共涉及了个品牌的手机:
63 | ##### 1).随意蹂躏系:
64 | Nexus5、Nexus6、Sony Z5、LG G4、LG G5、Samsung S6 S7(未升级到最新版本)
65 | ##### 2).尚有尊严系:
66 | 小米5X、魅族Note6、OPPO R11、VIVO X9柔光双摄照亮你的美(...洗脑真可怕)、Samsung J3 J5(升级到最新版本)、华为P9 P10、荣耀8(当你在后台啥都不做的时候,或者稍微动了一下)
67 | ##### 3).宁死不屈系:
68 | 华为P9 P10 mate10 v20 荣耀8(当你在后台动个不停的时候)
69 |
70 | 对于随意蹂躏系,请你一定要好好照顾它。它们提供了原生或者接近原生的后台管理机制,是因为相信每个应用都是善良的,所以,不到万不得已,不要欺负他们;
71 |
72 | 对于尚有尊严系,多为定制程度较高的第三方ROM,杀死后台也多处于其定制的功耗管理机制,尝试过很多灵性方法,很难做到一招鲜吃遍天,但这些ROM都留下了功耗管理白名单,他们需要保证自己系统地流畅运行,同时他们也考虑到了有些应用有他们不得不说的苦(交)衷(易),所以尊重ROM厂商的限制,不要作妖,有需求,打开白名单,你好,我好,大家好。
73 |
74 | 最后是宁死不屈系,这也是遇到的最大的难题,前面有提到我的应用不仅需要常驻后台,更需要在后台接收设备发出的蓝牙数据,也就是说我需要在后台搞事情。
75 |
76 | ## 5.围攻光明顶
77 | 以下的故事发生于我按照华为的显示开启了功耗管理白名单、后台清理白名单、忽略电量优化白名单。
78 | 于是号称是18个月不卡顿的华为出现了,也成功制裁了我:
79 | 首先是蓝牙广播模式,当你息屏五分钟之后,由后台发起的蓝牙扫描就被休眠了,GG;
80 | 然后是连接模式,息屏后运行一小时,凉凉;
81 | 定位和请求网络,也是被限制的不要不要;
82 | 服务重生+前台服务+双进程守护,六神装+复活甲在手,依旧被华为按在地上摩擦。
83 | 直到最后,武林中流传着这样一套拳谱,伤敌一千自损八百,名曰七伤拳:无声音乐保活大法;
84 | 也就是在服务中循环播放一段无声的音乐,cosplay正在播放的音乐播放器。
85 | 没错,确实在华为18个月不卡顿的后台管理下活了下来,但代价是飙升的功耗,以及多任务菜单提示的音乐播放icon。
86 | 但对于我这种特殊的应用来说,能够常驻后台,持续监测和记录,才是最重要的。
87 |
88 | ## 6.再续前缘
89 | At last,还是想聊一下各个rom做出的后台限制。
90 | 对于开发者来说,最欢迎的当然是原生这种随意蹂躏系,但是汝之蜜糖,彼之砒霜,这种策略如果在流氓肆虐的国内市场,估计早被啃得渣都不剩了。
91 | 所以我个人觉得在国内市场环境下,尚有尊严系的做法挺好的,有需求就手动开启,各取所需,一切由用户决定;
92 | 至于宁死不屈的华为,为了达到18个月不卡顿的效果,做出这种惨绝人寰的后台三光策略,有点不近人情,有点过分。
93 |
94 | 希望国内的应用市场流氓越来越少,Android手机越来越好用。
95 |
96 |
97 | ## 7.关于监听推送通知拉活的办法
98 | 消息推送(华为):https://blog.csdn.net/qingjiao233/article/details/79914893
99 | Android端外推送到底有多烦:
100 | https://mp.weixin.qq.com/s?__biz=MzA4NTg1MjM0Mg==&mid=2657261350&idx=1&sn=6cea730ef5a144ac243f07019fb43076%23rd
101 |
102 | 我想到市面上的推送(排名不分先后):
103 | 小米推送(MiPush)
104 | 华为推送(华为Push)
105 | 友盟推送(U-Push)
106 | 个推
107 | 极光推送
108 | 阿里云移动推送(Alibaba Cloud Channel Service)
109 | 腾讯信鸽推送
110 | 百度云推送
111 |
112 | 如果每一个都接入,是不是要疯#24
113 |
114 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 28
5 | defaultConfig {
6 | applicationId "com.shihoo.daemonlibrary"
7 | minSdkVersion 18
8 | targetSdkVersion 26
9 | versionCode 1
10 | versionName "1.0"
11 | }
12 | buildTypes {
13 | release {
14 | minifyEnabled false
15 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
16 | }
17 | }
18 | }
19 |
20 | dependencies {
21 | implementation fileTree(include: ['*.jar'], dir: 'libs')
22 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
23 | // implementation 'com.github.wangshihu123:DaemonLibrary:v0.0.3'
24 | implementation project(':daemonlibrary')
25 | }
26 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shihoo/daemonlibrary/ApkHelper.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemonlibrary;
2 |
3 | import android.app.ActivityManager;
4 | import android.content.Context;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * Created by shihoo ON 2018/12/13.
10 | * Email shihu.wang@bodyplus.cc 451082005@qq.com
11 | */
12 | public class ApkHelper {
13 |
14 | public static String getProcessName(Context context){
15 | int pid = android.os.Process.myPid();
16 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
17 | if (am == null) {
18 | return null;
19 | }
20 | List processes = am.getRunningAppProcesses();
21 | if (processes == null){
22 | return null;
23 | }
24 | for (ActivityManager.RunningAppProcessInfo info : processes){
25 | if (info.pid == pid){
26 | return info.processName;
27 | }
28 | }
29 | return null;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shihoo/daemonlibrary/App.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemonlibrary;
2 |
3 | import android.app.Application;
4 | import android.util.Log;
5 |
6 | import com.shihoo.daemon.DaemonEnv;
7 | import com.shihoo.daemon.ForegroundNotificationUtils;
8 | import com.shihoo.daemon.watch.WatchProcessPrefHelper;
9 |
10 | /**
11 | * Created by shihoo ON 2018/12/13.
12 | * Email shihu.wang@bodyplus.cc 451082005@qq.com
13 | */
14 | public class App extends Application {
15 |
16 | @Override
17 | public void onCreate() {
18 | super.onCreate();
19 | //需要在 Application 的 onCreate() 中调用一次 DaemonEnv.initialize()
20 | // 每一次创建进程的时候都需要对Daemon环境进行初始化,所以这里没有判断进程
21 |
22 |
23 | String processName = ApkHelper.getProcessName(this.getApplicationContext());
24 | if ("com.shihoo.daemonlibrary".equals(processName)){
25 | // 主进程 进行一些其他的操作
26 | Log.d("wsh-daemon", "启动主进程");
27 |
28 | }else if ("com.shihoo.daemonlibrary:work".equals(processName)){
29 | Log.d("wsh-daemon", "启动了工作进程");
30 | }else if ("com.shihoo.daemonlibrary:watch".equals(processName)){
31 | // 这里要设置下看护进程所启动的主进程信息
32 | WatchProcessPrefHelper.mWorkServiceClass = MainWorkService.class;
33 | // 设置通知栏的UI
34 | ForegroundNotificationUtils.setResId(R.drawable.ic_launcher);
35 | ForegroundNotificationUtils.setNotifyTitle("我是");
36 | ForegroundNotificationUtils.setNotifyContent("渣渣辉");
37 | Log.d("wsh-daemon", "启动了看门狗进程");
38 | }
39 |
40 |
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shihoo/daemonlibrary/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemonlibrary;
2 |
3 | import android.app.Activity;
4 | import android.app.Notification;
5 | import android.app.NotificationChannel;
6 | import android.app.NotificationManager;
7 | import android.content.Context;
8 | import android.graphics.BitmapFactory;
9 | import android.graphics.Color;
10 | import android.os.Build;
11 | import android.os.Bundle;
12 | import android.view.View;
13 |
14 | import com.shihoo.daemon.DaemonEnv;
15 | import com.shihoo.daemon.IntentWrapper;
16 | import com.shihoo.daemon.watch.WatchProcessPrefHelper;
17 |
18 | public class MainActivity extends Activity {
19 |
20 | //是否 任务完成, 不再需要服务运行? 最好使用SharePreference,注意要在同一进程中访问该属性
21 | public static boolean isCanStartWorkService;
22 |
23 | @Override
24 | protected void onCreate(Bundle savedInstanceState) {
25 | super.onCreate(savedInstanceState);
26 | setContentView(R.layout.activity_main);
27 | }
28 |
29 | public void onClick(View v) {
30 | switch (v.getId()) {
31 | case R.id.btn_start:
32 |
33 | DaemonEnv.sendStartWorkBroadcast(this);
34 | v.postDelayed(new Runnable() {
35 | @Override
36 | public void run() {
37 | isCanStartWorkService = true;
38 | DaemonEnv.startServiceSafely(MainActivity.this,MainWorkService.class);
39 | }
40 | },1000);
41 |
42 | // buildNotify(this);
43 | break;
44 | case R.id.btn_white:
45 | IntentWrapper.whiteListMatters(this, "轨迹跟踪服务的持续运行");
46 | break;
47 | case R.id.btn_stop:
48 | value ++;
49 | // buildNotify(this);
50 | DaemonEnv.sendStopWorkBroadcast(this);
51 | isCanStartWorkService = false;
52 | break;
53 | }
54 | }
55 |
56 | //防止华为机型未加入白名单时按返回键回到桌面再锁屏后几秒钟进程被杀
57 | public void onBackPressed() {
58 | IntentWrapper.onBackPressed(this);
59 | }
60 |
61 | private static final String CHANNEL_ID = "保活图腾";
62 | private static final int CHANNEL_POSITION = 1;
63 | private int value;
64 |
65 | private void buildNotify(Context service){
66 | NotificationManager manager = (NotificationManager)service.getSystemService(Context.NOTIFICATION_SERVICE);
67 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
68 | NotificationChannel channel = new NotificationChannel(CHANNEL_ID,"主服务",
69 | NotificationManager.IMPORTANCE_DEFAULT);
70 | //是否绕过请勿打扰模式
71 | channel.canBypassDnd();
72 | //闪光灯
73 | channel.enableLights(true);
74 | //锁屏显示通知
75 | channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
76 | //闪关灯的灯光颜色
77 | channel.setLightColor(Color.RED);
78 | //桌面launcher的消息角标
79 | channel.canShowBadge();
80 | //是否允许震动
81 | channel.enableVibration(true);
82 | //获取系统通知响铃声音的配置
83 | channel.getAudioAttributes();
84 | //获取通知取到组
85 | channel.getGroup();
86 | //设置可绕过 请勿打扰模式
87 | channel.setBypassDnd(true);
88 | //设置震动模式
89 | channel.setVibrationPattern(new long[]{100, 100, 200});
90 | //是否会有灯光
91 | channel.shouldShowLights();
92 | manager.createNotificationChannel(channel);
93 | Notification notification = new Notification.Builder(service,CHANNEL_ID)
94 | .setContentTitle("我是通知哦哦")//设置标题
95 | .setContentText("我是通知内容..."+value)//设置内容
96 | .setWhen(System.currentTimeMillis())//设置创建时间
97 | .setSmallIcon(com.shihoo.daemon.R.drawable.icon1)//设置状态栏图标
98 | .setLargeIcon(BitmapFactory.decodeResource(service.getResources(), com.shihoo.daemon.R.drawable.icon1))//设置通知栏图标
99 | .build();
100 | manager.notify(CHANNEL_POSITION,notification);
101 | }else {
102 | Notification notification = new Notification.Builder(service)
103 | .setContentTitle("我是通知哦哦")//设置标题
104 | .setContentText("我是通知内容..."+value)//设置内容
105 | .setWhen(System.currentTimeMillis())//设置创建时间
106 | .setSmallIcon(com.shihoo.daemon.R.drawable.icon1)//设置状态栏图标
107 | .setLargeIcon(BitmapFactory.decodeResource(service.getResources(), com.shihoo.daemon.R.drawable.icon1))//设置通知栏图标
108 | .build();
109 | manager.notify(CHANNEL_POSITION,notification);
110 | }
111 | }
112 |
113 | }
114 |
--------------------------------------------------------------------------------
/app/src/main/java/com/shihoo/daemonlibrary/MainWorkService.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemonlibrary;
2 |
3 | import android.content.Intent;
4 | import android.os.Handler;
5 | import android.os.IBinder;
6 | import android.os.Messenger;
7 | import android.util.Log;
8 |
9 | import com.shihoo.daemon.work.AbsWorkService;
10 |
11 | import java.util.concurrent.TimeUnit;
12 |
13 | import io.reactivex.Observable;
14 | import io.reactivex.disposables.Disposable;
15 | import io.reactivex.functions.Action;
16 | import io.reactivex.functions.Consumer;
17 |
18 |
19 | /**
20 | * Created by shihoo ON 2018/12/13.
21 | * Email shihu.wang@bodyplus.cc 451082005@qq.com
22 | */
23 | public class MainWorkService extends AbsWorkService {
24 |
25 | private Disposable mDisposable;
26 | private long mSaveDataStamp;
27 |
28 | /**
29 | * 是否 任务完成, 不再需要服务运行?
30 | * @return 应当停止服务, true; 应当启动服务, false; 无法判断, 什么也不做, null.
31 | */
32 | @Override
33 | public Boolean needStartWorkService() {
34 | return MainActivity.isCanStartWorkService;
35 | }
36 |
37 | /**
38 | * 任务是否正在运行?
39 | * @return 任务正在运行, true; 任务当前不在运行, false; 无法判断, 什么也不做, null.
40 | */
41 | @Override
42 | public Boolean isWorkRunning() {
43 | //若还没有取消订阅, 就说明任务仍在运行.
44 | return mDisposable != null && !mDisposable.isDisposed();
45 | }
46 |
47 | @Override
48 | public IBinder onBindService(Intent intent, Void v) {
49 | // 此处必须有返回,否则绑定无回调
50 | return new Messenger(new Handler()).getBinder();
51 | }
52 |
53 | @Override
54 | public void onServiceKilled() {
55 | saveData();
56 | Log.d("wsh-daemon", "onServiceKilled --- 保存数据到磁盘");
57 | }
58 |
59 | @Override
60 | public void stopWork() {
61 | //取消对任务的订阅
62 | if (mDisposable !=null && !mDisposable.isDisposed()){
63 | mDisposable.dispose();
64 | }
65 | saveData();
66 | }
67 |
68 | @Override
69 | public void startWork() {
70 | Log.d("wsh-daemon", "检查磁盘中是否有上次销毁时保存的数据");
71 | mDisposable = Observable
72 | .interval(3, TimeUnit.SECONDS)
73 | //取消任务时取消定时唤醒
74 | .doOnDispose(new Action() {
75 | @Override
76 | public void run() throws Exception {
77 | Log.d("wsh-daemon", " -- doOnDispose --- 取消订阅 .... ");
78 | saveData();
79 | }
80 | })
81 | .subscribe(new Consumer() {
82 | @Override
83 | public void accept(Long aLong) throws Exception {
84 | Log.d("wsh-daemon", "每 3 秒采集一次数据... count = " + aLong);
85 | if (aLong > 0 && aLong % 18 == 0){
86 | saveData();
87 | Log.d("wsh-daemon", " 采集数据 saveCount = " + (aLong / 18 - 1));
88 | }
89 | }
90 | });
91 | }
92 |
93 |
94 | private void saveData(){
95 | long stamp = System.currentTimeMillis()/1000;
96 | if (Math.abs(mSaveDataStamp - stamp) >= 3){
97 | // 处理业务逻辑
98 | Log.d("wsh-daemon", "保存数据到磁盘。");
99 | }
100 | mSaveDataStamp = stamp;
101 | }
102 |
103 | }
104 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShihooWang/DaemonLibrary/HEAD/app/src/main/res/drawable/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable/icon1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShihooWang/DaemonLibrary/HEAD/app/src/main/res/drawable/icon1.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
15 |
16 |
17 |
18 |
24 |
25 |
31 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #008577
4 | #00574B
5 | #D81B60
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | DaemonLibrary
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/daemonlibrary/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/daemonlibrary/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 | group='shihoo.wang'
4 |
5 | android {
6 | compileSdkVersion 28
7 |
8 |
9 | defaultConfig {
10 | minSdkVersion 18
11 | targetSdkVersion 26
12 | versionCode 1
13 | versionName "1.0"
14 |
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | }
25 |
26 | dependencies {
27 | api 'io.reactivex.rxjava2:rxjava:2.+'
28 | api 'com.android.support:support-annotations:+'
29 | }
30 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
13 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
35 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
73 |
74 |
77 |
78 |
86 |
87 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/AbsServiceConnection.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon;
2 |
3 | import android.content.ComponentName;
4 | import android.content.ServiceConnection;
5 | import android.os.IBinder;
6 |
7 | /**
8 | * Created by shihoo ON 2018/12/14.
9 | * Email shihu.wang@bodyplus.cc 451082005@qq.com
10 | *
11 | */
12 | public abstract class AbsServiceConnection implements ServiceConnection {
13 |
14 | // 当前绑定的状态
15 | public boolean mConnectedState = false;
16 |
17 | @Override
18 | public void onServiceConnected(ComponentName name, IBinder service) {
19 | mConnectedState = true;
20 | }
21 |
22 | @Override
23 | public void onServiceDisconnected(ComponentName name) {
24 | if (mConnectedState) {
25 | mConnectedState = false;
26 | onDisconnected(name);
27 | }
28 | }
29 |
30 | @Override
31 | public void onBindingDied(ComponentName name) {
32 | onServiceDisconnected(name);
33 | }
34 |
35 | public abstract void onDisconnected(ComponentName name);
36 | }
37 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/DaemonEnv.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon;
2 |
3 | import android.app.Service;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Build;
7 | import android.support.annotation.NonNull;
8 | import android.util.Log;
9 |
10 | import com.shihoo.daemon.work.AbsWorkService;
11 |
12 |
13 | /**
14 | * daemon 环境配置 每个进程独享一份 不同的进程,所有的静态和单例都会失效
15 | *
16 | * 这里将Daemon类 做成工具类
17 | *
18 | * 每一个进程都会有一个Daemon类
19 | *
20 | */
21 | public final class DaemonEnv {
22 |
23 | /**
24 | * 向 WakeUpReceiver 发送带有此 Action 的广播, 即可在不需要服务运行的时候取消 Job / Alarm / Subscription.
25 | */
26 | public static final String ACTION_START_JOB_ALARM_SUB = "com.shihoo.START_JOB_ALARM_SUB";
27 | public static final String ACTION_CANCEL_JOB_ALARM_SUB = "com.shihoo.CANCEL_JOB_ALARM_SUB";
28 |
29 | public static final int DEFAULT_WAKE_UP_INTERVAL = 2 * 60 * 1000; // 默认JobScheduler 唤醒时间为 2 分钟
30 | public static final int MINIMAL_WAKE_UP_INTERVAL = 60 * 1000; // 最小时间为 1 分钟
31 |
32 | public static void startServiceMayBind(@NonNull final Context context,
33 | @NonNull final Class extends Service> serviceClass,
34 | @NonNull AbsServiceConnection connection) {
35 |
36 | // 判断当前绑定的状态
37 | if (!connection.mConnectedState) {
38 | Log.d("wsh-daemon", "启动并绑定服务 :"+serviceClass.getSimpleName());
39 | final Intent intent = new Intent(context, serviceClass);
40 | startServiceSafely(context, serviceClass);
41 | context.bindService(intent, connection, Context.BIND_AUTO_CREATE);
42 | }
43 | }
44 |
45 | public static void startServiceSafely(Context context, Class extends Service> i) {
46 | Log.d("wsh-daemon", "安全启动服务。。: "+i.getSimpleName());
47 | try {
48 | if (Build.VERSION.SDK_INT >= 26){
49 | context.startForegroundService(new Intent(context,i));
50 | }else {
51 | context.startService(new Intent(context,i));
52 | }
53 | } catch (Exception e) {
54 | e.printStackTrace();
55 | }
56 | }
57 |
58 | public static int getWakeUpInterval(int sWakeUpInterval) {
59 | return Math.max(sWakeUpInterval, MINIMAL_WAKE_UP_INTERVAL);
60 | }
61 |
62 |
63 | public static void safelyUnbindService(Service service, AbsServiceConnection mConnection){
64 | try{
65 | if (mConnection.mConnectedState) {
66 | service.unbindService(mConnection);
67 | }
68 | }catch(Exception e){
69 | e.printStackTrace();
70 | }
71 | }
72 |
73 | /**
74 | * 当前哪个进程使用的时候 就用其上下文发送广播
75 | *
76 | * 如果是同一进程,可以自定义启动方式 不使用广播的模式
77 | */
78 | public static void sendStartWorkBroadcast(Context context) {
79 | Log.d("wsh-daemon", "发送开始广播。。。。");
80 | // 以广播的形式通知所有进程终止
81 | context.sendBroadcast(new Intent(ACTION_START_JOB_ALARM_SUB));
82 | }
83 |
84 |
85 | /**
86 | * 当前哪个进程使用的时候 就用其上下文发送广播
87 | */
88 | public static void sendStopWorkBroadcast(Context context) {
89 | Log.d("wsh-daemon", "发送停止广播。。。。");
90 | // 以广播的形式通知所有进程终止
91 | context.sendBroadcast(new Intent(ACTION_CANCEL_JOB_ALARM_SUB));
92 | }
93 |
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/ForegroundNotificationUtils.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon;
2 |
3 | import android.app.Notification;
4 | import android.app.NotificationChannel;
5 | import android.app.NotificationManager;
6 | import android.app.Service;
7 | import android.content.Context;
8 | import android.graphics.BitmapFactory;
9 | import android.graphics.Color;
10 | import android.os.Build;
11 | import android.support.annotation.DrawableRes;
12 |
13 | /**
14 | * Created by Shihoo.Wang 2019/7/11
15 | * Email shihu.wang@bodyplus.cc 451082005@qq.com
16 | */
17 | public class ForegroundNotificationUtils {
18 | // 通知渠道的id
19 | private static String CHANNEL_ID = "shihoo_daemon";
20 | private static int CHANNEL_POSITION = 1;
21 | private static String NotifyTitle = "主服务";
22 | private static String NotifyContent = "主服务";
23 | private static int ResId = R.drawable.icon1;
24 |
25 | public static void setResId(@DrawableRes int resId) {
26 | ForegroundNotificationUtils.ResId = resId;
27 | }
28 |
29 | public static void setNotifyTitle(String title){
30 | if (null != title){
31 | ForegroundNotificationUtils.NotifyTitle = title;
32 | }
33 | }
34 |
35 | public static void setNotifyContent(String content){
36 | if (null != content){
37 | ForegroundNotificationUtils.NotifyContent = content;
38 | }
39 | }
40 |
41 | public static void setChannelId(String channelId){
42 | if (null != channelId){
43 | ForegroundNotificationUtils.CHANNEL_ID = channelId;
44 | }
45 | }
46 |
47 | public static void setChannelPosition(int position){
48 | if (position >= 0){
49 | ForegroundNotificationUtils.CHANNEL_POSITION = position;
50 | }
51 | }
52 |
53 | // private static String notifyNma = "主服务";
54 | public static void startForegroundNotification(Service service){
55 |
56 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O){
57 | //启动前台服务而不显示通知的漏洞已在 API Level 25 修复
58 | NotificationManager manager = (NotificationManager)service.getSystemService(Context.NOTIFICATION_SERVICE);
59 | NotificationChannel Channel = new NotificationChannel(CHANNEL_ID,NotifyTitle,NotificationManager.IMPORTANCE_DEFAULT);
60 | Channel.enableLights(true);//设置提示灯
61 | Channel.setLightColor(Color.GREEN);//设置提示灯颜色
62 | Channel.setShowBadge(true);//显示logo
63 | Channel.setDescription("");//设置描述
64 | Channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC); //设置锁屏可见 VISIBILITY_PUBLIC=可见
65 | Channel.enableVibration(false);
66 | Channel.setSound(null,null);
67 | manager.createNotificationChannel(Channel);
68 |
69 | Notification notification = new Notification.Builder(service,CHANNEL_ID)
70 | .setContentTitle(NotifyTitle)//标题
71 | .setContentText(NotifyContent)//内容
72 | .setWhen(System.currentTimeMillis())
73 | .setSmallIcon(ResId)//小图标一定需要设置,否则会报错(如果不设置它启动服务前台化不会报错,但是你会发现这个通知不会启动),如果是普通通知,不设置必然报错
74 | .setLargeIcon(BitmapFactory.decodeResource(service.getResources(),ResId))
75 | .build();
76 | service.startForeground(CHANNEL_POSITION,notification);//服务前台化只能使用startForeground()方法,不能使用 notificationManager.notify(1,notification); 这个只是启动通知使用的,使用这个方法你只需要等待几秒就会发现报错了
77 | }else {
78 | //利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知
79 | // service.startForeground(Foreground_ID, new Notification());
80 | Notification notification = new Notification.Builder(service)
81 | .setContentTitle(NotifyTitle)//设置标题
82 | .setContentText(NotifyContent)//设置内容
83 | .setWhen(System.currentTimeMillis())//设置创建时间
84 | .setSmallIcon(ResId)//设置状态栏图标
85 | .setLargeIcon(BitmapFactory.decodeResource(service.getResources(),ResId))//设置通知栏图标
86 | .build();
87 | service.startForeground(CHANNEL_POSITION,notification);
88 | }
89 | }
90 |
91 | public static void deleteForegroundNotification(Service service){
92 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
93 | NotificationManager mNotificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE);
94 | NotificationChannel mChannel = mNotificationManager.getNotificationChannel(CHANNEL_ID);
95 | if (null != mChannel) {
96 | mNotificationManager.deleteNotificationChannel(CHANNEL_ID);
97 | }
98 | }else {
99 | NotificationManager notificationManager = (NotificationManager) service.getSystemService(Context.NOTIFICATION_SERVICE);
100 | notificationManager.cancel(CHANNEL_POSITION);
101 | }
102 | }
103 |
104 |
105 | // private void oldStartForegNotify(){
106 | //启动前台服务而不显示通知的漏洞已在 API Level 25 修复,大快人心!
107 | // if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.N) {
108 | // //利用漏洞在 API Level 17 及以下的 Android 系统中,启动前台服务而不显示通知
109 | // startForeground(HASH_CODE, new Notification());
110 | // //利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知
111 | // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
112 | // Boolean needStartWorkService = needStartWorkService(null, 0, 0);
113 | // DaemonEnv.startServiceSafely(AbsWorkService.this,
114 | // WorkNotificationService.class,
115 | // needStartWorkService);
116 | // }
117 | // }
118 | // }
119 |
120 | // public static class WorkNotificationService extends Service {
121 | //
122 | // /**
123 | // * 利用漏洞在 API Level 18 及以上的 Android 系统中,启动前台服务而不显示通知
124 | // */
125 | // @Override
126 | // public int onStartCommand(Intent intent, int flags, int startId) {
127 | // startForeground(1, new Notification());
128 | // stopSelf();
129 | // return START_STICKY;
130 | // }
131 | //
132 | // @Override
133 | // public IBinder onBind(Intent intent) {
134 | // return null;
135 | // }
136 | // }
137 | }
138 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/IntentWrapper.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon;
2 |
3 | import android.app.Activity;
4 | import android.app.AlertDialog;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.DialogInterface;
8 | import android.content.Intent;
9 | import android.content.pm.ApplicationInfo;
10 | import android.content.pm.PackageManager;
11 | import android.content.pm.ResolveInfo;
12 | import android.net.Uri;
13 | import android.os.Build;
14 | import android.os.PowerManager;
15 | import android.provider.Settings;
16 | import android.support.annotation.NonNull;
17 |
18 | import java.util.ArrayList;
19 | import java.util.List;
20 |
21 | public class IntentWrapper {
22 |
23 | //Android 7.0+ Doze 模式
24 | protected static final int DOZE = 98;
25 | //华为 自启管理
26 | protected static final int HUAWEI = 99;
27 | //华为 锁屏清理
28 | protected static final int HUAWEI_GOD = 100;
29 | //小米 自启动管理
30 | protected static final int XIAOMI = 101;
31 | //小米 神隐模式
32 | protected static final int XIAOMI_GOD = 102;
33 | //三星 5.0/5.1 自启动应用程序管理
34 | protected static final int SAMSUNG_L = 103;
35 | //魅族 自启动管理
36 | protected static final int MEIZU = 104;
37 | //魅族 待机耗电管理
38 | protected static final int MEIZU_GOD = 105;
39 | //Oppo 自启动管理
40 | protected static final int OPPO = 106;
41 | //三星 6.0+ 未监视的应用程序管理
42 | protected static final int SAMSUNG_M = 107;
43 | //Oppo 自启动管理(旧版本系统)
44 | protected static final int OPPO_OLD = 108;
45 | //Vivo 后台高耗电
46 | protected static final int VIVO_GOD = 109;
47 | //金立 应用自启
48 | protected static final int GIONEE = 110;
49 | //乐视 自启动管理
50 | protected static final int LETV = 111;
51 | //乐视 应用保护
52 | protected static final int LETV_GOD = 112;
53 | //酷派 自启动管理
54 | protected static final int COOLPAD = 113;
55 | //联想 后台管理
56 | protected static final int LENOVO = 114;
57 | //联想 后台耗电优化
58 | protected static final int LENOVO_GOD = 115;
59 | //中兴 自启管理
60 | protected static final int ZTE = 116;
61 | //中兴 锁屏加速受保护应用
62 | protected static final int ZTE_GOD = 117;
63 |
64 | // protected static List sIntentWrapperList;
65 |
66 | public static List getIntentWrapperList(Context context) {
67 | // if (sIntentWrapperList == null) {
68 |
69 | List sIntentWrapperList = new ArrayList<>();
70 | // sIntentWrapperList = new ArrayList<>();
71 |
72 | //Android 7.0+ Doze 模式
73 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
74 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
75 | boolean ignoringBatteryOptimizations = pm.isIgnoringBatteryOptimizations(context.getPackageName());
76 | if (!ignoringBatteryOptimizations) {
77 | Intent dozeIntent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
78 | dozeIntent.setData(Uri.parse("package:" + context.getPackageName()));
79 | sIntentWrapperList.add(new IntentWrapper(dozeIntent, DOZE));
80 | }
81 | }
82 |
83 | //华为 自启管理
84 | Intent huaweiIntent = new Intent();
85 | huaweiIntent.setAction("huawei.intent.action.HSM_BOOTAPP_MANAGER");
86 | sIntentWrapperList.add(new IntentWrapper(huaweiIntent, HUAWEI));
87 |
88 | //华为 锁屏清理
89 | Intent huaweiGodIntent = new Intent();
90 | huaweiGodIntent.setComponent(new ComponentName("com.huawei.systemmanager", "com.huawei.systemmanager.optimize.process.ProtectActivity"));
91 | sIntentWrapperList.add(new IntentWrapper(huaweiGodIntent, HUAWEI_GOD));
92 |
93 | //小米 自启动管理
94 | Intent xiaomiIntent = new Intent();
95 | xiaomiIntent.setAction("miui.intent.action.OP_AUTO_START");
96 | xiaomiIntent.addCategory(Intent.CATEGORY_DEFAULT);
97 | sIntentWrapperList.add(new IntentWrapper(xiaomiIntent, XIAOMI));
98 |
99 | //小米 神隐模式
100 | Intent xiaomiGodIntent = new Intent();
101 | xiaomiGodIntent.setComponent(new ComponentName("com.miui.powerkeeper", "com.miui.powerkeeper.ui.HiddenAppsConfigActivity"));
102 | xiaomiGodIntent.putExtra("package_name", context.getPackageName());
103 | xiaomiGodIntent.putExtra("package_label", getApplicationName(context));
104 | sIntentWrapperList.add(new IntentWrapper(xiaomiGodIntent, XIAOMI_GOD));
105 |
106 | //三星 5.0/5.1 自启动应用程序管理
107 | Intent samsungLIntent = context.getPackageManager().getLaunchIntentForPackage("com.samsung.android.sm");
108 | if (samsungLIntent != null) sIntentWrapperList.add(new IntentWrapper(samsungLIntent, SAMSUNG_L));
109 |
110 | //三星 6.0+ 未监视的应用程序管理
111 | Intent samsungMIntent = new Intent();
112 | samsungMIntent.setComponent(new ComponentName("com.samsung.android.sm_cn", "com.samsung.android.sm.ui.battery.BatteryActivity"));
113 | sIntentWrapperList.add(new IntentWrapper(samsungMIntent, SAMSUNG_M));
114 |
115 | //魅族 自启动管理
116 | Intent meizuIntent = new Intent("com.meizu.safe.security.SHOW_APPSEC");
117 | meizuIntent.addCategory(Intent.CATEGORY_DEFAULT);
118 | meizuIntent.putExtra("packageName", context.getPackageName());
119 | sIntentWrapperList.add(new IntentWrapper(meizuIntent, MEIZU));
120 |
121 | //魅族 待机耗电管理
122 | Intent meizuGodIntent = new Intent();
123 | meizuGodIntent.setComponent(new ComponentName("com.meizu.safe", "com.meizu.safe.powerui.PowerAppPermissionActivity"));
124 | sIntentWrapperList.add(new IntentWrapper(meizuGodIntent, MEIZU_GOD));
125 |
126 | //Oppo 自启动管理
127 | Intent oppoIntent = new Intent();
128 | oppoIntent.setComponent(new ComponentName("com.coloros.safecenter", "com.coloros.safecenter.permission.startup.StartupAppListActivity"));
129 | sIntentWrapperList.add(new IntentWrapper(oppoIntent, OPPO));
130 |
131 | //Oppo 自启动管理(旧版本系统)
132 | Intent oppoOldIntent = new Intent();
133 | oppoOldIntent.setComponent(new ComponentName("com.color.safecenter", "com.color.safecenter.permission.startup.StartupAppListActivity"));
134 | sIntentWrapperList.add(new IntentWrapper(oppoOldIntent, OPPO_OLD));
135 |
136 | //Vivo 后台高耗电
137 | Intent vivoGodIntent = new Intent();
138 | vivoGodIntent.setComponent(new ComponentName("com.vivo.abe", "com.vivo.applicationbehaviorengine.ui.ExcessivePowerManagerActivity"));
139 | sIntentWrapperList.add(new IntentWrapper(vivoGodIntent, VIVO_GOD));
140 |
141 | //金立 应用自启
142 | Intent gioneeIntent = new Intent();
143 | gioneeIntent.setComponent(new ComponentName("com.gionee.softmanager", "com.gionee.softmanager.MainActivity"));
144 | sIntentWrapperList.add(new IntentWrapper(gioneeIntent, GIONEE));
145 |
146 | //乐视 自启动管理
147 | Intent letvIntent = new Intent();
148 | letvIntent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.AutobootManageActivity"));
149 | sIntentWrapperList.add(new IntentWrapper(letvIntent, LETV));
150 |
151 | //乐视 应用保护
152 | Intent letvGodIntent = new Intent();
153 | letvGodIntent.setComponent(new ComponentName("com.letv.android.letvsafe", "com.letv.android.letvsafe.BackgroundAppManageActivity"));
154 | sIntentWrapperList.add(new IntentWrapper(letvGodIntent, LETV_GOD));
155 |
156 | //酷派 自启动管理
157 | Intent coolpadIntent = new Intent();
158 | coolpadIntent.setComponent(new ComponentName("com.yulong.android.security", "com.yulong.android.seccenter.tabbarmain"));
159 | sIntentWrapperList.add(new IntentWrapper(coolpadIntent, COOLPAD));
160 |
161 | //联想 后台管理
162 | Intent lenovoIntent = new Intent();
163 | lenovoIntent.setComponent(new ComponentName("com.lenovo.security", "com.lenovo.security.purebackground.PureBackgroundActivity"));
164 | sIntentWrapperList.add(new IntentWrapper(lenovoIntent, LENOVO));
165 |
166 | //联想 后台耗电优化
167 | Intent lenovoGodIntent = new Intent();
168 | lenovoGodIntent.setComponent(new ComponentName("com.lenovo.powersetting", "com.lenovo.powersetting.ui.Settings$HighPowerApplicationsActivity"));
169 | sIntentWrapperList.add(new IntentWrapper(lenovoGodIntent, LENOVO_GOD));
170 |
171 | //中兴 自启管理
172 | Intent zteIntent = new Intent();
173 | zteIntent.setComponent(new ComponentName("com.zte.heartyservice", "com.zte.heartyservice.autorun.AppAutoRunManager"));
174 | sIntentWrapperList.add(new IntentWrapper(zteIntent, ZTE));
175 |
176 | //中兴 锁屏加速受保护应用
177 | Intent zteGodIntent = new Intent();
178 | zteGodIntent.setComponent(new ComponentName("com.zte.heartyservice", "com.zte.heartyservice.setting.ClearAppSettingsActivity"));
179 | sIntentWrapperList.add(new IntentWrapper(zteGodIntent, ZTE_GOD));
180 | // }
181 | return sIntentWrapperList;
182 | }
183 |
184 |
185 | public static String getApplicationName(Context context) {
186 | String sApplicationName = "";
187 | PackageManager pm;
188 | ApplicationInfo ai;
189 | try {
190 | pm = context.getPackageManager();
191 | ai = pm.getApplicationInfo(context.getPackageName(), 0);
192 | sApplicationName = pm.getApplicationLabel(ai).toString();
193 | } catch (PackageManager.NameNotFoundException e) {
194 | e.printStackTrace();
195 | sApplicationName = context.getPackageName();
196 | }
197 | return sApplicationName;
198 | }
199 |
200 | /**
201 | * 处理白名单.
202 | * @return 弹过框的 IntentWrapper.
203 | */
204 | @NonNull
205 | public static List whiteListMatters(final Activity a, String reason) {
206 | List showed = new ArrayList<>();
207 | if (reason == null) reason = "核心服务的持续运行";
208 | List intentWrapperList = getIntentWrapperList(a);
209 | for (final IntentWrapper iw : intentWrapperList) {
210 | //如果本机上没有能处理这个Intent的Activity,说明不是对应的机型,直接忽略进入下一次循环。
211 | if (!iw.doesActivityExists(a)) continue;
212 | switch (iw.type) {
213 | case DOZE:
214 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
215 | PowerManager pm = (PowerManager) a.getSystemService(Context.POWER_SERVICE);
216 | if (pm.isIgnoringBatteryOptimizations(a.getPackageName())) break;
217 | new AlertDialog.Builder(a)
218 | .setCancelable(false)
219 | .setTitle("需要忽略 " + getApplicationName(a) + " 的电池优化")
220 | .setMessage(reason + "需要 " + getApplicationName(a) + " 加入到电池优化的忽略名单。\n\n" +
221 | "请点击『确定』,在弹出的『忽略电池优化』对话框中,选择『是』。")
222 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
223 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
224 | })
225 | .show();
226 | showed.add(iw);
227 | }
228 | break;
229 | case HUAWEI:
230 | new AlertDialog.Builder(a)
231 | .setCancelable(false)
232 | .setTitle("需要允许 " + getApplicationName(a) + " 自动启动")
233 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 的自动启动。\n\n" +
234 | "请点击『确定』,在弹出的『自启管理』中,将 " + getApplicationName(a) + " 对应的开关打开。")
235 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
236 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
237 | })
238 | .show();
239 | showed.add(iw);
240 | break;
241 | case ZTE_GOD:
242 | case HUAWEI_GOD:
243 | new AlertDialog.Builder(a)
244 | .setCancelable(false)
245 | .setTitle(getApplicationName(a) + " 需要加入锁屏清理白名单")
246 | .setMessage(reason + "需要 " + getApplicationName(a) + " 加入到锁屏清理白名单。\n\n" +
247 | "请点击『确定』,在弹出的『锁屏清理』列表中,将 " + getApplicationName(a) + " 对应的开关打开。")
248 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
249 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
250 | })
251 | .show();
252 | showed.add(iw);
253 | break;
254 | case XIAOMI_GOD:
255 | new AlertDialog.Builder(a)
256 | .setCancelable(false)
257 | .setTitle("需要关闭 " + getApplicationName(a) + " 的神隐模式")
258 | .setMessage(reason + "需要关闭 " + getApplicationName(a) + " 的神隐模式。\n\n" +
259 | "请点击『确定』,在弹出的 " + getApplicationName(a) + " 神隐模式设置中,选择『无限制』,然后选择『允许定位』。")
260 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
261 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
262 | })
263 | .show();
264 | showed.add(iw);
265 | break;
266 | case SAMSUNG_L:
267 | new AlertDialog.Builder(a)
268 | .setCancelable(false)
269 | .setTitle("需要允许 " + getApplicationName(a) + " 的自启动")
270 | .setMessage(reason + "需要 " + getApplicationName(a) + " 在屏幕关闭时继续运行。\n\n" +
271 | "请点击『确定』,在弹出的『智能管理器』中,点击『内存』,选择『自启动应用程序』选项卡,将 " + getApplicationName(a) + " 对应的开关打开。")
272 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
273 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
274 | })
275 | .show();
276 | showed.add(iw);
277 | break;
278 | case SAMSUNG_M:
279 | new AlertDialog.Builder(a)
280 | .setCancelable(false)
281 | .setTitle("需要允许 " + getApplicationName(a) + " 的自启动")
282 | .setMessage(reason + "需要 " + getApplicationName(a) + " 在屏幕关闭时继续运行。\n\n" +
283 | "请点击『确定』,在弹出的『电池』页面中,点击『未监视的应用程序』->『添加应用程序』,勾选 " + getApplicationName(a) + ",然后点击『完成』。")
284 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
285 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
286 | })
287 | .show();
288 | showed.add(iw);
289 | break;
290 | case MEIZU:
291 | new AlertDialog.Builder(a)
292 | .setCancelable(false)
293 | .setTitle("需要允许 " + getApplicationName(a) + " 保持后台运行")
294 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 保持后台运行。\n\n" +
295 | "请点击『确定』,在弹出的应用信息界面中,将『后台管理』选项更改为『保持后台运行』。")
296 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
297 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
298 | })
299 | .show();
300 | showed.add(iw);
301 | break;
302 | case MEIZU_GOD:
303 | new AlertDialog.Builder(a)
304 | .setCancelable(false)
305 | .setTitle(getApplicationName(a) + " 需要在待机时保持运行")
306 | .setMessage(reason + "需要 " + getApplicationName(a) + " 在待机时保持运行。\n\n" +
307 | "请点击『确定』,在弹出的『待机耗电管理』中,将 " + getApplicationName(a) + " 对应的开关打开。")
308 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
309 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
310 | })
311 | .show();
312 | showed.add(iw);
313 | break;
314 | case ZTE:
315 | case LETV:
316 | case XIAOMI:
317 | case OPPO:
318 | case OPPO_OLD:
319 | new AlertDialog.Builder(a)
320 | .setCancelable(false)
321 | .setTitle("需要允许 " + getApplicationName(a) + " 的自启动")
322 | .setMessage(reason + "需要 " + getApplicationName(a) + " 加入到自启动白名单。\n\n" +
323 | "请点击『确定』,在弹出的『自启动管理』中,将 " + getApplicationName(a) + " 对应的开关打开。")
324 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
325 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
326 | })
327 | .show();
328 | showed.add(iw);
329 | break;
330 | case COOLPAD:
331 | new AlertDialog.Builder(a)
332 | .setCancelable(false)
333 | .setTitle("需要允许 " + getApplicationName(a) + " 的自启动")
334 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 的自启动。\n\n" +
335 | "请点击『确定』,在弹出的『酷管家』中,找到『软件管理』->『自启动管理』,取消勾选 " + getApplicationName(a) + ",将 " + getApplicationName(a) + " 的状态改为『已允许』。")
336 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
337 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
338 | })
339 | .show();
340 | showed.add(iw);
341 | break;
342 | case VIVO_GOD:
343 | new AlertDialog.Builder(a)
344 | .setCancelable(false)
345 | .setTitle("需要允许 " + getApplicationName(a) + " 的后台运行")
346 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 在后台高耗电时运行。\n\n" +
347 | "请点击『确定』,在弹出的『后台高耗电』中,将 " + getApplicationName(a) + " 对应的开关打开。")
348 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
349 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
350 | })
351 | .show();
352 | showed.add(iw);
353 | break;
354 | case GIONEE:
355 | new AlertDialog.Builder(a)
356 | .setCancelable(false)
357 | .setTitle(getApplicationName(a) + " 需要加入应用自启和绿色后台白名单")
358 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 的自启动和后台运行。\n\n" +
359 | "请点击『确定』,在弹出的『系统管家』中,分别找到『应用管理』->『应用自启』和『绿色后台』->『清理白名单』,将 " + getApplicationName(a) + " 添加到白名单。")
360 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
361 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
362 | })
363 | .show();
364 | showed.add(iw);
365 | break;
366 | case LETV_GOD:
367 | new AlertDialog.Builder(a)
368 | .setCancelable(false)
369 | .setTitle("需要禁止 " + getApplicationName(a) + " 被自动清理")
370 | .setMessage(reason + "需要禁止 " + getApplicationName(a) + " 被自动清理。\n\n" +
371 | "请点击『确定』,在弹出的『应用保护』中,将 " + getApplicationName(a) + " 对应的开关关闭。")
372 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
373 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
374 | })
375 | .show();
376 | showed.add(iw);
377 | break;
378 | case LENOVO:
379 | new AlertDialog.Builder(a)
380 | .setCancelable(false)
381 | .setTitle("需要允许 " + getApplicationName(a) + " 的后台运行")
382 | .setMessage(reason + "需要允许 " + getApplicationName(a) + " 的后台自启、后台 GPS 和后台运行。\n\n" +
383 | "请点击『确定』,在弹出的『后台管理』中,分别找到『后台自启』、『后台 GPS』和『后台运行』,将 " + getApplicationName(a) + " 对应的开关打开。")
384 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
385 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
386 | })
387 | .show();
388 | showed.add(iw);
389 | break;
390 | case LENOVO_GOD:
391 | new AlertDialog.Builder(a)
392 | .setCancelable(false)
393 | .setTitle("需要关闭 " + getApplicationName(a) + " 的后台耗电优化")
394 | .setMessage(reason + "需要关闭 " + getApplicationName(a) + " 的后台耗电优化。\n\n" +
395 | "请点击『确定』,在弹出的『后台耗电优化』中,将 " + getApplicationName(a) + " 对应的开关关闭。")
396 | .setPositiveButton("确定", new DialogInterface.OnClickListener() {
397 | public void onClick(DialogInterface d, int w) {iw.startActivitySafely(a);}
398 | })
399 | .show();
400 | showed.add(iw);
401 | break;
402 | }
403 | }
404 | return showed;
405 | }
406 |
407 | /**
408 | * 防止华为机型未加入白名单时按返回键回到桌面再锁屏后几秒钟进程被杀
409 | */
410 | public static void onBackPressed(Activity a) {
411 | Intent launcherIntent = new Intent(Intent.ACTION_MAIN);
412 | launcherIntent.addCategory(Intent.CATEGORY_HOME);
413 | a.startActivity(launcherIntent);
414 | }
415 |
416 | protected Intent intent;
417 | protected int type;
418 |
419 | protected IntentWrapper(Intent intent, int type) {
420 | this.intent = intent;
421 | this.type = type;
422 | }
423 |
424 | /**
425 | * 判断本机上是否有能处理当前Intent的Activity
426 | */
427 | protected boolean doesActivityExists(Context context) {
428 | PackageManager pm = context.getPackageManager();
429 | List list = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
430 | return list != null && list.size() > 0;
431 | }
432 |
433 | /**
434 | * 安全地启动一个Activity
435 | */
436 | protected void startActivitySafely(Activity activityContext) {
437 | try { activityContext.startActivity(intent); } catch (Exception e) { e.printStackTrace(); }
438 | }
439 | }
440 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/singlepixel/ScreenManager.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.singlepixel;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import java.lang.ref.WeakReference;
8 |
9 | /**
10 | * Created by shihoo ON 2018/12/12.
11 | * Email shihu.wang@bodyplus.cc 451082005@qq.com
12 | *
13 | * 对1像素Activity进行防止内存泄露的处理,新建一个ScreenManager类
14 | */
15 | public class ScreenManager {
16 |
17 | private static final String TAG = ScreenManager.class.getSimpleName();
18 | private static ScreenManager sInstance;
19 | private Context mContext;
20 | private WeakReference mActivity;
21 |
22 | private ScreenManager(Context mContext) {
23 | this.mContext = mContext;
24 | }
25 |
26 | public static ScreenManager getInstance(Context context) {
27 | if (sInstance == null) {
28 | sInstance = new ScreenManager(context);
29 | }
30 | return sInstance;
31 | }
32 |
33 | /** 获得SinglePixelActivity的引用
34 | * @param activity
35 | */
36 | public void setSingleActivity(Activity activity) {
37 | mActivity = new WeakReference<>(activity);
38 | }
39 |
40 | /**
41 | * 启动SinglePixelActivity
42 | */
43 | public void startActivity() {
44 | Intent intent = new Intent(mContext, SinglePixelActivity.class);
45 | intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
46 | mContext.startActivity(intent);
47 | }
48 |
49 | /**
50 | * 结束SinglePixelActivity
51 | */
52 | public void finishActivity() {
53 | if (mActivity != null) {
54 | Activity activity = mActivity.get();
55 | if (activity != null) {
56 | activity.finish();
57 | }
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/singlepixel/ScreenReceiverUtil.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.singlepixel;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.content.IntentFilter;
7 | import android.util.Log;
8 |
9 | /**
10 | * Created by shihoo ON 2018/12/12.
11 | * Email shihu.wang@bodyplus.cc 451082005@qq.com
12 | *
13 | * 对广播进行监听,封装为一个ScreenReceiverUtil类,进行锁屏解锁的广播动态注册监听
14 | */
15 | public class ScreenReceiverUtil {
16 | private Context mContext;
17 | private SreenBroadcastReceiver mScreenReceiver;
18 | private ScreenManager mScreenManager;
19 |
20 | public ScreenReceiverUtil(Context mContext) {
21 | this.mContext = mContext;
22 | }
23 |
24 | public void startScreenReceiverListener() {
25 | // 动态启动广播接收器
26 | this.mScreenReceiver = new SreenBroadcastReceiver();
27 | IntentFilter filter = new IntentFilter();
28 | filter.addAction(Intent.ACTION_SCREEN_ON);
29 | filter.addAction(Intent.ACTION_SCREEN_OFF);
30 | filter.addAction(Intent.ACTION_USER_PRESENT);
31 | mContext.registerReceiver(mScreenReceiver, filter);
32 | mScreenManager = ScreenManager.getInstance(mContext);
33 | }
34 |
35 | public void stopScreenReceiverListener() {
36 | if (null != mScreenReceiver) {
37 | mContext.unregisterReceiver(mScreenReceiver);
38 | mScreenReceiver = null;
39 | }
40 | mScreenManager = null;
41 | }
42 |
43 | public class SreenBroadcastReceiver extends BroadcastReceiver {
44 | @Override
45 | public void onReceive(Context context, Intent intent) {
46 | String action = intent.getAction();
47 | if (Intent.ACTION_SCREEN_ON.equals(action)) { // 开屏
48 | } else if (Intent.ACTION_SCREEN_OFF.equals(action)) { // 锁屏
49 | if (mScreenManager != null) {
50 | mScreenManager.startActivity();
51 | }
52 | Log.d("wsh-daemon", "打开了1像素Activity");
53 | } else if (Intent.ACTION_USER_PRESENT.equals(action)) { // 解锁
54 | if (mScreenManager != null) {
55 | mScreenManager.finishActivity(); // 解锁
56 | }
57 | Log.d("wsh-daemon", "关闭了1像素Activity");
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/singlepixel/SinglePixelActivity.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.singlepixel;
2 |
3 | import android.app.Activity;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Build;
7 | import android.os.Bundle;
8 | import android.os.PowerManager;
9 | import android.support.annotation.Nullable;
10 | import android.util.Log;
11 | import android.view.Gravity;
12 | import android.view.MotionEvent;
13 | import android.view.Window;
14 | import android.view.WindowManager;
15 |
16 | import com.shihoo.daemon.watch.WatchDogService;
17 |
18 | /**
19 | * Created by shihoo ON 2018/12/12.
20 | * Email shihu.wang@bodyplus.cc 451082005@qq.com
21 | *
22 | * 该Activity的View只要设置为1像素然后设置在Window对象上即可。在Activity的onDestroy周期中进行保活服务的存活判断从而唤醒服务。
23 | * 运行在:watch进程, 为了提高watch进程的优先级 oom_adj值越小,优先级越高。
24 | * https://blog.csdn.net/hello_json/article/details/72730740
25 | */
26 | public class SinglePixelActivity extends Activity {
27 |
28 | private static final String TAG = SinglePixelActivity.class.getSimpleName();
29 |
30 | @Override
31 | protected void onCreate(@Nullable Bundle savedInstanceState) {
32 | super.onCreate(savedInstanceState);
33 | Window mWindow = getWindow();
34 | mWindow.setGravity(Gravity.LEFT | Gravity.TOP);
35 | WindowManager.LayoutParams attrParams = mWindow.getAttributes();
36 | attrParams.x = 0;
37 | attrParams.y = 0;
38 | attrParams.height = 1;
39 | attrParams.width = 1;
40 | mWindow.setAttributes(attrParams);
41 | ScreenManager.getInstance(this).setSingleActivity(this);
42 | }
43 |
44 | @Override
45 | protected void onDestroy() {
46 | // if (!SystemUtils.isAppAlive(this, Constant.PACKAGE_NAME)) {
47 | Log.d("wsh-daemon", " 1 像素Activity --- onDestroy");
48 | Intent intentAlive = new Intent(this, WatchDogService.class);
49 | startService(intentAlive);
50 | // }
51 | super.onDestroy();
52 | }
53 |
54 | @Override
55 | protected void onResume() {
56 | super.onResume();
57 | if (isScreenOn()) {
58 | finishSelf();
59 | }
60 | }
61 |
62 | @Override
63 | public boolean dispatchTouchEvent(MotionEvent motionEvent) {
64 | finishSelf();
65 | return super.dispatchTouchEvent(motionEvent);
66 | }
67 |
68 | @Override
69 | public boolean onTouchEvent(MotionEvent motionEvent) {
70 | finishSelf();
71 | return super.onTouchEvent(motionEvent);
72 | }
73 |
74 | public void finishSelf() {
75 | if (!isFinishing()) {
76 | finish();
77 | }
78 | }
79 |
80 |
81 | private boolean isScreenOn() {
82 | PowerManager powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
83 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
84 | return powerManager.isInteractive();
85 | } else {
86 | return powerManager.isScreenOn();
87 | }
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/sync/Authenticator.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.sync;
2 |
3 | import android.accounts.AbstractAccountAuthenticator;
4 | import android.accounts.Account;
5 | import android.accounts.AccountAuthenticatorResponse;
6 | import android.accounts.AccountManager;
7 | import android.accounts.NetworkErrorException;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.os.Bundle;
11 |
12 | /**
13 | * Created by shihu.wang on 2017/4/10.
14 | * Email shihu.wang@bodyplus.cc
15 | */
16 |
17 | public class Authenticator extends AbstractAccountAuthenticator {
18 |
19 | final Context context;
20 |
21 | public Authenticator(Context context) {
22 | super(context);
23 | this.context = context;
24 | }
25 |
26 | @Override
27 | public Bundle addAccount(AccountAuthenticatorResponse response,
28 | String accountType, String authTokenType,
29 | String[] requiredFeatures, Bundle options)
30 | throws NetworkErrorException {
31 | Intent intent = new Intent();
32 | intent.putExtra(AccountManager.KEY_ACCOUNT_AUTHENTICATOR_RESPONSE,
33 | response);
34 | Bundle bundle = new Bundle();
35 | bundle.putParcelable(AccountManager.KEY_INTENT, intent);
36 | return bundle;
37 | }
38 |
39 | @Override
40 | public Bundle confirmCredentials(AccountAuthenticatorResponse response,
41 | Account account, Bundle options) throws NetworkErrorException {
42 | return null;
43 | }
44 |
45 | @Override
46 | public Bundle editProperties(AccountAuthenticatorResponse response,
47 | String accountType) {
48 | return null;
49 | }
50 |
51 | @Override
52 | public Bundle getAuthToken(AccountAuthenticatorResponse response,
53 | Account account, String authTokenType, Bundle options)
54 | throws NetworkErrorException {
55 | return null;
56 | }
57 |
58 | @Override
59 | public String getAuthTokenLabel(String authTokenType) {
60 | return null;
61 | }
62 |
63 | @Override
64 | public Bundle hasFeatures(AccountAuthenticatorResponse response,
65 | Account account, String[] features)
66 | throws NetworkErrorException {
67 | return null;
68 | }
69 |
70 | @Override
71 | public Bundle updateCredentials(AccountAuthenticatorResponse response,
72 | Account account, String authTokenType, Bundle options)
73 | throws NetworkErrorException {
74 | return null;
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/sync/AuthenticatorService.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.sync;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.os.IBinder;
6 |
7 | /**
8 | * Created by shihu.wang on 2017/4/10.
9 | * Email shihu.wang@bodyplus.cc
10 | *
11 | * 授权此服务提供给SyncAdapter framework,用于调用Authenticator的方法
12 | */
13 |
14 | public class AuthenticatorService extends Service {
15 |
16 | private Authenticator mAuthenticator;
17 | public AuthenticatorService() {
18 | }
19 |
20 | @Override
21 | public void onCreate() {
22 | super.onCreate();
23 | mAuthenticator = new Authenticator(this);
24 | }
25 |
26 | @Override
27 | public IBinder onBind(Intent intent) {
28 | return mAuthenticator.getIBinder();
29 | }
30 | }
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/sync/StubProvider.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.sync;
2 |
3 | import android.content.ContentProvider;
4 | import android.content.ContentValues;
5 | import android.database.Cursor;
6 | import android.net.Uri;
7 | import android.support.annotation.Nullable;
8 |
9 | /**
10 | * Created by shihu.wang on 2017/4/10.
11 | * Email shihu.wang@bodyplus.cc
12 | */
13 |
14 | public class StubProvider extends ContentProvider {
15 | @Override
16 | public boolean onCreate() {
17 | return false;
18 | }
19 |
20 | @Nullable
21 | @Override
22 | public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
23 | return null;
24 | }
25 |
26 | @Nullable
27 | @Override
28 | public String getType(Uri uri) {
29 | return null;
30 | }
31 |
32 | @Nullable
33 | @Override
34 | public Uri insert(Uri uri, ContentValues values) {
35 | return null;
36 | }
37 |
38 | @Override
39 | public int delete(Uri uri, String selection, String[] selectionArgs) {
40 | return 0;
41 | }
42 |
43 | @Override
44 | public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
45 | return 0;
46 | }
47 | }
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/sync/SyncAdapter.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.sync;
2 |
3 | /**
4 | * Created by shihu.wang on 2017/4/10.
5 | * Email shihu.wang@bodyplus.cc
6 | */
7 |
8 | import android.accounts.Account;
9 | import android.content.AbstractThreadedSyncAdapter;
10 | import android.content.ContentProviderClient;
11 | import android.content.Context;
12 | import android.content.SyncResult;
13 | import android.os.Bundle;
14 |
15 | import com.shihoo.daemon.DaemonEnv;
16 | import com.shihoo.daemon.watch.WatchDogService;
17 | import com.shihoo.daemon.watch.WatchProcessPrefHelper;
18 |
19 |
20 | public class SyncAdapter extends AbstractThreadedSyncAdapter {
21 |
22 |
23 | private Context mContext;
24 |
25 | public SyncAdapter(Context context, boolean autoInitialize) {
26 | super(context, autoInitialize);
27 | this.mContext = context;
28 | }
29 |
30 | @Override
31 | public void onPerformSync(Account account, Bundle extras, String authority, ContentProviderClient provider, SyncResult syncResult) {
32 | DaemonEnv.startServiceSafely(mContext,WatchDogService.class);
33 | }
34 | }
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/sync/SyncService.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.sync;
2 |
3 | import android.app.Service;
4 | import android.content.Intent;
5 | import android.os.IBinder;
6 |
7 | /**
8 | * Created by shihu.wang on 2017/4/10.
9 | * Email shihu.wang@bodyplus.cc
10 | *
11 | * 此服务需能交给操作系统使用
12 | */
13 |
14 | public class SyncService extends Service {
15 |
16 |
17 | // Storage for an instance of the sync adapter
18 | private static SyncAdapter sSyncAdapter = null;
19 | // Object to use as a thread-safe lock
20 | private static final Object sSyncAdapterLock = new Object();
21 |
22 | public SyncService() {
23 | }
24 |
25 | @Override
26 | public void onCreate() {
27 | super.onCreate();
28 | synchronized (sSyncAdapterLock) {
29 | sSyncAdapter = new SyncAdapter(getApplicationContext(), true);
30 | }
31 | }
32 |
33 | @Override
34 | public IBinder onBind(Intent intent) {
35 | return sSyncAdapter.getSyncAdapterBinder();
36 | }
37 | }
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/watch/JobSchedulerService.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.watch;
2 |
3 | import android.annotation.TargetApi;
4 | import android.app.job.JobParameters;
5 | import android.app.job.JobService;
6 | import android.os.Build;
7 | import android.util.Log;
8 |
9 | import com.shihoo.daemon.DaemonEnv;
10 |
11 | /**
12 | * Android 5.0+ 使用的 JobScheduler.
13 | * 运行在 :watch 子进程中.
14 | *
15 | */
16 | @TargetApi(Build.VERSION_CODES.LOLLIPOP)
17 | public class JobSchedulerService extends JobService {
18 |
19 | @Override
20 | public boolean onStartJob(JobParameters params) {
21 | Log.d("wsh-daemon", "JobSchedulerService onStartJob 启动。。。。");
22 | DaemonEnv.startServiceSafely(JobSchedulerService.this,
23 | WatchDogService.class);
24 | return false;
25 | }
26 |
27 | @Override
28 | public boolean onStopJob(JobParameters params) {
29 | Log.d("wsh-daemon", "JobSchedulerService onStopJob 停止。。。。");
30 | return false;
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/watch/PlayMusicService.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.watch;
2 |
3 | import android.app.Service;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.media.MediaPlayer;
9 | import android.os.IBinder;
10 | import android.util.Log;
11 |
12 | import com.shihoo.daemon.DaemonEnv;
13 | import com.shihoo.daemon.ForegroundNotificationUtils;
14 | import com.shihoo.daemon.R;
15 |
16 | /**
17 | * Created by shihoo ON 2018/12/13.
18 | * Email shihu.wang@bodyplus.cc 451082005@qq.com
19 | *
20 | * 后台播放无声音乐
21 | */
22 | public class PlayMusicService extends Service {
23 | private boolean mNeedStop = false; //控制是否播放音频
24 | private MediaPlayer mMediaPlayer;
25 | private StopBroadcastReceiver stopBroadcastReceiver;
26 |
27 | @Override
28 | public IBinder onBind(Intent intent) {
29 | return null;
30 | }
31 |
32 | @Override
33 | public void onCreate() {
34 | super.onCreate();
35 | ForegroundNotificationUtils.startForegroundNotification(this);
36 | startRegisterReceiver();
37 | mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.no_notice);
38 | mMediaPlayer.setLooping(true);
39 | }
40 |
41 | @Override
42 | public int onStartCommand(Intent intent, int flags, int startId) {
43 | startPlayMusic();
44 | return START_STICKY;
45 | }
46 |
47 | private void startPlayMusic(){
48 | if (mMediaPlayer!=null && !mMediaPlayer.isPlaying() && !mNeedStop) {
49 | new Thread(new Runnable() {
50 | @Override
51 | public void run() {
52 | Log.d("wsh-daemon", "开始后台播放音乐");
53 | mMediaPlayer.start();
54 | }
55 | }).start();
56 | }
57 | }
58 |
59 | private void stopPlayMusic() {
60 | if (mMediaPlayer != null) {
61 | Log.d("wsh-daemon", "关闭后台播放音乐");
62 | mMediaPlayer.stop();
63 | }
64 | }
65 |
66 | @Override
67 | public void onDestroy() {
68 | super.onDestroy();
69 | stopPlayMusic();
70 | Log.d("wsh-daemon", "----> stopPlayMusic ,停止服务");
71 | // 重启自己
72 | if (!mNeedStop) {
73 | Log.d("wsh-daemon", "----> PlayMusic ,重启服务");
74 | Intent intent = new Intent(getApplicationContext(), PlayMusicService.class);
75 | startService(intent);
76 | }
77 | }
78 |
79 | private void startRegisterReceiver(){
80 | if (stopBroadcastReceiver == null){
81 | stopBroadcastReceiver = new StopBroadcastReceiver();
82 | IntentFilter intentFilter = new IntentFilter();
83 | intentFilter.addAction(DaemonEnv.ACTION_CANCEL_JOB_ALARM_SUB);
84 | registerReceiver(stopBroadcastReceiver,intentFilter);
85 | }
86 | }
87 |
88 | private void startUnRegisterReceiver(){
89 | if (stopBroadcastReceiver != null){
90 | unregisterReceiver(stopBroadcastReceiver);
91 | stopBroadcastReceiver = null;
92 | }
93 | }
94 |
95 | /**
96 | * 停止自己
97 | */
98 | private void stopService(){
99 | mNeedStop = true;
100 | stopPlayMusic();
101 | startUnRegisterReceiver();
102 | stopSelf();
103 | }
104 |
105 | class StopBroadcastReceiver extends BroadcastReceiver {
106 |
107 | @Override
108 | public void onReceive(Context context, Intent intent) {
109 | stopService();
110 | }
111 | }
112 | }
113 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/watch/WakeUpReceiver.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.watch;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 |
8 | import com.shihoo.daemon.DaemonEnv;
9 | import com.shihoo.daemon.watch.WatchDogService;
10 | import com.shihoo.daemon.watch.WatchProcessPrefHelper;
11 |
12 | public class WakeUpReceiver extends BroadcastReceiver {
13 |
14 | /**
15 | * 监听 8 种系统广播 :
16 | * CONNECTIVITY\_CHANGE, USER\_PRESENT, ACTION\_POWER\_CONNECTED, ACTION\_POWER\_DISCONNECTED,
17 | * BOOT\_COMPLETED, MEDIA\_MOUNTED, PACKAGE\_ADDED, PACKAGE\_REMOVED.
18 | * 在网络连接改变, 用户屏幕解锁, 电源连接 / 断开, 系统启动完成, 挂载 SD 卡, 安装 / 卸载软件包时拉起 Service.
19 | * Service 内部做了判断,若 Service 已在运行,不会重复启动.
20 | * 运行在:watch子进程中.
21 | */
22 | @SuppressLint("UnsafeProtectedBroadcastReceiver")
23 | @Override
24 | public void onReceive(Context context, Intent intent) {
25 | DaemonEnv.startServiceSafely(context, WatchDogService.class);
26 | }
27 |
28 | public static class WakeUpAutoStartReceiver extends BroadcastReceiver {
29 |
30 | @SuppressLint("UnsafeProtectedBroadcastReceiver")
31 | @Override
32 | public void onReceive(Context context, Intent intent) {
33 | DaemonEnv.startServiceSafely(context,WatchDogService.class);
34 | }
35 | }
36 |
37 | public static class StartWatchReceiver extends BroadcastReceiver {
38 |
39 | @SuppressLint("UnsafeProtectedBroadcastReceiver")
40 | @Override
41 | public void onReceive(Context context, Intent intent) {
42 | WatchProcessPrefHelper.setIsStartSDaemon(context,true);
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/watch/WatchDogService.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.watch;
2 |
3 | import android.app.AlarmManager;
4 | import android.app.PendingIntent;
5 | import android.app.Service;
6 | import android.app.job.JobInfo;
7 | import android.app.job.JobScheduler;
8 | import android.content.BroadcastReceiver;
9 | import android.content.ComponentName;
10 | import android.content.Context;
11 | import android.content.Intent;
12 | import android.content.IntentFilter;
13 | import android.content.pm.PackageManager;
14 | import android.os.Build;
15 | import android.os.Handler;
16 | import android.os.IBinder;
17 | import android.os.Messenger;
18 | import android.util.Log;
19 |
20 | import java.util.concurrent.TimeUnit;
21 |
22 | import io.reactivex.Observable;
23 | import io.reactivex.disposables.Disposable;
24 | import io.reactivex.functions.Consumer;
25 |
26 | import com.shihoo.daemon.AbsServiceConnection;
27 | import com.shihoo.daemon.DaemonEnv;
28 | import com.shihoo.daemon.ForegroundNotificationUtils;
29 |
30 |
31 | public class WatchDogService extends Service {
32 | protected static final int HASH_CODE = 11222;
33 | protected static Disposable mDisposable;
34 | protected static PendingIntent mPendingIntent;
35 | private StopBroadcastReceiver stopBroadcastReceiver;
36 | private boolean isCanStartWatchDog;
37 |
38 | /**
39 | * 服务绑定相关的操作
40 | */
41 | private AbsServiceConnection mConnection = new AbsServiceConnection() {
42 |
43 | @Override
44 | public void onDisconnected(ComponentName name) {
45 | startBindWorkServices();
46 | }
47 | };
48 |
49 | private void startBindWorkServices(){
50 | if (WatchProcessPrefHelper.mWorkServiceClass!=null && isCanStartWatchDog) {
51 | DaemonEnv.startServiceMayBind(WatchDogService.this, WatchProcessPrefHelper.mWorkServiceClass, mConnection);
52 | DaemonEnv.startServiceSafely(WatchDogService.this,
53 | PlayMusicService.class);
54 | }
55 | }
56 |
57 |
58 | @Override
59 | public void onCreate() {
60 | super.onCreate();
61 | isCanStartWatchDog = WatchProcessPrefHelper.getIsStartDaemon(this);
62 | if (!isCanStartWatchDog){
63 | stopSelf();
64 | }
65 | startRegisterReceiver();
66 | ForegroundNotificationUtils.startForegroundNotification(this);
67 | }
68 |
69 | @Override
70 | public final int onStartCommand(Intent intent, int flags, int startId) {
71 | onStart();
72 | return START_STICKY;
73 | }
74 |
75 | /**
76 | * 守护服务,运行在:watch子进程中
77 | */
78 | protected final void onStart() {
79 | if (mDisposable == null || mDisposable.isDisposed()) {
80 | //定时检查 AbsWorkService 是否在运行,如果不在运行就把它拉起来 Android 5.0+ 使用 JobScheduler,效果比 AlarmManager 好
81 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
82 | JobInfo.Builder builder = new JobInfo.Builder(HASH_CODE,
83 | new ComponentName(WatchDogService.this, JobSchedulerService.class));
84 | builder.setPeriodic(DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL));
85 | //Android 7.0+ 增加了一项针对 JobScheduler 的新限制,最小间隔只能是下面设定的数字
86 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
87 | builder.setPeriodic(JobInfo.getMinPeriodMillis(), JobInfo.getMinFlexMillis());
88 | }
89 | builder.setPersisted(true);
90 | JobScheduler scheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
91 | scheduler.schedule(builder.build());
92 | } else {
93 | //Android 4.4- 使用 AlarmManager
94 | AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
95 | Intent i = new Intent(WatchDogService.this, WatchProcessPrefHelper.mWorkServiceClass);
96 | mPendingIntent = PendingIntent.getService(WatchDogService.this, HASH_CODE, i, PendingIntent.FLAG_UPDATE_CURRENT);
97 | am.setRepeating(AlarmManager.RTC_WAKEUP,
98 | System.currentTimeMillis() + DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL),
99 | DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL), mPendingIntent);
100 | }
101 | //使用定时 Observable,避免 Android 定制系统 JobScheduler / AlarmManager 唤醒间隔不稳定的情况
102 | mDisposable = Observable
103 | .interval(DaemonEnv.getWakeUpInterval(DaemonEnv.MINIMAL_WAKE_UP_INTERVAL), TimeUnit.MILLISECONDS)
104 | .subscribe(new Consumer() {
105 | @Override
106 | public void accept(Long aLong) throws Exception {
107 | startBindWorkServices();
108 | }
109 | }, new Consumer() {
110 | @Override
111 | public void accept(Throwable throwable) throws Exception {
112 | throwable.printStackTrace();
113 | }
114 | });
115 | startBindWorkServices();
116 | //守护 Service 组件的启用状态, 使其不被 MAT 等工具禁用
117 | getPackageManager().setComponentEnabledSetting(new ComponentName(getPackageName(), WatchProcessPrefHelper.mWorkServiceClass.getName()),
118 | PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
119 | }
120 | }
121 |
122 |
123 | @Override
124 | public final IBinder onBind(Intent intent) {
125 | return new Messenger(new Handler()).getBinder();
126 | }
127 |
128 | private void onEnd() {
129 | Log.d("wsh-daemon", "onEnd ---- 搞事 + IsShouldStopSelf :" + isCanStartWatchDog);
130 | if (isCanStartWatchDog){
131 | DaemonEnv.startServiceSafely(WatchDogService.this,WatchProcessPrefHelper.mWorkServiceClass);
132 | DaemonEnv.startServiceSafely(WatchDogService.this,WatchDogService.class);
133 | }
134 | }
135 |
136 | /**
137 | * 最近任务列表中划掉卡片时回调
138 | */
139 | @Override
140 | public void onTaskRemoved(Intent rootIntent) {
141 | onEnd();
142 | }
143 |
144 | /**
145 | * 设置-正在运行中停止服务时回调
146 | */
147 | @Override
148 | public void onDestroy() {
149 | onEnd();
150 | stopRegisterReceiver();
151 | }
152 |
153 |
154 | /**
155 | * 停止运行本服务,本进程
156 | */
157 | private void stopService(){
158 | isCanStartWatchDog = false;
159 | WatchProcessPrefHelper.setIsStartSDaemon(this,false);
160 | cancelJobAlarmSub();
161 | if (mConnection.mConnectedState) {
162 | unbindService(mConnection);
163 | }
164 | exit();
165 | }
166 |
167 | private void exit(){
168 | new Handler().postDelayed(new Runnable() {
169 | @Override
170 | public void run() {
171 | stopSelf();
172 | }
173 | },2000);
174 | }
175 |
176 |
177 | private void startRegisterReceiver(){
178 | if (stopBroadcastReceiver == null){
179 | stopBroadcastReceiver = new StopBroadcastReceiver();
180 | IntentFilter intentFilter = new IntentFilter();
181 | intentFilter.addAction(DaemonEnv.ACTION_CANCEL_JOB_ALARM_SUB);
182 | registerReceiver(stopBroadcastReceiver,intentFilter);
183 | }
184 | }
185 |
186 | private void stopRegisterReceiver(){
187 | if (stopBroadcastReceiver != null){
188 | unregisterReceiver(stopBroadcastReceiver);
189 | stopBroadcastReceiver = null;
190 | }
191 | }
192 |
193 | /**
194 | * 用于在不需要服务运行的时候取消 Job / Alarm / Subscription.
195 | *
196 | * 因 WatchDogService 运行在 :watch 子进程, 请勿在主进程中直接调用此方法.
197 | * 而是向 WakeUpReceiver 发送一个 Action 为 WakeUpReceiver.ACTION_CANCEL_JOB_ALARM_SUB 的广播.
198 | */
199 | public void cancelJobAlarmSub() {
200 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
201 | JobScheduler scheduler = (JobScheduler) WatchDogService.this.getSystemService(JOB_SCHEDULER_SERVICE);
202 | scheduler.cancel(HASH_CODE);
203 | } else {
204 | AlarmManager am = (AlarmManager) WatchDogService.this.getSystemService(ALARM_SERVICE);
205 | if (mPendingIntent != null) {
206 | am.cancel(mPendingIntent);
207 | }
208 | }
209 | if (mDisposable !=null && !mDisposable.isDisposed()){
210 | mDisposable.dispose();
211 | }
212 | }
213 |
214 | class StopBroadcastReceiver extends BroadcastReceiver{
215 |
216 | @Override
217 | public void onReceive(Context context, Intent intent) {
218 | stopService();
219 | }
220 | }
221 | }
222 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/watch/WatchProcessPrefHelper.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.watch;
2 |
3 | import android.content.Context;
4 |
5 | import com.shihoo.daemon.work.AbsWorkService;
6 |
7 |
8 | /**
9 | * Created by shihoo ON 2018/12/13.
10 | * Email shihu.wang@bodyplus.cc 451082005@qq.com
11 | *
12 | * 用于多进程通讯的 SharedPreferences
13 | *
14 | * 此处存在着风险 github --> PreferencesProvider 项目
15 | */
16 |
17 | public class WatchProcessPrefHelper {
18 |
19 | private static final String SHARED_UTILS = "watch_process";
20 |
21 | private static final String KEY_IS_START_DAEMON = "is_start_sport"; // 是否开始了一次保活(做为保活的判断依据)
22 |
23 | // 多进程时,尽量少用静态、单例 此处不得已
24 | public static Class extends AbsWorkService> mWorkServiceClass;
25 |
26 | public static void setIsStartSDaemon(Context context,boolean mapType){
27 | context.getSharedPreferences(SHARED_UTILS, Context.MODE_MULTI_PROCESS).edit().putBoolean(KEY_IS_START_DAEMON, mapType).apply();
28 | }
29 |
30 | public static boolean getIsStartDaemon(Context context){
31 | return context.getSharedPreferences(SHARED_UTILS, Context.MODE_MULTI_PROCESS).getBoolean(KEY_IS_START_DAEMON, false);
32 | }
33 |
34 |
35 | }
36 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/java/com/shihoo/daemon/work/AbsWorkService.java:
--------------------------------------------------------------------------------
1 | package com.shihoo.daemon.work;
2 |
3 | import android.app.Service;
4 | import android.content.BroadcastReceiver;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.IntentFilter;
9 | import android.content.pm.PackageManager;
10 | import android.os.IBinder;
11 | import android.support.annotation.NonNull;
12 | import android.util.Log;
13 |
14 | import com.shihoo.daemon.AbsServiceConnection;
15 | import com.shihoo.daemon.DaemonEnv;
16 | import com.shihoo.daemon.ForegroundNotificationUtils;
17 | import com.shihoo.daemon.singlepixel.ScreenReceiverUtil;
18 | import com.shihoo.daemon.watch.WatchDogService;
19 |
20 | /**
21 | * Created by shihoo ON 2018/12/12.
22 | * Email shihu.wang@bodyplus.cc 451082005@qq.com
23 | *
24 | * 主要Service 用户继承该类用来处理自己业务逻辑
25 | *
26 | * 该类已经实现如何启动结束及保活的功能,用户无需关心。
27 | */
28 | public abstract class AbsWorkService extends Service {
29 |
30 | private StopBroadcastReceiver stopBroadcastReceiver;
31 |
32 | private AbsServiceConnection mConnection = new AbsServiceConnection() {
33 |
34 | @Override
35 | public void onDisconnected(ComponentName name) {
36 | if (needStartWorkService()) {
37 | DaemonEnv.startServiceMayBind(AbsWorkService.this, WatchDogService.class, mConnection);
38 | }
39 | }
40 |
41 | };
42 |
43 | @Override
44 | public void onCreate() {
45 | super.onCreate();
46 | if (!needStartWorkService()) {
47 | stopSelf();
48 | }else {
49 | Log.d("wsh-daemon", "AbsWorkService onCreate 启动。。。。");
50 | startRegisterReceiver();
51 | createScreenListener();
52 | ForegroundNotificationUtils.startForegroundNotification(this);
53 | getPackageManager().setComponentEnabledSetting(new ComponentName(getPackageName(), WatchDogService.class.getName()),
54 | PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
55 | }
56 | }
57 |
58 | @Override
59 | public int onStartCommand(Intent intent, int flags, int startId) {
60 | return onStart();
61 | }
62 |
63 | /**
64 | * 1.防止重复启动,可以任意调用 DaemonEnv.startServiceMayBind(Class serviceClass);
65 | * 2.利用漏洞启动前台服务而不显示通知;
66 | * 3.在子线程中运行定时任务,处理了运行前检查和销毁时保存的问题;
67 | * 4.启动守护服务;
68 | * 5.守护 Service 组件的启用状态, 使其不被 MAT 等工具禁用.
69 | */
70 | protected int onStart() {
71 | //启动守护服务,运行在:watch子进程中
72 | //业务逻辑: 实际使用时,根据需求,将这里更改为自定义的条件,判定服务应当启动还是停止 (任务是否需要运行)
73 | // 此处不比重复关闭服务。否则mConnection.mConnectedState的状态没有来得及改变,
74 | // 再次unbindService(conn)服务会导致 Service not registered 异常抛出。 服务启动和关闭都需要耗时,段时间内不宜频繁开启和关闭。
75 | //若还没有取消订阅,说明任务仍在运行,为防止重复启动,直接 return
76 | DaemonEnv.startServiceMayBind(AbsWorkService.this, WatchDogService.class, mConnection);
77 | Boolean workRunning = isWorkRunning();
78 | if (!workRunning){
79 | //业务逻辑
80 | startWork();
81 | }
82 | return START_STICKY;
83 | }
84 |
85 |
86 | @NonNull
87 | @Override
88 | public IBinder onBind(Intent intent) {
89 | return onBindService(intent, null);
90 | }
91 |
92 | /**
93 | * 最近任务列表中划掉卡片时回调
94 | */
95 | @Override
96 | public void onTaskRemoved(Intent rootIntent) {
97 | onEnd();
98 | }
99 |
100 | /**
101 | * 设置-正在运行中停止服务时回调
102 | */
103 | @Override
104 | public void onDestroy() {
105 | ForegroundNotificationUtils.deleteForegroundNotification(this);
106 | stopRegisterReceiver();
107 | stopScreenListener();
108 | onEnd();
109 | }
110 |
111 | protected void onEnd() {
112 | onServiceKilled();
113 | // // 不同的进程,所有的静态和单例都会失效
114 | if (needStartWorkService()){
115 | DaemonEnv.startServiceSafely(AbsWorkService.this,WatchDogService.class);
116 | }
117 |
118 | }
119 |
120 | /**
121 | * 是否 任务完成, 不再需要服务运行?
122 | * @return 应当停止服务, true; 应当启动服务, false; 无法判断, 什么也不做, null.
123 | */
124 | public abstract Boolean needStartWorkService();
125 |
126 | public abstract void startWork();
127 |
128 | public abstract void stopWork();
129 | /**
130 | * 任务是否正在运行? 由实现者处理
131 | * @return 任务正在运行, true; 任务当前不在运行, false; 无法判断, 什么也不做, null.
132 | */
133 | public abstract Boolean isWorkRunning();
134 |
135 | @NonNull
136 | public abstract IBinder onBindService(Intent intent, Void alwaysNull);
137 | public abstract void onServiceKilled();
138 |
139 |
140 | /**
141 | * 任务完成,停止服务并取消定时唤醒
142 | *
143 | * 停止服务使用取消订阅的方式实现,而不是调用 Context.stopService(Intent name)。因为:
144 | * 1.stopService 会调用 Service.onDestroy(),而 AbsWorkService 做了保活处理,会把 Service 再拉起来;
145 | * 2.我们希望 AbsWorkService 起到一个类似于控制台的角色,即 AbsWorkService 始终运行 (无论任务是否需要运行),
146 | * 而是通过 onStart() 里自定义的条件,来决定服务是否应当启动或停止。
147 | */
148 | private void stopService() {
149 | // 给实现者处理业务逻辑
150 | DaemonEnv.safelyUnbindService(this,mConnection);
151 | stopWork();
152 | stopSelf();
153 | }
154 |
155 |
156 | private ScreenReceiverUtil mScreenUtils;
157 |
158 | private void createScreenListener(){
159 | // 注册锁屏广播监听器
160 | mScreenUtils = new ScreenReceiverUtil(this);
161 | mScreenUtils.startScreenReceiverListener();
162 | }
163 |
164 | private void stopScreenListener(){
165 | // 取消注册
166 | if (mScreenUtils != null){
167 | mScreenUtils.stopScreenReceiverListener();
168 | mScreenUtils = null;
169 | }
170 | }
171 |
172 | private void startRegisterReceiver(){
173 | if (stopBroadcastReceiver == null){
174 | stopBroadcastReceiver = new StopBroadcastReceiver();
175 | IntentFilter intentFilter = new IntentFilter();
176 | intentFilter.addAction(DaemonEnv.ACTION_CANCEL_JOB_ALARM_SUB);
177 | registerReceiver(stopBroadcastReceiver,intentFilter);
178 | }
179 | }
180 |
181 | private void stopRegisterReceiver(){
182 | if (stopBroadcastReceiver != null){
183 | unregisterReceiver(stopBroadcastReceiver);
184 | stopBroadcastReceiver = null;
185 | }
186 | }
187 |
188 | class StopBroadcastReceiver extends BroadcastReceiver {
189 |
190 | @Override
191 | public void onReceive(Context context, Intent intent) {
192 | // 停止业务
193 | stopService();
194 | }
195 | }
196 | }
197 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/res/drawable/icon1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShihooWang/DaemonLibrary/HEAD/daemonlibrary/src/main/res/drawable/icon1.png
--------------------------------------------------------------------------------
/daemonlibrary/src/main/res/raw/no_notice.mp3:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ShihooWang/DaemonLibrary/HEAD/daemonlibrary/src/main/res/raw/no_notice.mp3
--------------------------------------------------------------------------------
/daemonlibrary/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | deamon
3 |
4 |
--------------------------------------------------------------------------------
/daemonlibrary/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
16 |
17 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':daemonlibrary'
2 |
--------------------------------------------------------------------------------