)
99 |
100 | ### 进程
101 |
102 | 如果内存不足,需要为其他用户提供更紧急服务的进程又需要内存时,Android 可能会决定在某一时刻关闭某一进程。在被终止进程中运行的应用组件也会随之销毁。 当这些组件需要再次运行时,系统将为它们重启进程。
103 |
104 | 决定终止哪个进程时,Android 系统将权衡它们对用户的相对重要程度。例如,相对于托管可见 Activity 的进程而言,它更有可能关闭托管屏幕上不再可见的 Activity 的进程。 因此,是否终止某个进程的决定取决于该进程中所运行组件的状态。 下面,我们介绍决定终止进程所用的规则。
105 |
106 | ### 进程生命周期
107 |
108 | Android 系统将尽量长时间地保持应用进程,但为了新建进程或运行更重要的进程,最终需要移除旧进程来回收内存。 为了确定保留或终止哪些进程,系统会根据进程中正在运行的组件以及这些组件的状态,将每个进程放入“重要性层次结构”中。 必要时,系统会首先消除重要性最低的进程,然后是重要性略逊的进程,依此类推,以回收系统资源。
109 |
110 | 重要性层次结构一共有 5 级。以下列表按照重要程度列出了各类进程(第一个进程*最重要*,将是*最后一个被终止*的进程):
111 |
112 | | 名称 | 概括 | 回收状态 |
113 | | :------- | ------------------------------------------------------------ | ------------------------------------------------------------ |
114 | | 前台进程 | 正在交互 | 只有在内存不足以支持它们同时继续运行这一万不得已的情况下,系统才会终止它们 |
115 | | 可见进程 | 没有任何前台组件、但仍会影响用户在屏幕上所见内容的进程 | 可见进程被视为是极其重要的进程,除非为了维持所有前台进程同时运行而必须终止,否则系统不会终止这些进程。 |
116 | | 服务进程 | 正在运行已使用 `startService()` 方法启动的服务且不属于上述两个更高类别进程的进程。 | 除非内存不足以维持所有前台进程和可见进程同时运行,否则系统会让服务进程保持运行状态。 |
117 | | 后台进程 | 对用户不可见的 Activity 的进程 | 系统可能随时终止它们 |
118 | | 空进程 | 不含任何活动应用组件的进程 | 最容易为杀死 |
119 |
120 | ## LMK(LowMemoryKiller)
121 |
122 | - 为什么引入 LMK ?
123 |
124 | 进程的启动分冷启动和热启动,当用户退出某一个进程的时候,并不会真正的将进程退出,而是将这个进程放到后台,以便下次启动的时候可以马上启动起来,这个过程名为热启动,这也是Android 的设计理念之一。这个机制会带来一个问题,每个进程都有自己独立的内存地址空间,随着应用打开数量的增多, 系统已使用的内存越来越大,就很有可能导致系统内存不足。为了解决这个问题,系统引入 LowmemoryKiller (简称 lmk ) 管理所有进程,根据一定策略来 kill 某个进程并释放占用的内存,保证系统的正常运行。
125 |
126 | - LMK 基本原理
127 |
128 | 所有应用进程都是从 zygote 孵化出来的,记录在 AMS 中mLruProcesses 列表中,由 AMS 进行统一管理,AMS 中会根据进程的状态更新进程对应的 oom_adj 值,这个值会通过文件传递到 kernel 中去,kernel 有个低内存回收机制,在内存达到一定阀值时会触发清理 oom_adj 值高的进程腾出更多的内存空间
129 |
130 | - LMK 杀进程标准
131 |
132 | minfree : 存放6个数值,单位内存页面数 ( 一个页面 4kb )
133 |
134 | 
135 |
136 | 得到的数值为:18432 , 23040 , 27648 , 32256 , 36864 , 46080
137 |
138 | 这 6 个数值分别代表 android 系统回收 6 种进程的阈值,这么看不方便查看,转换为 M 会更直观,这 6 个数值的单位为 page 1 page = 4 KB ,所以通过数值 * 4 / 1024 就能转换为M : 72 M , 90 M , 108 M , 126 M , 144 M , 180M
139 |
140 | 也就是说:
141 |
142 | 1.前台进程(foreground),2.可见进程(visible),3.次要服务(secondary server),4.后台进程(hidden),5.内容供应节点(content provider),6.空进程(empty)这6类进程进行回收的内存阈值分别为72M,90M,108M,126M,144M,180 M
143 |
144 | 当内存到 180 M的时候会将空进程进行回收,当内存到 144 M 的时候把空进程回收完以后开始对内容供应节点进行回收,并不是所有的内容供应节点都回收,而是通过判断它的优先级进行回收,优先级是用 oom_adj 的值来表示,值越大回收的几率越高
145 |
146 |
147 |
148 | adj 查看:
149 |
150 | ```java
151 | cat /sys/module/lowmemorykiller/parameters/adj
152 | ```
153 |
154 | 
155 |
156 | 查看进程 adj 值:
157 |
158 | ```java
159 | adb shell ps
160 | ```
161 |
162 | 
163 |
164 | 值越低越不易被回收,0 代表就不会被回收。
165 |
166 | 内存阈值在不同的手机上不一样,一旦低于该值, Android 便开始按顺序关闭进程. 因此 Android 开始结束优先级最低的空进程,即当可用内存小于 180MB (46080)
167 |
168 | ## 进程保活方案
169 |
170 | ### Activity 提权
171 |
172 | 
173 |
174 | 这里可见 oom_adj 为 0 是不会被回收的
175 |
176 | 后台 oom_adj 为 6 内存不足会被回收
177 |
178 | 锁屏 oom_adj 开启一像素 Activity 为 0 相当于可见进程,不易被回收
179 |
180 | **实现原理:**
181 |
182 | 监控手机锁屏解锁事件,在屏幕锁屏时启动 1 个像素透明的 Activity ,在用户解锁时将 Activity 销毁掉,从而达到提高进程优先级的作用。
183 |
184 | **代码实现**
185 |
186 | 1. 创建 onePxActivity
187 |
188 | ```java
189 | @Override
190 | protected void onCreate(Bundle savedInstanceState) {
191 | super.onCreate(savedInstanceState);
192 | //设定一像素的activity
193 | Window window = getWindow();
194 | window.setGravity(Gravity.START | Gravity.TOP);
195 | WindowManager.LayoutParams params = window.getAttributes();
196 | params.x = 0;
197 | params.y = 0;
198 | params.height = 1;
199 | params.width = 1;
200 | window.setAttributes(params);
201 | //在一像素activity里注册广播接受者 接受到广播结束掉一像素
202 | br = new BroadcastReceiver() {
203 | @Override
204 | public void onReceive(Context context, Intent intent) {
205 | finish();
206 | }
207 | };
208 | registerReceiver(br, new IntentFilter("finish activity"));
209 | checkScreenOn("onCreate");
210 | }
211 | ```
212 |
213 |
214 |
215 | 2. 创建锁屏开屏广播接收
216 |
217 | ```java
218 | @Override
219 | public void onReceive(final Context context, Intent intent) {
220 | if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { //屏幕关闭的时候接受到广播
221 | appIsForeground = IsForeground(context);
222 | try {
223 | Intent it = new Intent(context, OnePixelActivity.class);
224 | it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
225 | it.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
226 | context.startActivity(it);
227 | } catch (Exception e) {
228 | e.printStackTrace();
229 | }
230 | //通知屏幕已关闭,开始播放无声音乐
231 | context.sendBroadcast(new Intent("_ACTION_SCREEN_OFF"));
232 | } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { //屏幕打开的时候发送广播 结束一像素
233 | context.sendBroadcast(new Intent("finish activity"));
234 | if (!appIsForeground) {
235 | appIsForeground = false;
236 | try {
237 | Intent home = new Intent(Intent.ACTION_MAIN);
238 | home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
239 | home.addCategory(Intent.CATEGORY_HOME);
240 | context.getApplicationContext().startActivity(home);
241 | } catch (Exception e) {
242 | e.printStackTrace();
243 | }
244 | }
245 | //通知屏幕已点亮,停止播放无声音乐
246 | context.sendBroadcast(new Intent("_ACTION_SCREEN_ON"));
247 | }
248 | }
249 | ```
250 |
251 |
252 |
253 | ### Service 提权
254 |
255 | 创建一个前台服务用于提高 app 在按下 home 键之后的进程优先级
256 |
257 | ```java
258 | private void startService(Context context) {
259 | try {
260 | Log.i(TAG, "---》启动双进程保活服务");
261 | //启动本地服务
262 | Intent localIntent = new Intent(context, LocalService.class);
263 | //启动守护进程
264 | Intent guardIntent = new Intent(context, RemoteService.class);
265 | if (Build.VERSION.SDK_INT >= 26) {
266 | startForegroundService(localIntent);
267 | startForegroundService(guardIntent);
268 | } else {
269 | startService(localIntent);
270 | startService(guardIntent);
271 | }
272 | } catch (Exception e) {
273 | Log.e(TAG, e.getMessage());
274 | }
275 | }
276 | ```
277 |
278 | 注意如果开启 startForegroundService 前台服务,那么必须在 5 s内开启一个前台进程的服务通知栏,不会报 ANR
279 |
280 | ```java
281 | startForeground(KeepAliveConfig.FOREGROUD_NOTIFICATION_ID, notification);
282 | ```
283 |
284 | ### 广播拉活(在 8.0 以下很受用)
285 |
286 | 在发生特定系统事件时,系统会发出广播,通过在 AndroidManifest 中静态注册对应的广播监听器,即可在发生响应事件时拉活。但是从android 7.0 开始,对广播进行了限制,而且在 8.0 更加严格。
287 |
288 | 以静态广播的形式注册
289 |
290 | ```java
291 |
292 |
293 |
294 |
295 |
296 | ```
297 |
298 | ### 全家桶 拉活
299 |
300 | 有多个 app 在用户设备上安装,只要开启其中一个就可以将其他的app 也拉活。比如手机里装了手 Q、QQ 空间、兴趣部落等等,那么打开任意一个 app 后,其他的 app 也都会被唤醒。
301 |
302 | ### Service 机制拉活
303 |
304 | 将 Service 设置为 START_STICKY,利用系统机制在 Service 挂掉后自动拉活
305 |
306 | 只要 targetSdkVersion 不小于5,就默认是 START_STICKY。
307 | 但是某些 ROM 系统不会拉活。并且经过测试,Service 第一次被异常杀死后很快被重启,第二次会比第一次慢,第三次又会比前一次慢,一旦在短时间内 Service 被杀死 4-5 次,则系统不再拉起。
308 |
309 | ### 账号同步拉活(只做了解,不靠谱)
310 |
311 | 手机系统设置里会有 “帐户” 一项功能,任何第三方 APP 都可以通过此功能将数据在一定时间内同步到服务器中去。系统在将 APP 帐户同步时,会将未启动的 APP 进程拉活
312 |
313 | ### JobScheduler 拉活(靠谱,8.0 官方推荐)
314 |
315 | JobScheduler 允许在特定状态与特定时间间隔周期执行任务。可以利用它的这个特点完成保活的功能,效果即开启一个定时器,与普通定时器不同的是其调度由系统完成。
316 |
317 | 注意 setPeriodic 方法
318 | 在 7.0 以上如果设置小于 15 min 不起作用,可以使用setMinimumLatency 设置延时启动,并且轮询
319 |
320 | ```java
321 | public static void startJob(Context context) {
322 | try {
323 | mJobScheduler = (JobScheduler) context.getSystemService(
324 | Context.JOB_SCHEDULER_SERVICE);
325 | JobInfo.Builder builder = new JobInfo.Builder(10,
326 | new ComponentName(context.getPackageName(),
327 | JobHandlerService.class.getName())).setPersisted(true);
328 | /**
329 | * I was having this problem and after review some blogs and the official documentation,
330 | * I realised that JobScheduler is having difference behavior on Android N(24 and 25).
331 | * JobScheduler works with a minimum periodic of 15 mins.
332 | *
333 | */
334 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
335 | //7.0以上延迟1s执行
336 | builder.setMinimumLatency(KeepAliveConfig.JOB_TIME);
337 | } else {
338 | //每隔1s执行一次job
339 | builder.setPeriodic(KeepAliveConfig.JOB_TIME);
340 | }
341 | mJobScheduler.schedule(builder.build());
342 |
343 | } catch (Exception e) {
344 | Log.e("startJob->", e.getMessage());
345 | }
346 | }
347 | ```
348 |
349 | ### 推送拉活
350 |
351 | 根据终端不同,在小米手机(包括 MIUI)接入小米推送、华为手机接入华为推送。
352 |
353 | ### Native 拉活
354 |
355 | Native fork 子进程用于观察当前 app 主进程的存亡状态。对于 5.0以上成功率极低。
356 |
357 | ### 后台循环播放一条无声文件
358 |
359 |
360 |
361 | ```java
362 | //如果选择流氓模式,就默认接收了耗电的缺点,但是保活效果很好。
363 | if (mediaPlayer == null && KeepAliveConfig.runMode == RunMode.HIGH_POWER_CONSUMPTION) {
364 | mediaPlayer = MediaPlayer.create(this, R.raw.novioce);
365 | mediaPlayer.setVolume(0f, 0f);
366 | mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
367 | @Override
368 | public void onCompletion(MediaPlayer mediaPlayer) {
369 | Log.i(TAG, "循环播放音乐");
370 | play();
371 | }
372 | });
373 | play();
374 | }
375 | ```
376 |
377 |
378 |
379 | ### 双进程守护 (靠谱)
380 |
381 | 两个进程相互绑定 (bindService),如果有其中一个进程被杀,那么另外一个进程就会将被杀的进程重新拉起
382 |
383 | 
384 |
385 | ## 总结
386 |
387 | 进程保活就讲到这里了,最后我自己是结合里面最靠谱的(Activity + Service 提权 + Service 机制拉活 + JobScheduler 定时检测进程是否运行 + 后台播放无声文件 + 双进程守护),然后组成了一个进程保活终极方案。
388 | 文章中只是部分代码,感兴趣的可以[下载 demo](https://github.com/yangkun19921001/KeepAlive) 试下保活效果。
389 |
390 |
391 |
392 |
393 |
394 |
--------------------------------------------------------------------------------
/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.ykun.keepalive"
7 | minSdkVersion 21
8 | targetSdkVersion 28
9 | versionCode 1
10 | versionName "1.0"
11 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
12 | }
13 | buildTypes {
14 | release {
15 | minifyEnabled false
16 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
17 | }
18 | }
19 | }
20 |
21 | dependencies {
22 | implementation fileTree(dir: 'libs', include: ['*.jar'])
23 | implementation 'com.android.support:appcompat-v7:28.0.0'
24 | implementation 'com.android.support.constraint:constraint-layout:1.1.3'
25 | testImplementation 'junit:junit:4.12'
26 | androidTestImplementation 'com.android.support.test:runner:1.0.2'
27 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
28 | implementation project(path: ':live_library')
29 | // implementation project(path: ':keeplive')
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/app/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 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/ykun/keepalive/ExampleInstrumentedTest.java:
--------------------------------------------------------------------------------
1 | package com.ykun.keepalive;
2 |
3 | import android.content.Context;
4 | import android.support.test.InstrumentationRegistry;
5 | import android.support.test.runner.AndroidJUnit4;
6 |
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 |
10 | import static org.junit.Assert.*;
11 |
12 | /**
13 | * Instrumented test, which will execute on an Android device.
14 | *
15 | * @see Testing documentation
16 | */
17 | @RunWith(AndroidJUnit4.class)
18 | public class ExampleInstrumentedTest {
19 | @Test
20 | public void useAppContext() {
21 | // Context of the app under test.
22 | Context appContext = InstrumentationRegistry.getTargetContext();
23 |
24 | assertEquals("com.ykun.keepalive", appContext.getPackageName());
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/app/src/main/java/com/ykun/keepalive/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.ykun.keepalive;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 | import android.os.Build;
6 | import android.os.Bundle;
7 | import android.os.Handler;
8 | import android.os.Message;
9 | import android.support.v7.app.AppCompatActivity;
10 | import android.util.Log;
11 | import android.view.View;
12 | import android.widget.TextView;
13 |
14 | import com.ykun.live_library.KeepAliveManager;
15 | import com.ykun.live_library.config.ForegroundNotification;
16 | import com.ykun.live_library.config.ForegroundNotificationClickListener;
17 |
18 | import java.text.SimpleDateFormat;
19 | import java.util.Date;
20 |
21 | import static com.ykun.live_library.config.RunMode.HIGH_POWER_CONSUMPTION;
22 |
23 | public class MainActivity extends AppCompatActivity {
24 |
25 | private String TAG = getClass().getSimpleName();
26 |
27 | private SimpleDateFormat mSimpleDateFormat = new SimpleDateFormat("YYYY-MM-DD hh:mm:ss");
28 |
29 |
30 | private Handler handler = new Handler(){
31 | @Override
32 | public void handleMessage(Message msg) {
33 | super.handleMessage(msg);
34 | tvTime.setText(mSimpleDateFormat.format(new Date()));
35 | sendDelayMeg();
36 | }
37 | };
38 | private TextView tvTime;
39 |
40 |
41 |
42 | @Override
43 | protected void onCreate(Bundle savedInstanceState) {
44 | super.onCreate(savedInstanceState);
45 | setContentView(R.layout.activity_main);
46 |
47 | tvTime = findViewById(R.id.tv_time);
48 | /**
49 | * 开启电量优化
50 | */
51 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
52 | KeepAliveManager.batteryOptimizations(getApplicationContext());
53 | }
54 |
55 | }
56 |
57 | /**
58 | * 启动代码适配的保活
59 | * @param view
60 | */
61 | public void startKeepAlive(View view) {
62 | start();
63 | sendDelayMeg();
64 | }
65 |
66 | private void sendDelayMeg() {
67 | handler.sendEmptyMessageDelayed(0, 1000);
68 | }
69 |
70 | /**
71 | * 停止保活
72 | * @param view
73 | */
74 | public void stopKeepAlive(View view) {
75 | KeepAliveManager.stopWork(getApplication());
76 | }
77 |
78 | public void start() {
79 | //启动保活服务
80 | KeepAliveManager.toKeepAlive(
81 | getApplication()
82 | , HIGH_POWER_CONSUMPTION,
83 | "进程保活",
84 | "Process: System(哥们儿) 我不想被杀死",
85 | R.mipmap.ic_launcher,
86 | new ForegroundNotification(
87 | //定义前台服务的通知点击事件
88 | new ForegroundNotificationClickListener() {
89 | @Override
90 | public void foregroundNotificationClick(Context context, Intent intent) {
91 | Log.d("JOB-->", " foregroundNotificationClick");
92 | }
93 | })
94 | );
95 | }
96 |
97 | /**
98 | * 开启系统设置保活
99 | * @param view
100 | */
101 | public void launch_system(View view) {
102 | sendDelayMeg();
103 | KeepAliveManager.launcherSyskeepAlive(getApplicationContext());
104 | }
105 |
106 |
107 | @Override
108 | public void onBackPressed() {
109 | moveTaskToBack(true);
110 | }
111 |
112 | @Override
113 | protected void onDestroy() {
114 | super.onDestroy();
115 | handler.removeMessages(0);
116 | handler = null;
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
12 |
13 |
19 |
22 |
25 |
26 |
27 |
28 |
34 |
35 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_launcher_background.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
10 |
15 |
20 |
25 |
30 |
35 |
40 |
45 |
50 |
55 |
60 |
65 |
70 |
75 |
80 |
85 |
90 |
95 |
100 |
105 |
110 |
115 |
120 |
125 |
130 |
135 |
140 |
145 |
150 |
155 |
160 |
165 |
170 |
171 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
10 |
11 |
12 |
19 |
20 |
21 |
27 |
28 |
34 |
35 |
36 |
41 |
42 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/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 | keepAlive
3 |
4 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/ykun/keepalive/ExampleUnitTest.java:
--------------------------------------------------------------------------------
1 | package com.ykun.keepalive;
2 |
3 | import org.junit.Test;
4 |
5 | import static org.junit.Assert.*;
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * @see Testing documentation
11 | */
12 | public class ExampleUnitTest {
13 | @Test
14 | public void addition_isCorrect() {
15 | assertEquals(4, 2 + 2);
16 | }
17 | }
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 |
3 | buildscript {
4 | repositories {
5 | google()
6 | jcenter()
7 |
8 | }
9 | dependencies {
10 | classpath 'com.android.tools.build:gradle:3.4.0'
11 |
12 | // NOTE: Do not place your application dependencies here; they belong
13 | // in the individual module build.gradle files
14 | }
15 | }
16 |
17 | allprojects {
18 | repositories {
19 | google()
20 | jcenter()
21 |
22 | }
23 | }
24 |
25 | task clean(type: Delete) {
26 | delete rootProject.buildDir
27 | }
28 |
--------------------------------------------------------------------------------
/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 |
15 |
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Sat Jun 08 21:43:48 GMT+08:00 2019
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-5.1.1-all.zip
7 |
--------------------------------------------------------------------------------
/gradlew:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | ##############################################################################
4 | ##
5 | ## Gradle start up script for UN*X
6 | ##
7 | ##############################################################################
8 |
9 | # Attempt to set APP_HOME
10 | # Resolve links: $0 may be a link
11 | PRG="$0"
12 | # Need this for relative symlinks.
13 | while [ -h "$PRG" ] ; do
14 | ls=`ls -ld "$PRG"`
15 | link=`expr "$ls" : '.*-> \(.*\)$'`
16 | if expr "$link" : '/.*' > /dev/null; then
17 | PRG="$link"
18 | else
19 | PRG=`dirname "$PRG"`"/$link"
20 | fi
21 | done
22 | SAVED="`pwd`"
23 | cd "`dirname \"$PRG\"`/" >/dev/null
24 | APP_HOME="`pwd -P`"
25 | cd "$SAVED" >/dev/null
26 |
27 | APP_NAME="Gradle"
28 | APP_BASE_NAME=`basename "$0"`
29 |
30 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
31 | DEFAULT_JVM_OPTS=""
32 |
33 | # Use the maximum available, or set MAX_FD != -1 to use that value.
34 | MAX_FD="maximum"
35 |
36 | warn () {
37 | echo "$*"
38 | }
39 |
40 | die () {
41 | echo
42 | echo "$*"
43 | echo
44 | exit 1
45 | }
46 |
47 | # OS specific support (must be 'true' or 'false').
48 | cygwin=false
49 | msys=false
50 | darwin=false
51 | nonstop=false
52 | case "`uname`" in
53 | CYGWIN* )
54 | cygwin=true
55 | ;;
56 | Darwin* )
57 | darwin=true
58 | ;;
59 | MINGW* )
60 | msys=true
61 | ;;
62 | NONSTOP* )
63 | nonstop=true
64 | ;;
65 | esac
66 |
67 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
68 |
69 | # Determine the Java command to use to start the JVM.
70 | if [ -n "$JAVA_HOME" ] ; then
71 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
72 | # IBM's JDK on AIX uses strange locations for the executables
73 | JAVACMD="$JAVA_HOME/jre/sh/java"
74 | else
75 | JAVACMD="$JAVA_HOME/bin/java"
76 | fi
77 | if [ ! -x "$JAVACMD" ] ; then
78 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
79 |
80 | Please set the JAVA_HOME variable in your environment to match the
81 | location of your Java installation."
82 | fi
83 | else
84 | JAVACMD="java"
85 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
86 |
87 | Please set the JAVA_HOME variable in your environment to match the
88 | location of your Java installation."
89 | fi
90 |
91 | # Increase the maximum file descriptors if we can.
92 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
93 | MAX_FD_LIMIT=`ulimit -H -n`
94 | if [ $? -eq 0 ] ; then
95 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
96 | MAX_FD="$MAX_FD_LIMIT"
97 | fi
98 | ulimit -n $MAX_FD
99 | if [ $? -ne 0 ] ; then
100 | warn "Could not set maximum file descriptor limit: $MAX_FD"
101 | fi
102 | else
103 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
104 | fi
105 | fi
106 |
107 | # For Darwin, add options to specify how the application appears in the dock
108 | if $darwin; then
109 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
110 | fi
111 |
112 | # For Cygwin, switch paths to Windows format before running java
113 | if $cygwin ; then
114 | APP_HOME=`cygpath --path --mixed "$APP_HOME"`
115 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
116 | JAVACMD=`cygpath --unix "$JAVACMD"`
117 |
118 | # We build the pattern for arguments to be converted via cygpath
119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
120 | SEP=""
121 | for dir in $ROOTDIRSRAW ; do
122 | ROOTDIRS="$ROOTDIRS$SEP$dir"
123 | SEP="|"
124 | done
125 | OURCYGPATTERN="(^($ROOTDIRS))"
126 | # Add a user-defined pattern to the cygpath arguments
127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then
128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
129 | fi
130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh
131 | i=0
132 | for arg in "$@" ; do
133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
135 |
136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
138 | else
139 | eval `echo args$i`="\"$arg\""
140 | fi
141 | i=$((i+1))
142 | done
143 | case $i in
144 | (0) set -- ;;
145 | (1) set -- "$args0" ;;
146 | (2) set -- "$args0" "$args1" ;;
147 | (3) set -- "$args0" "$args1" "$args2" ;;
148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
154 | esac
155 | fi
156 |
157 | # Escape application args
158 | save () {
159 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
160 | echo " "
161 | }
162 | APP_ARGS=$(save "$@")
163 |
164 | # Collect all arguments for the java command, following the shell quoting and substitution rules
165 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
166 |
167 | # by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
168 | if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
169 | cd "$(dirname "$0")"
170 | fi
171 |
172 | exec "$JAVACMD" "$@"
173 |
--------------------------------------------------------------------------------
/gradlew.bat:
--------------------------------------------------------------------------------
1 | @if "%DEBUG%" == "" @echo off
2 | @rem ##########################################################################
3 | @rem
4 | @rem Gradle startup script for Windows
5 | @rem
6 | @rem ##########################################################################
7 |
8 | @rem Set local scope for the variables with windows NT shell
9 | if "%OS%"=="Windows_NT" setlocal
10 |
11 | set DIRNAME=%~dp0
12 | if "%DIRNAME%" == "" set DIRNAME=.
13 | set APP_BASE_NAME=%~n0
14 | set APP_HOME=%DIRNAME%
15 |
16 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
17 | set DEFAULT_JVM_OPTS=
18 |
19 | @rem Find java.exe
20 | if defined JAVA_HOME goto findJavaFromJavaHome
21 |
22 | set JAVA_EXE=java.exe
23 | %JAVA_EXE% -version >NUL 2>&1
24 | if "%ERRORLEVEL%" == "0" goto init
25 |
26 | echo.
27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
28 | echo.
29 | echo Please set the JAVA_HOME variable in your environment to match the
30 | echo location of your Java installation.
31 |
32 | goto fail
33 |
34 | :findJavaFromJavaHome
35 | set JAVA_HOME=%JAVA_HOME:"=%
36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
37 |
38 | if exist "%JAVA_EXE%" goto init
39 |
40 | echo.
41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
42 | echo.
43 | echo Please set the JAVA_HOME variable in your environment to match the
44 | echo location of your Java installation.
45 |
46 | goto fail
47 |
48 | :init
49 | @rem Get command-line arguments, handling Windows variants
50 |
51 | if not "%OS%" == "Windows_NT" goto win9xME_args
52 |
53 | :win9xME_args
54 | @rem Slurp the command line arguments.
55 | set CMD_LINE_ARGS=
56 | set _SKIP=2
57 |
58 | :win9xME_args_slurp
59 | if "x%~1" == "x" goto execute
60 |
61 | set CMD_LINE_ARGS=%*
62 |
63 | :execute
64 | @rem Setup the command line
65 |
66 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
67 |
68 | @rem Execute Gradle
69 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
70 |
71 | :end
72 | @rem End local scope for the variables with windows NT shell
73 | if "%ERRORLEVEL%"=="0" goto mainEnd
74 |
75 | :fail
76 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
77 | rem the _cmd.exe /c_ return code!
78 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
79 | exit /b 1
80 |
81 | :mainEnd
82 | if "%OS%"=="Windows_NT" endlocal
83 |
84 | :omega
85 |
--------------------------------------------------------------------------------
/live_library/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/live_library/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.library'
2 |
3 | android {
4 | compileSdkVersion 28
5 |
6 |
7 | defaultConfig {
8 | minSdkVersion 21
9 | targetSdkVersion 28
10 | versionCode 1
11 | versionName "1.0.2"
12 |
13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
14 |
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 |
24 | sourceSets { main { aidl.srcDirs = ['src/main/aidl', 'src/main/aidl/'] } }
25 | }
26 |
27 | dependencies {
28 | implementation fileTree(dir: 'libs', include: ['*.jar'])
29 | implementation 'com.android.support:appcompat-v7:28.0.0-alpha3'
30 | }
31 |
--------------------------------------------------------------------------------
/live_library/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 |
--------------------------------------------------------------------------------
/live_library/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
19 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
35 |
36 |
37 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/live_library/src/main/aidl/com/ykun/keeplive/KeepAliveAidl.aidl:
--------------------------------------------------------------------------------
1 | package com.ykun.keeplive;
2 |
3 | interface KeepAliveAidl {
4 | //相互唤醒服务
5 | void wakeUp(String title, String discription, int iconRes);
6 | }
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/DevicesLaunchConfig.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library;
2 |
3 | import android.content.ComponentName;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Build;
7 | import android.support.annotation.NonNull;
8 |
9 | /**
10 | *
11 | * author : devyk on 2020-04-02 20:19
12 | * blog : https://juejin.im/user/578259398ac2470061f3a3fb/posts
13 | * github : https://github.com/yangkun19921001
14 | * mailbox : yang1001yk@gmail.com
15 | * desc : This is DevicesLaunchConfig
16 | *
17 | */
18 | public class DevicesLaunchConfig {
19 | private static Context mContext;
20 |
21 | /**
22 | * @param context
23 | */
24 | public static void launchSystemKeepAlive(Context context) {
25 | mContext = context.getApplicationContext();
26 | if (isHuawei())
27 | goHuaweiSetting();
28 | else if (isLeTV())
29 | goLetvSetting();
30 | else if (isMeizu())
31 | goMeizuSetting();
32 | else if (isSamsung())
33 | goSamsungSetting();
34 | else if (isVIVO())
35 | goVIVOSetting();
36 | else if (isXiaomi())
37 | goXiaomiSetting();
38 | }
39 |
40 |
41 | private static boolean isHuawei() {
42 | if (Build.BRAND == null) {
43 | return false;
44 | } else {
45 | return Build.BRAND.toLowerCase().equals("huawei") || Build.BRAND.toLowerCase().equals("honor");
46 | }
47 | }
48 |
49 |
50 | private static void goHuaweiSetting() {
51 | try {
52 | showActivity("com.huawei.systemmanager",
53 | "com.huawei.systemmanager.startupmgr.ui.StartupNormalAppListActivity");
54 | } catch (Exception e) {
55 | showActivity("com.huawei.systemmanager",
56 | "com.huawei.systemmanager.optimize.bootstart.BootStartActivity");
57 | }
58 | }
59 |
60 |
61 | public static boolean isXiaomi() {
62 | return Build.BRAND != null && Build.BRAND.toLowerCase().equals("xiaomi");
63 | }
64 |
65 | private static void goXiaomiSetting() {
66 | showActivity("com.miui.securitycenter",
67 | "com.miui.permcenter.autostart.AutoStartManagementActivity");
68 | }
69 |
70 | public static boolean isOPPO() {
71 | return Build.BRAND != null && Build.BRAND.toLowerCase().equals("oppo");
72 | }
73 |
74 | private static void goOPPOSetting() {
75 | try {
76 | showActivity("com.coloros.phonemanager");
77 | } catch (Exception e1) {
78 | try {
79 | showActivity("com.oppo.safe");
80 | } catch (Exception e2) {
81 | try {
82 | showActivity("com.coloros.oppoguardelf");
83 | } catch (Exception e3) {
84 | showActivity("com.coloros.safecenter");
85 | }
86 | }
87 | }
88 | }
89 |
90 | public static boolean isVIVO() {
91 | return Build.BRAND != null && Build.BRAND.toLowerCase().equals("vivo");
92 | }
93 |
94 | private static void goVIVOSetting() {
95 | showActivity("com.iqoo.secure");
96 | }
97 |
98 | public static boolean isMeizu() {
99 | return Build.BRAND != null && Build.BRAND.toLowerCase().equals("meizu");
100 | }
101 |
102 | private static void goMeizuSetting() {
103 | showActivity("com.meizu.safe");
104 | }
105 |
106 | public static boolean isSamsung() {
107 | return Build.BRAND != null && Build.BRAND.toLowerCase().equals("samsung");
108 | }
109 |
110 | private static void goSamsungSetting() {
111 | try {
112 | showActivity("com.samsung.android.sm_cn");
113 | } catch (Exception e) {
114 | showActivity("com.samsung.android.sm");
115 | }
116 | }
117 |
118 | public static boolean isLeTV() {
119 | return Build.BRAND != null && Build.BRAND.toLowerCase().equals("letv");
120 | }
121 |
122 | private static void goLetvSetting() {
123 | showActivity("com.letv.android.letvsafe",
124 | "com.letv.android.letvsafe.AutobootManageActivity");
125 | }
126 |
127 | public static boolean isSmartisan() {
128 | return Build.BRAND != null && Build.BRAND.toLowerCase().equals("smartisan");
129 | }
130 |
131 | private static void goSmartisanSetting() {
132 | showActivity("com.smartisanos.security");
133 | }
134 |
135 | /**
136 | * 跳转到指定应用的首页
137 | */
138 | private static void showActivity(@NonNull String packageName) {
139 | Intent intent = mContext.getPackageManager().getLaunchIntentForPackage(packageName);
140 | mContext.startActivity(intent);
141 | }
142 |
143 | /**
144 | * 跳转到指定应用的指定页面
145 | */
146 | private static void showActivity(@NonNull String packageName, @NonNull String activityDir) {
147 | Intent intent = new Intent();
148 | intent.setComponent(new ComponentName(packageName, activityDir));
149 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
150 | mContext.startActivity(intent);
151 | }
152 |
153 | }
154 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/IKeepAliveRuning.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library;
2 |
3 | public interface IKeepAliveRuning {
4 | void onRuning();
5 | void onStop();
6 | }
7 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/KeepAliveManager.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library;
2 |
3 | import android.app.Application;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.net.Uri;
7 | import android.os.Build;
8 | import android.os.PowerManager;
9 | import android.provider.Settings;
10 | import android.support.annotation.NonNull;
11 | import android.support.annotation.RequiresApi;
12 | import android.util.Log;
13 |
14 | import com.ykun.live_library.config.ForegroundNotification;
15 | import com.ykun.live_library.config.KeepAliveConfig;
16 | import com.ykun.live_library.config.NotificationUtils;
17 | import com.ykun.live_library.config.RunMode;
18 | import com.ykun.live_library.service.JobHandlerService;
19 | import com.ykun.live_library.service.LocalService;
20 | import com.ykun.live_library.service.RemoteService;
21 | import com.ykun.live_library.utils.KeepAliveUtils;
22 | import com.ykun.live_library.utils.SPUtils;
23 |
24 | import static com.ykun.live_library.config.KeepAliveConfig.SP_NAME;
25 |
26 | /**
27 | * 进程保活管理
28 | */
29 | public class KeepAliveManager {
30 | private static final String TAG = "KeepAliveManager";
31 |
32 | /**
33 | * 启动保活
34 | *
35 | * @param application your application
36 | * @param runMode
37 | * @param foregroundNotification 前台服务
38 | */
39 | public static void toKeepAlive(@NonNull Application application, @NonNull int runMode, String title, String content, int res_icon, ForegroundNotification foregroundNotification) {
40 | if (KeepAliveUtils.isRunning(application)) {
41 | KeepAliveConfig.foregroundNotification = foregroundNotification;
42 | SPUtils.getInstance(application, SP_NAME).put(KeepAliveConfig.TITLE, title);
43 | SPUtils.getInstance(application, SP_NAME).put(KeepAliveConfig.CONTENT, content);
44 | SPUtils.getInstance(application, SP_NAME).put(KeepAliveConfig.RES_ICON, res_icon);
45 | SPUtils.getInstance(application, SP_NAME).put(KeepAliveConfig.RUN_MODE, runMode);
46 | //优化后的枚举
47 | RunMode.setShape(runMode);
48 | KeepAliveConfig.runMode = RunMode.getShape();
49 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
50 | //启动定时器,在定时器中启动本地服务和守护进程
51 | JobHandlerService.startJob(application);
52 | } else {
53 | Intent localIntent = new Intent(application, LocalService.class);
54 | //启动守护进程
55 | Intent guardIntent = new Intent(application, RemoteService.class);
56 | if (Build.VERSION.SDK_INT >= 26) {
57 | application.startForegroundService(localIntent);
58 | application.startForegroundService(guardIntent);
59 | } else {
60 | application.startService(localIntent);
61 | application.startService(guardIntent);
62 | }
63 | }
64 | }
65 | }
66 |
67 | public static void stopWork(Application application) {
68 | try {
69 | KeepAliveConfig.foregroundNotification = null;
70 | KeepAliveConfig.keepLiveService = null;
71 | KeepAliveConfig.runMode = RunMode.getShape();
72 | JobHandlerService.stopJob();
73 | //启动本地服务
74 | Intent localIntent = new Intent(application, LocalService.class);
75 | //启动守护进程
76 | Intent guardIntent = new Intent(application, RemoteService.class);
77 | application.stopService(localIntent);
78 | application.stopService(guardIntent);
79 | application.stopService(new Intent(application, JobHandlerService.class));
80 | } catch (Exception e) {
81 | Log.e(TAG, "stopWork-->" + e.getMessage());
82 | }
83 | }
84 |
85 |
86 | public static void sendNotification(Context context, String title, String content, int icon, Intent intent2) {
87 | NotificationUtils.sendNotification(context, title, content, icon, intent2);
88 | }
89 |
90 | /**
91 | * 启动系统保活
92 | * @param cex
93 | */
94 | public static void launcherSyskeepAlive(Context cex){
95 | DevicesLaunchConfig.launchSystemKeepAlive(cex);
96 | }
97 |
98 | /**
99 | * 启动电量优化
100 | */
101 | @RequiresApi(api = Build.VERSION_CODES.M)
102 | public static void batteryOptimizations(Context context) {
103 | if (!isIgnoringBatteryOptimizations(context)) {
104 | requestIgnoreBatteryOptimizations(context);
105 | }
106 | }
107 |
108 | @RequiresApi(api = Build.VERSION_CODES.M)
109 | private static boolean isIgnoringBatteryOptimizations(Context context) {
110 | boolean isIgnoring = false;
111 | PowerManager powerManager = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
112 | if (powerManager != null) {
113 | isIgnoring = powerManager.isIgnoringBatteryOptimizations(context.getPackageName());
114 | }
115 | return isIgnoring;
116 | }
117 |
118 |
119 | @RequiresApi(api = Build.VERSION_CODES.M)
120 | private static void requestIgnoreBatteryOptimizations(Context context) {
121 | try {
122 | Intent intent = new Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
123 | intent.setData(Uri.parse("package:" + context.getPackageName()));
124 | intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
125 | context.startActivity(intent);
126 | } catch (Exception e) {
127 | e.printStackTrace();
128 | }
129 | }
130 |
131 | }
132 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/KeepAliveRuning.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library;
2 |
3 | import android.util.Log;
4 |
5 | public class KeepAliveRuning implements IKeepAliveRuning {
6 |
7 |
8 | @Override
9 | public void onRuning() {
10 | //TODO--------------------------------------------
11 | Log.e("runing?KeepAliveRuning", "true");
12 | }
13 |
14 | @Override
15 | public void onStop() {
16 | Log.e("runing?KeepAliveRuning", "false");
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/config/ForegroundNotification.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.config;
2 |
3 |
4 | import android.support.annotation.NonNull;
5 |
6 | import java.io.Serializable;
7 |
8 | /**
9 | * 默认前台服务样式
10 | */
11 | public class ForegroundNotification implements Serializable {
12 | private String title;
13 | private String description;
14 | private int iconRes;
15 | private ForegroundNotificationClickListener foregroundNotificationClickListener;
16 | private ForegroundNotification(){
17 | }
18 | public ForegroundNotification(ForegroundNotificationClickListener foregroundNotificationClickListener) {
19 | this.foregroundNotificationClickListener = foregroundNotificationClickListener;
20 | }
21 |
22 |
23 | /**
24 | * 初始化
25 | * @return ForegroundNotification
26 | */
27 | public static ForegroundNotification ini(){
28 | return new ForegroundNotification();
29 | }
30 | /**
31 | * 设置前台通知点击事件
32 | * @param foregroundNotificationClickListener 前台通知点击回调
33 | * @return ForegroundNotification
34 | */
35 | public ForegroundNotification foregroundNotificationClickListener(@NonNull ForegroundNotificationClickListener foregroundNotificationClickListener){
36 | this.foregroundNotificationClickListener = foregroundNotificationClickListener;
37 | return this;
38 | }
39 |
40 |
41 | public ForegroundNotificationClickListener getForegroundNotificationClickListener() {
42 | return foregroundNotificationClickListener;
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/config/ForegroundNotificationClickListener.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.config;
2 |
3 | import android.content.Context;
4 | import android.content.Intent;
5 |
6 | /**
7 | * 前台服务通知点击事件
8 | */
9 | public interface ForegroundNotificationClickListener {
10 | void foregroundNotificationClick(Context context, Intent intent);
11 | }
12 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/config/KeepAliveConfig.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.config;
2 |
3 | import com.ykun.live_library.R;
4 |
5 | /**
6 | * 进程保活需要配置的属性
7 | */
8 | public class KeepAliveConfig {
9 |
10 | /**
11 | * Job 的时间
12 | */
13 | public static final int JOB_TIME = 1000;
14 | public static final int FOREGROUD_NOTIFICATION_ID = 8888;
15 | /**
16 | * 运行模式
17 | */
18 | public static final String RUN_MODE = "RUN_MODE";
19 | /**
20 | * 进程开启的广播
21 | */
22 | public static final String PROCESS_ALIVE_ACTION = "PROCESS_ALIVE_ACTION";
23 | public static final String PROCESS_STOP_ACTION = "PROCESS_STOP_ACTION";
24 | public static ForegroundNotification foregroundNotification = null;
25 | public static KeepLiveService keepLiveService = null;
26 | public static int runMode = RunMode.getShape();
27 |
28 | /**
29 | * 广播通知的 action
30 | */
31 | public static String NOTIFICATION_ACTION = "NOTIFICATION_ACTION";
32 |
33 | public static String TITLE = "TITLE";
34 | public static String CONTENT = "CONTENT";
35 | public static String RES_ICON = "RES_ICON";
36 | public static int DEF_ICONS = R.drawable.ic_launcher;
37 | public static String SP_NAME = "KeepAliveConfig";
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/config/KeepLiveService.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.config;
2 |
3 | /**
4 | * 需要保活的服务
5 | */
6 | public interface KeepLiveService {
7 | /**
8 | * 运行中
9 | * 由于服务可能会多次自动启动,该方法可能重复调用
10 | */
11 | void onWorking();
12 |
13 | /**
14 | * 服务终止
15 | * 由于服务可能会被多次终止,该方法可能重复调用,需同onWorking配套使用,如注册和注销
16 | */
17 | void onStop();
18 | }
19 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/config/NotificationUtils.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.config;
2 |
3 | import android.app.Notification;
4 | import android.app.NotificationChannel;
5 | import android.app.NotificationManager;
6 | import android.app.PendingIntent;
7 | import android.content.Context;
8 | import android.content.ContextWrapper;
9 | import android.content.Intent;
10 | import android.os.Build;
11 | import android.support.annotation.NonNull;
12 | import android.support.annotation.RequiresApi;
13 | import android.support.v4.app.NotificationCompat;
14 | import android.text.TextUtils;
15 |
16 | import com.ykun.live_library.R;
17 |
18 | public class NotificationUtils extends ContextWrapper {
19 | private NotificationManager manager;
20 | private String id;
21 | private String name;
22 | private Context mContext;
23 | private NotificationChannel channel;
24 |
25 | private NotificationUtils(Context context) {
26 | super(context);
27 | this.mContext = context;
28 | id = mContext.getPackageName();
29 | name = mContext.getPackageName();
30 | }
31 |
32 | @RequiresApi(api = Build.VERSION_CODES.O)
33 | public void createNotificationChannel() {
34 | if (channel == null) {
35 | channel = new NotificationChannel(id, name, NotificationManager.IMPORTANCE_HIGH);
36 | channel.enableVibration(false);
37 | channel.enableLights(false);
38 | channel.enableVibration(false);
39 | channel.setVibrationPattern(new long[]{0});
40 | channel.setSound(null, null);
41 | getManager().createNotificationChannel(channel);
42 | }
43 | }
44 |
45 | private NotificationManager getManager() {
46 | if (manager == null) {
47 | manager = (NotificationManager) mContext.getSystemService(NOTIFICATION_SERVICE);
48 | }
49 | return manager;
50 | }
51 |
52 | @RequiresApi(api = Build.VERSION_CODES.O)
53 | public Notification.Builder getChannelNotification(String title, String content, int icon, Intent intent) {
54 | //PendingIntent.FLAG_UPDATE_CURRENT 这个类型才能传值
55 | PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
56 | if (TextUtils.isEmpty(title)){
57 | title = mContext.getApplicationInfo().name;
58 | }
59 | if (TextUtils.isEmpty(content)){
60 | content = mContext.getApplicationInfo().name;
61 | }
62 | if (icon == 0) {
63 | icon = R.drawable.ic_launcher;
64 | }
65 |
66 | return new Notification.Builder(mContext, id)
67 | .setContentTitle(title)
68 | .setContentText(content)
69 | .setSmallIcon(icon)
70 | .setAutoCancel(true)
71 | .setContentIntent(pendingIntent);
72 | }
73 |
74 | public NotificationCompat.Builder getNotification_25(String title, String content, int icon, Intent intent) {
75 | //PendingIntent.FLAG_UPDATE_CURRENT 这个类型才能传值
76 | PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
77 | return new NotificationCompat.Builder(mContext, id)
78 | .setContentTitle(title)
79 | .setContentText(content)
80 | .setSmallIcon(icon)
81 | .setAutoCancel(true)
82 | .setVibrate(new long[]{0})
83 | .setContentIntent(pendingIntent);
84 | }
85 |
86 | public static void sendNotification(@NonNull Context context, @NonNull String title, @NonNull String content, @NonNull int icon, @NonNull Intent intent) {
87 | NotificationUtils notificationUtils = new NotificationUtils(context);
88 | Notification notification = null;
89 | if (Build.VERSION.SDK_INT >= 26) {
90 | notificationUtils.createNotificationChannel();
91 | notification = notificationUtils.getChannelNotification(title, content, icon, intent).build();
92 | } else {
93 | notification = notificationUtils.getNotification_25(title, content, icon, intent).build();
94 | }
95 | notificationUtils.getManager().notify(new java.util.Random().nextInt(10000), notification);
96 | }
97 |
98 | public static Notification createNotification(@NonNull Context context, @NonNull String title, @NonNull String content, @NonNull int icon, @NonNull Intent intent) {
99 | NotificationUtils notificationUtils = new NotificationUtils(context);
100 | Notification notification = null;
101 | if (Build.VERSION.SDK_INT >= 26) {
102 | notificationUtils.createNotificationChannel();
103 | notification = notificationUtils.getChannelNotification(title, content, icon, intent).build();
104 | } else {
105 | notification = notificationUtils.getNotification_25(title, content, icon, intent).build();
106 | }
107 | return notification;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/config/RunMode.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.config;
2 |
3 | import android.support.annotation.IntDef;
4 |
5 | import java.lang.annotation.ElementType;
6 | import java.lang.annotation.Retention;
7 | import java.lang.annotation.RetentionPolicy;
8 | import java.lang.annotation.Target;
9 |
10 | public class RunMode {
11 | /**
12 | * 省电模式
13 | * 省电一些,但保活效果会差一点
14 | */
15 | public static final int POWER_SAVING = 0;
16 | /**
17 | * 流氓模式
18 | * 相对耗电,但可造就不死之身
19 | */
20 | public static final int HIGH_POWER_CONSUMPTION = 1;
21 |
22 |
23 | @IntDef(flag = true, value = {POWER_SAVING, HIGH_POWER_CONSUMPTION})
24 | @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD})
25 | @Retention(RetentionPolicy.SOURCE)
26 | public static @interface Model {
27 |
28 | }
29 |
30 | private static @Model
31 | int value = POWER_SAVING;
32 |
33 | public static void setShape(@Model int values) {
34 | value = values;
35 | }
36 |
37 | @Model
38 | public static int getShape() {
39 | return value;
40 | }
41 | }
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/onepx/OnePixelActivity.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.onepx;
2 |
3 | import android.app.Activity;
4 | import android.content.BroadcastReceiver;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.content.IntentFilter;
8 | import android.os.Bundle;
9 | import android.os.PowerManager;
10 | import android.view.Gravity;
11 | import android.view.Window;
12 | import android.view.WindowManager;
13 |
14 | public final class OnePixelActivity extends Activity {
15 | //注册广播接受者 当屏幕开启结果成功结束一像素的activity
16 | BroadcastReceiver br;
17 |
18 | @Override
19 | protected void onCreate(Bundle savedInstanceState) {
20 | super.onCreate(savedInstanceState);
21 | //设定一像素的activity
22 | Window window = getWindow();
23 | window.setGravity(Gravity.START | Gravity.TOP);
24 | WindowManager.LayoutParams params = window.getAttributes();
25 | params.x = 0;
26 | params.y = 0;
27 | params.height = 1;
28 | params.width = 1;
29 | window.setAttributes(params);
30 | //在一像素activity里注册广播接受者 接受到广播结束掉一像素
31 | br = new BroadcastReceiver() {
32 | @Override
33 | public void onReceive(Context context, Intent intent) {
34 | finish();
35 | }
36 | };
37 | registerReceiver(br, new IntentFilter("finish activity"));
38 | checkScreenOn("onCreate");
39 | }
40 |
41 | @Override
42 | protected void onDestroy() {
43 | try {
44 | unregisterReceiver(br);
45 | } catch (IllegalArgumentException e) {
46 | }
47 | super.onDestroy();
48 | }
49 |
50 | @Override
51 | protected void onResume() {
52 | super.onResume();
53 | checkScreenOn("onResume");
54 | }
55 |
56 | private void checkScreenOn(String methodName) {
57 | PowerManager pm = (PowerManager) OnePixelActivity.this.getSystemService(Context.POWER_SERVICE);
58 | boolean isScreenOn = pm.isScreenOn();
59 | if (isScreenOn) {
60 | finish();
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/pro_sp/OpEntry.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.pro_sp;
2 |
3 | import android.os.Bundle;
4 | import android.support.annotation.NonNull;
5 |
6 | import java.util.ArrayList;
7 | import java.util.HashSet;
8 | import java.util.Set;
9 |
10 |
11 | class OpEntry {
12 |
13 | static final int OP_TYPE_GET = 1;
14 |
15 | static final int OP_TYPE_PUT = 2;
16 |
17 | static final int OP_TYPE_CLEAR = 3;
18 |
19 | static final int OP_TYPE_REMOVE = 4;
20 |
21 | static final int OP_TYPE_COMMIT = 5;
22 |
23 | static final int OP_TYPE_APPLY = 6;
24 |
25 |
26 | static final int VALUE_TYPE_STRING = 1;
27 |
28 | static final int VALUE_TYPE_INT = 2;
29 |
30 | static final int VALUE_TYPE_LONG = 3;
31 |
32 | static final int VALUE_TYPE_FLOAT = 4;
33 |
34 | static final int VALUE_TYPE_BOOLEAN = 5;
35 |
36 | static final int VALUE_TYPE_STRING_SET = 6;
37 |
38 |
39 | static final String KEY_KEY = "key_key";
40 |
41 | static final String KEY_VALUE = "key_value";
42 |
43 |
44 | static final String KEY_VALUE_TYPE = "key_value_type";
45 |
46 | static final String KEY_OP_TYPE = "key_op_type";
47 |
48 | @NonNull
49 | private Bundle bundle;
50 |
51 | private OpEntry() {
52 | this.bundle = new Bundle();
53 | }
54 |
55 | public OpEntry(@NonNull Bundle bundle) {
56 | this.bundle = bundle;
57 | }
58 |
59 | public String getKey() {
60 | return bundle.getString(KEY_KEY, null);
61 | }
62 |
63 | public OpEntry setKey(String key) {
64 | bundle.putString(KEY_KEY, key);
65 | return this;
66 | }
67 |
68 | public int getValueType() {
69 | return bundle.getInt(KEY_VALUE_TYPE, 0);
70 | }
71 |
72 | public OpEntry setValueType(int valueType) {
73 | bundle.putInt(KEY_VALUE_TYPE, valueType);
74 | return this;
75 | }
76 |
77 | public int getOpType() {
78 | return bundle.getInt(KEY_OP_TYPE, 0);
79 | }
80 |
81 | public OpEntry setOpType(int opType) {
82 | bundle.putInt(KEY_OP_TYPE, opType);
83 | return this;
84 | }
85 |
86 | public String getStringValue(String defValue) {
87 | return bundle.getString(KEY_VALUE, defValue);
88 | }
89 |
90 | public OpEntry setStringValue(String value) {
91 | bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_STRING);
92 | bundle.putString(KEY_VALUE, value);
93 | return this;
94 | }
95 |
96 | public int getIntValue(int defValue) {
97 | return bundle.getInt(KEY_VALUE, defValue);
98 | }
99 |
100 | public OpEntry setIntValue(int value) {
101 | bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_INT);
102 | bundle.putInt(KEY_VALUE, value);
103 | return this;
104 | }
105 |
106 | public long getLongValue(long defValue) {
107 | return bundle.getLong(KEY_VALUE, defValue);
108 | }
109 |
110 | public OpEntry setLongValue(long value) {
111 | bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_LONG);
112 | bundle.putLong(KEY_VALUE, value);
113 | return this;
114 | }
115 |
116 | public float getFloatValue(float defValue) {
117 | return bundle.getFloat(KEY_VALUE);
118 | }
119 |
120 | public OpEntry setFloatValue(float value) {
121 | bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_FLOAT);
122 | bundle.putFloat(KEY_VALUE, value);
123 | return this;
124 | }
125 |
126 |
127 | public boolean getBooleanValue(boolean defValue) {
128 | return bundle.getBoolean(KEY_VALUE, defValue);
129 | }
130 |
131 | public OpEntry setBooleanValue(boolean value) {
132 | bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_BOOLEAN);
133 | bundle.putBoolean(KEY_VALUE, value);
134 | return this;
135 | }
136 |
137 | public Set getStringSet() {
138 | ArrayList list = bundle.getStringArrayList(KEY_VALUE);
139 | return list == null ? null : new HashSet<>(list);
140 | }
141 |
142 |
143 | public Bundle getBundle() {
144 | return bundle;
145 | }
146 |
147 | public OpEntry setStringSettingsValue(Set value) {
148 | bundle.putInt(KEY_VALUE_TYPE, VALUE_TYPE_STRING_SET);
149 | bundle.putStringArrayList(KEY_VALUE, value == null ? null : new ArrayList<>(value));
150 | return this;
151 | }
152 |
153 |
154 | static OpEntry obtainGetOperation(String key) {
155 | return new OpEntry()
156 | .setKey(key)
157 | .setOpType(OP_TYPE_GET);
158 | }
159 |
160 | static OpEntry obtainPutOperation(String key) {
161 | return new OpEntry()
162 | .setKey(key)
163 | .setOpType(OP_TYPE_PUT);
164 | }
165 |
166 | static OpEntry obtainRemoveOperation(String key) {
167 | return new OpEntry()
168 | .setKey(key)
169 | .setOpType(OP_TYPE_REMOVE);
170 | }
171 |
172 | static OpEntry obtainClear() {
173 | return new OpEntry()
174 | .setOpType(OP_TYPE_CLEAR);
175 | }
176 | }
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/pro_sp/PreferenceUtil.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.pro_sp;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.net.Uri;
6 | import android.support.annotation.NonNull;
7 |
8 |
9 | public class PreferenceUtil {
10 | public static final String METHOD_CONTAIN_KEY = "method_contain_key";
11 | public static final String AUTHORITY = "com.ykun.preference";
12 | public static final Uri URI = Uri.parse("content://" + AUTHORITY);
13 | public static final String METHOD_QUERY_VALUE = "method_query_value";
14 | public static final String METHOD_EIDIT_VALUE = "method_edit";
15 | public static final String METHOD_QUERY_PID = "method_query_pid";
16 | public static final String KEY_VALUES = "key_result";
17 |
18 |
19 | public static final Uri sContentCreate = Uri.withAppendedPath(URI, "create");
20 |
21 | public static final Uri sContentChanged = Uri.withAppendedPath(URI, "changed");
22 |
23 | public static SharedPreferences getSharedPreference(@NonNull Context ctx, String preferName) {
24 | return SharedPreferenceProxy.getSharedPreferences(ctx, preferName);
25 | }
26 | }
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/pro_sp/SharedPreferenceProvider.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.pro_sp;
2 |
3 | import android.content.ContentProvider;
4 | import android.content.ContentValues;
5 | import android.content.Context;
6 | import android.content.SharedPreferences;
7 | import android.database.Cursor;
8 | import android.net.Uri;
9 | import android.os.Bundle;
10 | import android.os.Process;
11 | import android.support.annotation.NonNull;
12 | import android.support.annotation.Nullable;
13 | import android.support.v4.util.ArrayMap;
14 |
15 | import java.util.ArrayList;
16 | import java.util.HashSet;
17 | import java.util.Map;
18 | import java.util.Set;
19 |
20 |
21 | public class SharedPreferenceProvider extends ContentProvider{
22 |
23 | private Map processerMap = new ArrayMap<>();
24 | @Override
25 | public boolean onCreate() {
26 | processerMap.put(PreferenceUtil.METHOD_QUERY_VALUE, methodQueryValues);
27 | processerMap.put(PreferenceUtil.METHOD_CONTAIN_KEY, methodContainKey);
28 | processerMap.put(PreferenceUtil.METHOD_EIDIT_VALUE, methodEditor);
29 | processerMap.put(PreferenceUtil.METHOD_QUERY_PID, methodQueryPid);
30 | return true;
31 | }
32 |
33 | @Nullable
34 | @Override
35 | public Cursor query(@NonNull Uri uri, @Nullable String[] projection, @Nullable String selection, @Nullable String[] selectionArgs, @Nullable String sortOrder) {
36 | throw new UnsupportedOperationException();
37 | }
38 |
39 | @Nullable
40 | @Override
41 | public String getType(@NonNull Uri uri) {
42 | throw new UnsupportedOperationException();
43 | }
44 |
45 | @Nullable
46 | @Override
47 | public Uri insert(@NonNull Uri uri, @Nullable ContentValues values) {
48 | throw new UnsupportedOperationException();
49 | }
50 |
51 | @Override
52 | public int delete(@NonNull Uri uri, @Nullable String selection, @Nullable String[] selectionArgs) {
53 | throw new UnsupportedOperationException();
54 | }
55 |
56 | @Override
57 | public int update(@NonNull Uri uri, @Nullable ContentValues values, @Nullable String selection, @Nullable String[] selectionArgs) {
58 | throw new UnsupportedOperationException();
59 | }
60 |
61 | @Nullable
62 | @Override
63 | public Bundle call(@NonNull String method, @Nullable String arg, @Nullable Bundle extras) {
64 | MethodProcess processer = processerMap.get(method);
65 | return processer == null?null:processer.process(arg, extras);
66 | }
67 |
68 |
69 | public interface MethodProcess {
70 | Bundle process(@Nullable String arg, @Nullable Bundle extras);
71 | }
72 |
73 | private MethodProcess methodQueryPid = new MethodProcess() {
74 | @Override
75 | public Bundle process(@Nullable String arg, @Nullable Bundle extras) {
76 | Bundle bundle = new Bundle();
77 | bundle.putInt(PreferenceUtil.KEY_VALUES, Process.myPid());
78 | return bundle;
79 | }
80 | };
81 |
82 | private MethodProcess methodQueryValues = new MethodProcess() {
83 | @Override
84 | public Bundle process(@Nullable String arg, @Nullable Bundle extras) {
85 | if (extras == null) {
86 | throw new IllegalArgumentException("methodQueryValues, extras is null!");
87 | }
88 | Context ctx = getContext();
89 | if (ctx == null) {
90 | throw new IllegalArgumentException("methodQueryValues, ctx is null!");
91 | }
92 | String key = extras.getString(OpEntry.KEY_KEY);
93 | SharedPreferences preferences = ctx.getSharedPreferences(arg, Context.MODE_PRIVATE);
94 | int valueType = extras.getInt(OpEntry.KEY_VALUE_TYPE);
95 | switch (valueType) {
96 | case OpEntry.VALUE_TYPE_BOOLEAN:{
97 | boolean value = preferences.getBoolean(key, extras.getBoolean(OpEntry.KEY_VALUE));
98 | extras.putBoolean(OpEntry.KEY_VALUE, value);
99 | return extras;
100 | }
101 | case OpEntry.VALUE_TYPE_FLOAT:{
102 | float value = preferences.getFloat(key, extras.getFloat(OpEntry.KEY_VALUE));
103 | extras.putFloat(OpEntry.KEY_VALUE, value);
104 | return extras;
105 | }
106 | case OpEntry.VALUE_TYPE_INT:{
107 | int value = preferences.getInt(key, extras.getInt(OpEntry.KEY_VALUE));
108 | extras.putInt(OpEntry.KEY_VALUE, value);
109 | return extras;
110 | }
111 | case OpEntry.VALUE_TYPE_LONG:{
112 | long value = preferences.getLong(key, extras.getLong(OpEntry.KEY_VALUE));
113 | extras.putLong(OpEntry.KEY_VALUE, value);
114 | return extras;
115 | }
116 | case OpEntry.VALUE_TYPE_STRING:{
117 | String value = preferences.getString(key, extras.getString(OpEntry.KEY_VALUE));
118 | extras.putString(OpEntry.KEY_VALUE, value);
119 | return extras;
120 | }
121 | case OpEntry.VALUE_TYPE_STRING_SET:{
122 | Set value = preferences.getStringSet(key, null);
123 | extras.putStringArrayList(OpEntry.KEY_VALUE, value == null?null:new ArrayList<>(value));
124 | return extras;
125 | }
126 | default:{
127 | throw new IllegalArgumentException("unknown valueType:" + valueType);
128 | }
129 | }
130 | }
131 | };
132 |
133 | private MethodProcess methodContainKey = new MethodProcess() {
134 | @Override
135 | public Bundle process(@Nullable String arg, @Nullable Bundle extras) {
136 | if (extras == null) {
137 | throw new IllegalArgumentException("methodQueryValues, extras is null!");
138 | }
139 | Context ctx = getContext();
140 | if (ctx == null) {
141 | throw new IllegalArgumentException("methodQueryValues, ctx is null!");
142 | }
143 | String key = extras.getString(OpEntry.KEY_KEY);
144 | SharedPreferences preferences = ctx.getSharedPreferences(arg, Context.MODE_PRIVATE);
145 | extras.putBoolean(PreferenceUtil.KEY_VALUES, preferences.contains(key));
146 | return extras;
147 | }
148 | };
149 |
150 | private MethodProcess methodEditor = new MethodProcess() {
151 | @Override
152 | public Bundle process(@Nullable String arg, @Nullable Bundle extras) {
153 | if (extras == null) {
154 | throw new IllegalArgumentException("methodQueryValues, extras is null!");
155 | }
156 | Context ctx = getContext();
157 | if (ctx == null) {
158 | throw new IllegalArgumentException("methodQueryValues, ctx is null!");
159 | }
160 | SharedPreferences preferences = ctx.getSharedPreferences(arg, Context.MODE_PRIVATE);
161 | ArrayList ops = extras.getParcelableArrayList(PreferenceUtil.KEY_VALUES);
162 | if (ops == null) {
163 | ops = new ArrayList<>();
164 | }
165 | SharedPreferences.Editor editor = preferences.edit();
166 | for (Bundle opBundler : ops) {
167 | int opType = opBundler.getInt(OpEntry.KEY_OP_TYPE);
168 | switch (opType) {
169 | case OpEntry.OP_TYPE_PUT: {
170 | editor = editValue(editor, opBundler);
171 | break;
172 | }
173 | case OpEntry.OP_TYPE_REMOVE: {
174 | editor = editor.remove(opBundler.getString(OpEntry.KEY_KEY));
175 | break;
176 | }
177 | case OpEntry.OP_TYPE_CLEAR: {
178 | editor = editor.clear();
179 | break;
180 | }
181 | default: {
182 | throw new IllegalArgumentException("unkonw op type:" + opType);
183 | }
184 | }
185 | }
186 |
187 | int applyOrCommit = extras.getInt(OpEntry.KEY_OP_TYPE);
188 | if (applyOrCommit == OpEntry.OP_TYPE_APPLY) {
189 | editor.apply();
190 | return null;
191 | } else if (applyOrCommit == OpEntry.OP_TYPE_COMMIT) {
192 | boolean res = editor.commit();
193 | Bundle bundle = new Bundle();
194 | bundle.putBoolean(PreferenceUtil.KEY_VALUES, res);
195 | return bundle;
196 | } else {
197 | throw new IllegalArgumentException("unknown applyOrCommit:" + applyOrCommit);
198 | }
199 | }
200 |
201 |
202 | private SharedPreferences.Editor editValue(SharedPreferences.Editor editor, Bundle opBundle) {
203 | String key = opBundle.getString(OpEntry.KEY_KEY);
204 | int valueType = opBundle.getInt(OpEntry.KEY_VALUE_TYPE);
205 | switch (valueType) {
206 | case OpEntry.VALUE_TYPE_BOOLEAN: {
207 | return editor.putBoolean(key, opBundle.getBoolean(OpEntry.KEY_VALUE));
208 | }
209 | case OpEntry.VALUE_TYPE_FLOAT: {
210 | return editor.putFloat(key, opBundle.getFloat(OpEntry.KEY_VALUE));
211 | }
212 | case OpEntry.VALUE_TYPE_INT: {
213 | return editor.putInt(key, opBundle.getInt(OpEntry.KEY_VALUE));
214 | }
215 | case OpEntry.VALUE_TYPE_LONG: {
216 | return editor.putLong(key, opBundle.getLong(OpEntry.KEY_VALUE));
217 | }
218 | case OpEntry.VALUE_TYPE_STRING: {
219 | return editor.putString(key, opBundle.getString(OpEntry.KEY_VALUE));
220 | }
221 | case OpEntry.VALUE_TYPE_STRING_SET: {
222 | ArrayList list = opBundle.getStringArrayList(OpEntry.KEY_VALUE);
223 | if (list == null) {
224 | return editor.putStringSet(key, null);
225 | }
226 | return editor.putStringSet(key, new HashSet<>(list));
227 | }
228 | default: {
229 | throw new IllegalArgumentException("unknown valueType:" + valueType);
230 | }
231 | }
232 | }
233 | };
234 | }
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/pro_sp/SharedPreferenceProxy.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.pro_sp;
2 |
3 | import android.content.Context;
4 | import android.content.SharedPreferences;
5 | import android.os.Bundle;
6 | import android.os.Process;
7 | import android.support.annotation.NonNull;
8 | import android.support.annotation.Nullable;
9 | import android.support.v4.util.ArrayMap;
10 |
11 | import java.util.ArrayList;
12 | import java.util.Map;
13 | import java.util.Set;
14 | import java.util.concurrent.atomic.AtomicInteger;
15 |
16 | public class SharedPreferenceProxy implements SharedPreferences {
17 | private static Map sharedPreferenceProxyMap;
18 |
19 | /**
20 | * Flag whether caller process is the same with provider
21 | * 0: unknown
22 | * 1: the same
23 | * -1: not the same
24 | */
25 | private static AtomicInteger processFlag = new AtomicInteger(0);
26 |
27 | private Context ctx;
28 | private String preferName;
29 |
30 | private SharedPreferenceProxy(Context ctx, String name) {
31 | this.ctx = ctx.getApplicationContext();
32 | this.preferName = name;
33 | }
34 |
35 | @Override
36 | public Map getAll() {
37 | throw new UnsupportedOperationException("Not support method getAll");
38 | }
39 |
40 | @Nullable
41 | @Override
42 | public String getString(String key, @Nullable String defValue) {
43 | OpEntry result = getResult(OpEntry.obtainGetOperation(key).setStringValue(defValue));
44 | return result == null ? defValue : result.getStringValue(defValue);
45 | }
46 |
47 | @Nullable
48 | @Override
49 | public Set getStringSet(String key, @Nullable Set defValues) {
50 | OpEntry result = getResult(OpEntry.obtainGetOperation(key).setStringSettingsValue(defValues));
51 | if (result == null) {
52 | return defValues;
53 | }
54 | Set set = result.getStringSet();
55 | if (set == null) {
56 | return defValues;
57 | }
58 | return set;
59 | }
60 |
61 | @Override
62 | public int getInt(String key, int defValue) {
63 | OpEntry result = getResult(OpEntry.obtainGetOperation(key).setIntValue(defValue));
64 | return result == null ? defValue : result.getIntValue(defValue);
65 | }
66 |
67 | @Override
68 | public long getLong(String key, long defValue) {
69 | OpEntry result = getResult(OpEntry.obtainGetOperation(key).setLongValue(defValue));
70 | return result == null ? defValue : result.getLongValue(defValue);
71 | }
72 |
73 | @Override
74 | public float getFloat(String key, float defValue) {
75 | OpEntry result = getResult(OpEntry.obtainGetOperation(key).setFloatValue(defValue));
76 | return result == null ? defValue : result.getFloatValue(defValue);
77 | }
78 |
79 | @Override
80 | public boolean getBoolean(String key, boolean defValue) {
81 | OpEntry result = getResult(OpEntry.obtainGetOperation(key).setBooleanValue(defValue));
82 | return result == null ? defValue : result.getBooleanValue(defValue);
83 | }
84 |
85 | @Override
86 | public boolean contains(String key) {
87 | Bundle input = new Bundle();
88 | input.putString(OpEntry.KEY_KEY, key);
89 | try {
90 | Bundle res = ctx.getContentResolver().call(PreferenceUtil.URI
91 | , PreferenceUtil.METHOD_CONTAIN_KEY
92 | , preferName
93 | , input);
94 | return res.getBoolean(PreferenceUtil.KEY_VALUES);
95 | } catch (Exception e) {
96 | e.printStackTrace();
97 | return false;
98 | }
99 | }
100 |
101 | @Override
102 | public Editor edit() {
103 | return new EditorImpl();
104 | }
105 |
106 | @Override
107 | public void registerOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
108 | throw new UnsupportedOperationException();
109 | }
110 |
111 | @Override
112 | public void unregisterOnSharedPreferenceChangeListener(OnSharedPreferenceChangeListener listener) {
113 | throw new UnsupportedOperationException();
114 | }
115 |
116 | private OpEntry getResult(@NonNull OpEntry input) {
117 | try {
118 | Bundle res = ctx.getContentResolver().call(PreferenceUtil.URI
119 | , PreferenceUtil.METHOD_QUERY_VALUE
120 | , preferName
121 | , input.getBundle());
122 | return new OpEntry(res);
123 | } catch (Exception e) {
124 | e.printStackTrace();
125 | return null;
126 | }
127 | }
128 |
129 | public class EditorImpl implements Editor {
130 | private ArrayList mModified = new ArrayList<>();
131 |
132 | @Override
133 | public Editor putString(String key, @Nullable String value) {
134 | OpEntry entry = OpEntry.obtainPutOperation(key).setStringValue(value);
135 | return addOps(entry);
136 | }
137 |
138 | @Override
139 | public Editor putStringSet(String key, @Nullable Set values) {
140 | OpEntry entry = OpEntry.obtainPutOperation(key).setStringSettingsValue(values);
141 | return addOps(entry);
142 | }
143 |
144 | @Override
145 | public Editor putInt(String key, int value) {
146 | OpEntry entry = OpEntry.obtainPutOperation(key).setIntValue(value);
147 | return addOps(entry);
148 | }
149 |
150 | @Override
151 | public Editor putLong(String key, long value) {
152 | OpEntry entry = OpEntry.obtainPutOperation(key).setLongValue(value);
153 | return addOps(entry);
154 | }
155 |
156 | @Override
157 | public Editor putFloat(String key, float value) {
158 | OpEntry entry = OpEntry.obtainPutOperation(key).setFloatValue(value);
159 | return addOps(entry);
160 | }
161 |
162 | @Override
163 | public Editor putBoolean(String key, boolean value) {
164 | OpEntry entry = OpEntry.obtainPutOperation(key).setBooleanValue(value);
165 | return addOps(entry);
166 | }
167 |
168 | @Override
169 | public Editor remove(String key) {
170 | OpEntry entry = OpEntry.obtainRemoveOperation(key);
171 | return addOps(entry);
172 | }
173 |
174 | @Override
175 | public Editor clear() {
176 | return addOps(OpEntry.obtainClear());
177 | }
178 |
179 | @Override
180 | public boolean commit() {
181 | Bundle input = new Bundle();
182 | input.putParcelableArrayList(PreferenceUtil.KEY_VALUES, convertBundleList());
183 | input.putInt(OpEntry.KEY_OP_TYPE, OpEntry.OP_TYPE_COMMIT);
184 | Bundle res = null;
185 | try {
186 | res = ctx.getContentResolver().call(PreferenceUtil.URI, PreferenceUtil.METHOD_EIDIT_VALUE, preferName, input);
187 | } catch (Exception e) {
188 | e.printStackTrace();
189 | }
190 | if (res == null) {
191 | return false;
192 | }
193 | return res.getBoolean(PreferenceUtil.KEY_VALUES, false);
194 | }
195 |
196 | @Override
197 | public void apply() {
198 | Bundle intput = new Bundle();
199 | intput.putParcelableArrayList(PreferenceUtil.KEY_VALUES, convertBundleList());
200 | intput.putInt(OpEntry.KEY_OP_TYPE, OpEntry.OP_TYPE_APPLY);
201 | try {
202 | ctx.getContentResolver().call(PreferenceUtil.URI, PreferenceUtil.METHOD_EIDIT_VALUE, preferName, intput);
203 | } catch (Exception e) {
204 | e.printStackTrace();
205 | }
206 | }
207 |
208 | private Editor addOps(OpEntry op) {
209 | synchronized (this) {
210 | mModified.add(op);
211 | return this;
212 | }
213 | }
214 |
215 | private ArrayList convertBundleList() {
216 | synchronized (this) {
217 | ArrayList bundleList = new ArrayList<>(mModified.size());
218 | for (OpEntry entry : mModified) {
219 | bundleList.add(entry.getBundle());
220 | }
221 | return bundleList;
222 | }
223 | }
224 | }
225 |
226 |
227 |
228 |
229 |
230 | public static SharedPreferences getSharedPreferences(@NonNull Context ctx, String preferName) {
231 | //First check if the same process
232 | if (processFlag.get() == 0) {
233 | Bundle bundle = ctx.getContentResolver().call(PreferenceUtil.URI, PreferenceUtil.METHOD_QUERY_PID, "", null);
234 | int pid = 0;
235 | if (bundle != null) {
236 | pid = bundle.getInt(PreferenceUtil.KEY_VALUES);
237 | }
238 | //Can not get the pid, something wrong!
239 | if (pid == 0) {
240 | return getFromLocalProcess(ctx, preferName);
241 | }
242 | processFlag.set(Process.myPid() == pid ? 1 : -1);
243 | return getSharedPreferences(ctx, preferName);
244 | } else if (processFlag.get() > 0) {
245 | return getFromLocalProcess(ctx, preferName);
246 | } else {
247 | return getFromRemoteProcess(ctx, preferName);
248 | }
249 | }
250 |
251 |
252 | private static SharedPreferences getFromRemoteProcess(@NonNull Context ctx, String preferName) {
253 | synchronized (SharedPreferenceProxy.class) {
254 | if (sharedPreferenceProxyMap == null) {
255 | sharedPreferenceProxyMap = new ArrayMap<>();
256 | }
257 | SharedPreferenceProxy preferenceProxy = sharedPreferenceProxyMap.get(preferName);
258 | if (preferenceProxy == null) {
259 | preferenceProxy = new SharedPreferenceProxy(ctx.getApplicationContext(), preferName);
260 | sharedPreferenceProxyMap.put(preferName, preferenceProxy);
261 | }
262 | return preferenceProxy;
263 | }
264 | }
265 |
266 | private static SharedPreferences getFromLocalProcess(@NonNull Context ctx, String preferName) {
267 | return ctx.getSharedPreferences(preferName, Context.MODE_PRIVATE);
268 | }
269 | }
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/receive/NotificationClickReceiver.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.receive;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 |
7 | import com.ykun.live_library.config.KeepAliveConfig;
8 |
9 |
10 | public final class NotificationClickReceiver extends BroadcastReceiver {
11 | public final static String CLICK_NOTIFICATION = "CLICK_NOTIFICATION";
12 |
13 | @Override
14 | public void onReceive(Context context, Intent intent) {
15 | if (intent.getAction().equals(NotificationClickReceiver.CLICK_NOTIFICATION)) {
16 | if (KeepAliveConfig.foregroundNotification != null) {
17 | if (KeepAliveConfig.foregroundNotification.getForegroundNotificationClickListener() != null) {
18 | KeepAliveConfig.foregroundNotification.getForegroundNotificationClickListener().foregroundNotificationClick(context, intent);
19 | }
20 | }
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/receive/OnepxReceiver.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.receive;
2 |
3 | import android.content.BroadcastReceiver;
4 | import android.content.Context;
5 | import android.content.Intent;
6 | import android.os.Looper;
7 |
8 | import com.ykun.live_library.onepx.OnePixelActivity;
9 | import com.ykun.live_library.utils.KeepAliveUtils;
10 |
11 | @SuppressWarnings(value = {"unchecked", "deprecation"})
12 | public final class OnepxReceiver extends BroadcastReceiver {
13 | android.os.Handler mHander;
14 | boolean appIsForeground = false;
15 |
16 | public OnepxReceiver() {
17 | mHander = new android.os.Handler(Looper.getMainLooper());
18 | }
19 |
20 | @Override
21 | public void onReceive(final Context context, Intent intent) {
22 | if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) { //屏幕关闭的时候接受到广播
23 | appIsForeground = KeepAliveUtils.IsForeground(context);
24 | try {
25 | Intent it = new Intent(context, OnePixelActivity.class);
26 | it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
27 | it.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
28 | context.startActivity(it);
29 | } catch (Exception e) {
30 | e.printStackTrace();
31 | }
32 | //通知屏幕已关闭,开始播放无声音乐
33 | context.sendBroadcast(new Intent("_ACTION_SCREEN_OFF"));
34 | } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) { //屏幕打开的时候发送广播 结束一像素
35 | context.sendBroadcast(new Intent("finish activity"));
36 | if (!appIsForeground) {
37 | appIsForeground = false;
38 | try {
39 | Intent home = new Intent(Intent.ACTION_MAIN);
40 | home.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
41 | home.addCategory(Intent.CATEGORY_HOME);
42 | context.getApplicationContext().startActivity(home);
43 | } catch (Exception e) {
44 | e.printStackTrace();
45 | }
46 | }
47 | //通知屏幕已点亮,停止播放无声音乐
48 | context.sendBroadcast(new Intent("_ACTION_SCREEN_ON"));
49 | }
50 | }
51 |
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/service/HideForegroundService.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.service;
2 |
3 | import android.app.Notification;
4 | import android.app.Service;
5 | import android.content.Intent;
6 | import android.os.Handler;
7 | import android.os.IBinder;
8 |
9 | import com.ykun.live_library.config.KeepAliveConfig;
10 | import com.ykun.live_library.config.NotificationUtils;
11 | import com.ykun.live_library.receive.NotificationClickReceiver;
12 | import com.ykun.live_library.utils.SPUtils;
13 |
14 | import static com.ykun.live_library.config.KeepAliveConfig.SP_NAME;
15 |
16 |
17 | /**
18 | * 隐藏前台服务通知
19 | */
20 | public class HideForegroundService extends Service {
21 | private Handler handler;
22 |
23 | @Override
24 | public int onStartCommand(Intent intent, int flags, int startId) {
25 | startForeground();
26 | if (handler == null) {
27 | handler = new Handler();
28 | }
29 | handler.postDelayed(new Runnable() {
30 | @Override
31 | public void run() {
32 | stopForeground(true);
33 | stopSelf();
34 | }
35 | }, 2000);
36 | return START_NOT_STICKY;
37 | }
38 |
39 |
40 | private void startForeground() {
41 | if (KeepAliveConfig.foregroundNotification != null) {
42 | Intent intent = new Intent(getApplicationContext(), NotificationClickReceiver.class);
43 | intent.setAction(NotificationClickReceiver.CLICK_NOTIFICATION);
44 | Notification notification = NotificationUtils.createNotification(this,
45 | SPUtils.getInstance(getApplicationContext(),SP_NAME).getString(KeepAliveConfig.TITLE),
46 | SPUtils.getInstance(getApplicationContext(),SP_NAME).getString(KeepAliveConfig.CONTENT),
47 | SPUtils.getInstance(getApplicationContext(),SP_NAME).getInt(KeepAliveConfig.RES_ICON),
48 | intent
49 | );
50 | startForeground(KeepAliveConfig.FOREGROUD_NOTIFICATION_ID, notification);
51 | }
52 | }
53 |
54 | @Override
55 | public void onDestroy() {
56 | super.onDestroy();
57 | if (handler != null) {
58 | handler.removeCallbacksAndMessages(null);
59 | handler = null;
60 | }
61 | }
62 |
63 | @Override
64 | public IBinder onBind(Intent intent) {
65 | return null;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/service/JobHandlerService.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.service;
2 |
3 | import android.app.job.JobInfo;
4 | import android.app.job.JobParameters;
5 | import android.app.job.JobScheduler;
6 | import android.app.job.JobService;
7 | import android.content.ComponentName;
8 | import android.content.Context;
9 | import android.content.Intent;
10 | import android.os.Build;
11 | import android.support.annotation.RequiresApi;
12 | import android.util.Log;
13 |
14 | import com.ykun.live_library.config.KeepAliveConfig;
15 | import com.ykun.live_library.utils.KeepAliveUtils;
16 |
17 | /**
18 | * 定时器
19 | * 安卓5.0及以上
20 | */
21 | @SuppressWarnings(value = {"unchecked", "deprecation"})
22 | @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
23 | public final class JobHandlerService extends JobService {
24 | private String TAG = this.getClass().getSimpleName();
25 | private static JobScheduler mJobScheduler;
26 |
27 | private static int EXECUTE_COUNT = 0;
28 |
29 | public static void startJob(Context context) {
30 | try {
31 | mJobScheduler = (JobScheduler) context.getSystemService(
32 | Context.JOB_SCHEDULER_SERVICE);
33 | JobInfo.Builder builder = new JobInfo.Builder(10,
34 | new ComponentName(context.getPackageName(),
35 | JobHandlerService.class.getName())).setPersisted(true);
36 | /**
37 | * I was having this problem and after review some blogs and the official documentation,
38 | * I realised that JobScheduler is having difference behavior on Android N(24 and 25).
39 | * JobScheduler works with a minimum periodic of 15 mins.
40 | *
41 | */
42 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
43 | //7.0以上延迟1s执行
44 | builder.setMinimumLatency(KeepAliveConfig.JOB_TIME);
45 | } else {
46 | //每隔1s执行一次job
47 | builder.setPeriodic(KeepAliveConfig.JOB_TIME);
48 | }
49 | mJobScheduler.schedule(builder.build());
50 |
51 | } catch (Exception e) {
52 | Log.e("startJob->", e.getMessage());
53 | }
54 | }
55 |
56 | public static void stopJob() {
57 | if (mJobScheduler != null)
58 | mJobScheduler.cancelAll();
59 |
60 |
61 | }
62 |
63 | private void startService(Context context) {
64 | try {
65 | Log.i(TAG, "---》启动双进程保活服务");
66 | //启动本地服务
67 | Intent localIntent = new Intent(context, LocalService.class);
68 | //启动守护进程
69 | Intent guardIntent = new Intent(context, RemoteService.class);
70 | if (Build.VERSION.SDK_INT >= 26) {
71 | startForegroundService(localIntent);
72 | startForegroundService(guardIntent);
73 | } else {
74 | startService(localIntent);
75 | startService(guardIntent);
76 | }
77 | } catch (Exception e) {
78 | Log.e(TAG, e.getMessage());
79 | }
80 | }
81 |
82 | @Override
83 | public boolean onStartJob(JobParameters jobParameters) {
84 | try {
85 | ++EXECUTE_COUNT;
86 | Log.d("JOB-->", " Job 执行 " + EXECUTE_COUNT);
87 | //7.0以上轮询
88 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
89 | startJob(this);
90 | }
91 | if (!KeepAliveUtils.isServiceRunning(getApplicationContext(), getPackageName() + ":local") || !KeepAliveUtils.isRunningTaskExist(getApplicationContext(), getPackageName() + ":remote")) {
92 | Log.d("JOB-->", " 重新开启了 服务 ");
93 | startService(this);
94 | }
95 | } catch (Exception e) {
96 | Log.e(TAG, e.getMessage());
97 | }
98 | return false;
99 | }
100 |
101 | @Override
102 | public boolean onStopJob(JobParameters jobParameters) {
103 | Log.d("JOB-->", " Job 停止");
104 | if (!KeepAliveUtils.isServiceRunning(getApplicationContext(), getPackageName() + ":local") || !KeepAliveUtils.isRunningTaskExist(getApplicationContext(), getPackageName() + ":remote")) {
105 | startService(this);
106 | }
107 | return false;
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/service/LocalService.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.service;
2 |
3 | import android.app.Notification;
4 | import android.app.Service;
5 | import android.content.BroadcastReceiver;
6 | import android.content.ComponentName;
7 | import android.content.Context;
8 | import android.content.Intent;
9 | import android.content.IntentFilter;
10 | import android.content.ServiceConnection;
11 | import android.media.MediaPlayer;
12 | import android.os.Build;
13 | import android.os.Handler;
14 | import android.os.IBinder;
15 | import android.os.PowerManager;
16 | import android.os.RemoteException;
17 | import android.text.TextUtils;
18 | import android.util.Log;
19 |
20 | import com.ykun.keeplive.KeepAliveAidl;
21 | import com.ykun.live_library.KeepAliveRuning;
22 | import com.ykun.live_library.R;
23 | import com.ykun.live_library.config.KeepAliveConfig;
24 | import com.ykun.live_library.config.NotificationUtils;
25 | import com.ykun.live_library.config.RunMode;
26 | import com.ykun.live_library.receive.NotificationClickReceiver;
27 | import com.ykun.live_library.receive.OnepxReceiver;
28 | import com.ykun.live_library.utils.SPUtils;
29 |
30 | import static com.ykun.live_library.config.KeepAliveConfig.SP_NAME;
31 |
32 |
33 | public final class LocalService extends Service {
34 | private OnepxReceiver mOnepxReceiver;
35 | private ScreenStateReceiver screenStateReceiver;
36 | private boolean isPause = true;//控制暂停
37 | private MediaPlayer mediaPlayer;
38 | private LocalBinder mBilder;
39 | private Handler handler;
40 | private String TAG = getClass().getSimpleName();
41 | private KeepAliveRuning mKeepAliveRuning;
42 |
43 | @Override
44 | public void onCreate() {
45 | super.onCreate();
46 | Log.i("本地服务", ":本地服务启动成功");
47 | if (mBilder == null) {
48 | mBilder = new LocalBinder();
49 | }
50 | PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
51 | isPause = pm.isScreenOn();
52 | if (handler == null) {
53 | handler = new Handler();
54 | }
55 | }
56 |
57 | @Override
58 | public IBinder onBind(Intent intent) {
59 | return mBilder;
60 | }
61 |
62 | @Override
63 | public int onStartCommand(Intent intent, int flags, int startId) {
64 | //播放无声音乐
65 | KeepAliveConfig.runMode = SPUtils.getInstance(getApplicationContext(), SP_NAME).getInt(KeepAliveConfig.RUN_MODE);
66 | Log.d(TAG, "运行模式:" + KeepAliveConfig.runMode);
67 | if (mediaPlayer == null && KeepAliveConfig.runMode == RunMode.HIGH_POWER_CONSUMPTION) {
68 | mediaPlayer = MediaPlayer.create(this, R.raw.novioce);
69 | mediaPlayer.setVolume(0f, 0f);
70 | mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
71 | @Override
72 | public void onCompletion(MediaPlayer mediaPlayer) {
73 | Log.i(TAG, "循环播放音乐");
74 | play();
75 | }
76 | });
77 | play();
78 | }
79 | //像素保活
80 | if (mOnepxReceiver == null) {
81 | mOnepxReceiver = new OnepxReceiver();
82 | }
83 | IntentFilter intentFilter = new IntentFilter();
84 | intentFilter.addAction("android.intent.action.SCREEN_OFF");
85 | intentFilter.addAction("android.intent.action.SCREEN_ON");
86 | registerReceiver(mOnepxReceiver, intentFilter);
87 | //屏幕点亮状态监听,用于单独控制音乐播放
88 | if (screenStateReceiver == null) {
89 | screenStateReceiver = new ScreenStateReceiver();
90 | }
91 | IntentFilter intentFilter2 = new IntentFilter();
92 | intentFilter2.addAction("_ACTION_SCREEN_OFF");
93 | intentFilter2.addAction("_ACTION_SCREEN_ON");
94 | registerReceiver(screenStateReceiver, intentFilter2);
95 |
96 | //开启一个前台通知,用于提升服务进程优先级
97 | shouDefNotify();
98 | //绑定守护进程
99 | try {
100 | Intent intent3 = new Intent(this, RemoteService.class);
101 | this.bindService(intent3, connection, Context.BIND_ABOVE_CLIENT);
102 | } catch (Exception e) {
103 | Log.i("RemoteService--", e.getMessage());
104 | }
105 | //隐藏服务通知
106 | try {
107 | if (Build.VERSION.SDK_INT < 25) {
108 | startService(new Intent(this, HideForegroundService.class));
109 | }
110 | } catch (Exception e) {
111 | Log.i("HideForegroundService--", e.getMessage());
112 |
113 | }
114 | if (mKeepAliveRuning == null)
115 | mKeepAliveRuning = new KeepAliveRuning();
116 | mKeepAliveRuning.onRuning();
117 | return START_STICKY;
118 | }
119 |
120 | private void play() {
121 | Log.i(TAG, "播放音乐");
122 | if (mediaPlayer != null && !mediaPlayer.isPlaying()) {
123 | mediaPlayer.start();
124 | }
125 | }
126 |
127 | private void pause() {
128 | if (mediaPlayer != null && mediaPlayer.isPlaying()) {
129 | mediaPlayer.pause();
130 | }
131 | }
132 |
133 | private class ScreenStateReceiver extends BroadcastReceiver {
134 | @Override
135 | public void onReceive(final Context context, Intent intent) {
136 | if (intent.getAction().equals("_ACTION_SCREEN_OFF")) {
137 | isPause = false;
138 | play();
139 | } else if (intent.getAction().equals("_ACTION_SCREEN_ON")) {
140 | isPause = true;
141 | pause();
142 | }
143 | }
144 | }
145 |
146 | private final class LocalBinder extends KeepAliveAidl.Stub {
147 | @Override
148 | public void wakeUp(String title, String discription, int iconRes) throws RemoteException {
149 | shouDefNotify();
150 | }
151 | }
152 |
153 | private ServiceConnection connection = new ServiceConnection() {
154 |
155 | @Override
156 | public void onServiceDisconnected(ComponentName name) {
157 | Intent remoteService = new Intent(LocalService.this,
158 | RemoteService.class);
159 | if (Build.VERSION.SDK_INT >= 26) {
160 | LocalService.this.startForegroundService(remoteService);
161 | } else {
162 | LocalService.this.startService(remoteService);
163 | }
164 | Intent intent = new Intent(LocalService.this, RemoteService.class);
165 | LocalService.this.bindService(intent, connection,
166 | Context.BIND_ABOVE_CLIENT);
167 | PowerManager pm = (PowerManager) LocalService.this.getSystemService(Context.POWER_SERVICE);
168 | boolean isScreenOn = pm.isScreenOn();
169 | if (isScreenOn) {
170 | sendBroadcast(new Intent("_ACTION_SCREEN_ON"));
171 | } else {
172 | sendBroadcast(new Intent("_ACTION_SCREEN_OFF"));
173 | }
174 | }
175 |
176 | @Override
177 | public void onServiceConnected(ComponentName name, IBinder service) {
178 | try {
179 | if (mBilder != null && KeepAliveConfig.foregroundNotification != null) {
180 | KeepAliveAidl guardAidl = KeepAliveAidl.Stub.asInterface(service);
181 | guardAidl.wakeUp(SPUtils.getInstance(getApplicationContext(), SP_NAME).getString(KeepAliveConfig.TITLE),
182 | SPUtils.getInstance(getApplicationContext(), SP_NAME).getString(KeepAliveConfig.CONTENT),
183 | SPUtils.getInstance(getApplicationContext(), SP_NAME).getInt(KeepAliveConfig.RES_ICON));
184 | }
185 | } catch (RemoteException e) {
186 | e.printStackTrace();
187 | }
188 | }
189 | };
190 |
191 | @Override
192 | public void onDestroy() {
193 | super.onDestroy();
194 | unbindService(connection);
195 | unregisterReceiver(mOnepxReceiver);
196 | unregisterReceiver(screenStateReceiver);
197 |
198 | if (mKeepAliveRuning != null) {
199 | mKeepAliveRuning.onStop();
200 | }
201 |
202 | if (mediaPlayer != null && mediaPlayer.isPlaying()) {
203 | mediaPlayer.stop();
204 | mediaPlayer = null;
205 | }
206 | }
207 |
208 | private void shouDefNotify() {
209 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
210 | KeepAliveConfig.CONTENT = SPUtils.getInstance(getApplicationContext(), SP_NAME).getString(KeepAliveConfig.CONTENT);
211 | KeepAliveConfig.DEF_ICONS = SPUtils.getInstance(getApplicationContext(), SP_NAME).getInt(KeepAliveConfig.RES_ICON, R.drawable.ic_launcher);
212 | KeepAliveConfig.TITLE = SPUtils.getInstance(getApplicationContext(), SP_NAME).getString(KeepAliveConfig.TITLE);
213 | String title = SPUtils.getInstance(getApplicationContext(), SP_NAME).getString(KeepAliveConfig.TITLE);
214 | Log.d("JOB-->" + TAG, "KeepAliveConfig.CONTENT_" + KeepAliveConfig.CONTENT + " " + KeepAliveConfig.TITLE + " " + title);
215 | if (!TextUtils.isEmpty(KeepAliveConfig.TITLE) && !TextUtils.isEmpty(KeepAliveConfig.CONTENT)) {
216 | //启用前台服务,提升优先级
217 | Intent intent2 = new Intent(getApplicationContext(), NotificationClickReceiver.class);
218 | intent2.setAction(NotificationClickReceiver.CLICK_NOTIFICATION);
219 | Notification notification = NotificationUtils.createNotification(LocalService.this, KeepAliveConfig.TITLE, KeepAliveConfig.CONTENT, KeepAliveConfig.DEF_ICONS, intent2);
220 | startForeground(KeepAliveConfig.FOREGROUD_NOTIFICATION_ID, notification);
221 | Log.d("JOB-->", TAG + "显示通知栏");
222 | }
223 | }
224 | }
225 | }
226 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/service/RemoteService.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.service;
2 |
3 | import android.app.Notification;
4 | import android.app.Service;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.Intent;
8 | import android.content.ServiceConnection;
9 | import android.os.Build;
10 | import android.os.IBinder;
11 | import android.os.PowerManager;
12 | import android.os.RemoteException;
13 | import android.text.TextUtils;
14 | import android.util.Log;
15 |
16 | import com.ykun.keeplive.KeepAliveAidl;
17 | import com.ykun.live_library.R;
18 | import com.ykun.live_library.config.KeepAliveConfig;
19 | import com.ykun.live_library.config.NotificationUtils;
20 | import com.ykun.live_library.receive.NotificationClickReceiver;
21 | import com.ykun.live_library.utils.SPUtils;
22 |
23 | import static com.ykun.live_library.config.KeepAliveConfig.SP_NAME;
24 |
25 |
26 | /**
27 | * 守护进程
28 | */
29 | @SuppressWarnings(value = {"unchecked", "deprecation"})
30 | public final class RemoteService extends Service {
31 | private RemoteBinder mBilder;
32 | private String TAG = "RemoteService";
33 |
34 |
35 | @Override
36 | public void onCreate() {
37 | super.onCreate();
38 | Log.i(TAG, " onCreate");
39 | if (mBilder == null) {
40 | mBilder = new RemoteBinder();
41 | }
42 | }
43 |
44 | @Override
45 | public IBinder onBind(Intent intent) {
46 | return mBilder;
47 | }
48 |
49 | @Override
50 | public int onStartCommand(Intent intent, int flags, int startId) {
51 | try {
52 | this.bindService(new Intent(RemoteService.this, LocalService.class),
53 | connection, Context.BIND_ABOVE_CLIENT);
54 |
55 | shouDefNotify();
56 | } catch (Exception e) {
57 | Log.e(TAG, e.getMessage());
58 | }
59 | return START_STICKY;
60 | }
61 |
62 | private void shouDefNotify() {
63 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
64 | KeepAliveConfig.CONTENT = SPUtils.getInstance(getApplicationContext(), SP_NAME).getString(KeepAliveConfig.CONTENT);
65 | KeepAliveConfig.DEF_ICONS = SPUtils.getInstance(getApplicationContext(), SP_NAME).getInt(KeepAliveConfig.RES_ICON, R.drawable.ic_launcher);
66 | KeepAliveConfig.TITLE = SPUtils.getInstance(getApplicationContext(), SP_NAME).getString(KeepAliveConfig.TITLE);
67 | String title = SPUtils.getInstance(getApplicationContext(), SP_NAME).getString(KeepAliveConfig.TITLE);
68 | Log.d("JOB-->"+TAG,"KeepAliveConfig.CONTENT_"+KeepAliveConfig.CONTENT+" " + KeepAliveConfig.TITLE+" "+title);
69 | if (!TextUtils.isEmpty(KeepAliveConfig.TITLE) && !TextUtils.isEmpty( KeepAliveConfig.CONTENT)) {
70 | //启用前台服务,提升优先级
71 | Intent intent2 = new Intent(getApplicationContext(), NotificationClickReceiver.class);
72 | intent2.setAction(NotificationClickReceiver.CLICK_NOTIFICATION);
73 | Notification notification = NotificationUtils.createNotification(RemoteService.this, KeepAliveConfig.TITLE, KeepAliveConfig.CONTENT, KeepAliveConfig.DEF_ICONS, intent2);
74 | startForeground(KeepAliveConfig.FOREGROUD_NOTIFICATION_ID, notification);
75 | Log.d("JOB-->", TAG + "显示通知栏");
76 | }
77 | }
78 | }
79 |
80 | @Override
81 | public void onDestroy() {
82 | super.onDestroy();
83 | unbindService(connection);
84 | }
85 |
86 | private final class RemoteBinder extends KeepAliveAidl.Stub {
87 |
88 | @Override
89 | public void wakeUp(String title, String discription, int iconRes) throws RemoteException {
90 | Log.i(TAG, " wakeUp");
91 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
92 | if (title != null || discription != null) {
93 | KeepAliveConfig.CONTENT = title;
94 | KeepAliveConfig.DEF_ICONS = iconRes;
95 | KeepAliveConfig.TITLE = discription;
96 | } else {
97 | KeepAliveConfig.CONTENT = SPUtils.getInstance(getApplicationContext(), SP_NAME).getString(KeepAliveConfig.CONTENT);
98 | KeepAliveConfig.DEF_ICONS = SPUtils.getInstance(getApplicationContext(), SP_NAME).getInt(KeepAliveConfig.RES_ICON, R.drawable.ic_launcher);
99 | KeepAliveConfig.TITLE = SPUtils.getInstance(getApplicationContext(), SP_NAME).getString(KeepAliveConfig.TITLE);
100 |
101 | }
102 | if (KeepAliveConfig.TITLE != null && KeepAliveConfig.CONTENT != null) {
103 | //启用前台服务,提升优先级
104 | Intent intent2 = new Intent(getApplicationContext(), NotificationClickReceiver.class);
105 | intent2.setAction(NotificationClickReceiver.CLICK_NOTIFICATION);
106 | Notification notification = NotificationUtils.createNotification(RemoteService.this, KeepAliveConfig.TITLE, KeepAliveConfig.CONTENT, KeepAliveConfig.DEF_ICONS, intent2);
107 | startForeground(KeepAliveConfig.FOREGROUD_NOTIFICATION_ID, notification);
108 | Log.d("JOB-->", TAG + "2 显示通知栏");
109 | }
110 | }
111 | }
112 | }
113 |
114 | private final ServiceConnection connection = new ServiceConnection() {
115 | @Override
116 | public void onServiceDisconnected(ComponentName name) {
117 | Intent remoteService = new Intent(RemoteService.this,
118 | LocalService.class);
119 | if (Build.VERSION.SDK_INT >= 26) {
120 | RemoteService.this.startForegroundService(remoteService);
121 | } else {
122 | RemoteService.this.startService(remoteService);
123 | }
124 | RemoteService.this.bindService(new Intent(RemoteService.this,
125 | LocalService.class), connection, Context.BIND_ABOVE_CLIENT);
126 | PowerManager pm = (PowerManager) RemoteService.this.getSystemService(Context.POWER_SERVICE);
127 | boolean isScreenOn = pm.isScreenOn();
128 | if (isScreenOn) {
129 | sendBroadcast(new Intent("_ACTION_SCREEN_ON"));
130 | } else {
131 | sendBroadcast(new Intent("_ACTION_SCREEN_OFF"));
132 | }
133 | }
134 |
135 | @Override
136 | public void onServiceConnected(ComponentName name, IBinder service) {
137 | shouDefNotify();
138 | }
139 | };
140 |
141 | }
142 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/utils/KeepAliveUtils.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.utils;
2 |
3 | import android.app.ActivityManager;
4 | import android.app.Application;
5 | import android.content.ComponentName;
6 | import android.content.Context;
7 | import android.content.pm.ApplicationInfo;
8 | import android.content.pm.PackageManager;
9 |
10 | import java.util.Iterator;
11 | import java.util.List;
12 |
13 | public class KeepAliveUtils {
14 |
15 | public static boolean isRunning(Application application) {
16 | int pid = android.os.Process.myPid();
17 | String processName = "";
18 | ActivityManager mActivityManager = (ActivityManager) application.getSystemService(Context.ACTIVITY_SERVICE);
19 | for (ActivityManager.RunningAppProcessInfo appProcess : mActivityManager.getRunningAppProcesses()) {
20 | if (appProcess.pid == pid) {
21 | processName = appProcess.processName;
22 | break;
23 | }
24 | }
25 | String packageName = application.getPackageName();
26 | if (processName.equals(packageName)) {
27 | return true;
28 | }
29 | return false;
30 | }
31 |
32 |
33 | public static boolean isServiceRunning(Context ctx, String className) {
34 | boolean isRunning = false;
35 | ActivityManager activityManager = (ActivityManager) ctx
36 | .getSystemService(Context.ACTIVITY_SERVICE);
37 | List servicesList = activityManager
38 | .getRunningServices(Integer.MAX_VALUE);
39 | Iterator l = servicesList.iterator();
40 | while (l.hasNext()) {
41 | ActivityManager.RunningServiceInfo si = l.next();
42 | if (className.equals(si.service.getClassName())) {
43 | isRunning = true;
44 | }
45 |
46 | if (className.equals(si.process)){
47 | isRunning = true;
48 | }
49 | }
50 | return isRunning;
51 | }
52 |
53 | public static boolean IsForeground(Context context) {
54 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
55 | List tasks = am.getRunningTasks(1);
56 | if (tasks != null && !tasks.isEmpty()) {
57 | ComponentName topActivity = tasks.get(0).topActivity;
58 | if (topActivity.getPackageName().equals(context.getPackageName())) {
59 | return true;
60 | }
61 | }
62 | return false;
63 | }
64 |
65 | public static boolean isRunningTaskExist(Context context, String processName) {
66 | ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
67 | List processList = am.getRunningAppProcesses();
68 | for (ActivityManager.RunningAppProcessInfo info : processList) {
69 | if (info.processName.equals(processName)) {
70 | return true;
71 | }
72 | }
73 | return false;
74 | }
75 |
76 | public static String getApplicationName(Application application) {
77 | PackageManager packageManager = null;
78 | ApplicationInfo applicationInfo = null;
79 | try {
80 | packageManager = application.getPackageManager();
81 | applicationInfo = packageManager.getApplicationInfo(application.getPackageName(), 0);
82 | } catch (PackageManager.NameNotFoundException e) {
83 | applicationInfo = null;
84 | }
85 | String applicationName =
86 | (String) packageManager.getApplicationLabel(applicationInfo);
87 | return applicationName;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/live_library/src/main/java/com/ykun/live_library/utils/SPUtils.java:
--------------------------------------------------------------------------------
1 | package com.ykun.live_library.utils;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.content.Context;
5 | import android.content.SharedPreferences;
6 | import android.support.annotation.NonNull;
7 |
8 | import com.ykun.live_library.R;
9 | import com.ykun.live_library.pro_sp.PreferenceUtil;
10 |
11 | @SuppressLint("ApplySharedPref")
12 | public final class SPUtils {
13 |
14 | private static Context mContext;
15 | private static SharedPreferences mPreferences;
16 |
17 | /**
18 | * Return the single {@link SPUtils} instance
19 | *
20 | * @param spName The name of sp.
21 | * @return the single {@link SPUtils} instance
22 | */
23 | public static SPUtils getInstance(Context context, String spName) {
24 | mContext = context;
25 | init(mContext);
26 | return new SPUtils();
27 | }
28 |
29 |
30 | /**
31 | * Put the string value in sp.
32 | *
33 | * @param key The key of sp.
34 | * @param value The value of sp.
35 | */
36 | public void put(@NonNull final String key, final String value) {
37 | // put(key, value, false);
38 | putS(key, value);
39 | }
40 |
41 | /**
42 | * Return the string value in sp.
43 | *
44 | * @param key The key of sp.
45 | * @return the string value if sp exists or {@code ""} otherwise
46 | */
47 | public String getString(@NonNull final String key) {
48 | return getS(key);
49 | }
50 |
51 | /**
52 | * Return the string value in sp.
53 | *
54 | * @param key The key of sp.
55 | * @param defaultValue The default value if the sp doesn't exist.
56 | * @return the string value if sp exists or {@code defaultValue} otherwise
57 | */
58 | public String getString(@NonNull final String key, final String defaultValue) {
59 | return getS(key);
60 | }
61 |
62 | /**
63 | * Put the int value in sp.
64 | *
65 | * @param key The key of sp.
66 | * @param value The value of sp.
67 | */
68 | public void put(@NonNull final String key, final int value) {
69 | // put(key, value, false);
70 | putI(key, value);
71 | }
72 |
73 | /**
74 | * Return the int value in sp.
75 | *
76 | * @param key The key of sp.
77 | * @return the int value if sp exists or {@code -1} otherwise
78 | */
79 | public int getInt(@NonNull final String key) {
80 | return getI(key, R.drawable.ic_launcher);
81 | }
82 |
83 | /**
84 | * Return the int value in sp.
85 | *
86 | * @param key The key of sp.
87 | * @param defaultValue The default value if the sp doesn't exist.
88 | * @return the int value if sp exists or {@code defaultValue} otherwise
89 | */
90 | public int getInt(@NonNull final String key, final int defaultValue) {
91 | return getI(key, defaultValue);
92 | }
93 |
94 |
95 | public static void init(Context context) {
96 | if (mPreferences == null)
97 | mPreferences = PreferenceUtil.getSharedPreference(context, "DEV_YKUN");
98 | }
99 |
100 | public static String getS(String key) {
101 | return mPreferences.getString(key, "");
102 | }
103 |
104 | public static int getI(String key, int def) {
105 | return mPreferences.getInt(key, def);
106 | }
107 |
108 | public static void putS(String key, String def) {
109 | mPreferences.edit().putString(key, def).apply();
110 | }
111 |
112 | public static void putI(String key, int def) {
113 | mPreferences.edit().putInt(key, def).apply();
114 | }
115 | }
--------------------------------------------------------------------------------
/live_library/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/live_library/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/live_library/src/main/res/raw/novioce.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/yangkun19921001/KeepAlive/e754680440338da493352d053a148afd7045a77e/live_library/src/main/res/raw/novioce.wav
--------------------------------------------------------------------------------
/live_library/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | live_library
3 |
4 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app',
2 | ':live_library'
3 |
--------------------------------------------------------------------------------