商量Android6.0以上的系统APP驻留内存(保活)达成-争宠篇
原创探讨Android6.0以及上述系统APP常驻内存(保活)实现-争宠篇
(请注明转载来源:http://blog.csdn.net/andrexpert/article/details/75045678)
APP保活系列(最大支撑力Android 7.0):
(1) 本文讨论了一种在双进程守护进程应用中保持实时的新方法。
(2) 探讨Android6.0以及上述系统APP常驻内存(保活)实现-争宠篇
(3) 探讨Android6.0以及上述系统APP常驻内存(保活)实现-复活篇
APP常驻内存(保活)距上次研究已有半年多了。最近,用户反馈称 多进程守护程序 在华为Mate8(7.0)维持生命的效果不是很好。有时我还是不能及时收到消息。因此,我持怀疑态度,重新发现了测试的原始代码。顺便说一下,我分析了市场上的主流运动。APP保活方法(微信、手Q算了吧,富裕家庭的孩子并不具有代表性。),而且还传输到内存中的系统。APP进一步探索管理规则。这篇文章是最近几周对未来的探索、学习和测试之一。
一、APP总结了保持活力的核心思想。
对于Android6.0及以上系统APP我认为主要是通过这两个方面,即减少omm_adj值,尝试确保进程不会被系统终止。然而,需要理解的是,面对各家手机厂商的深度定制和谷歌日益严格的资源管理机制,这两种方法的结合并不是永久的,只能相对存在,不同模式的结果也不尽相同。
由于空间限制,本文主要分析了减少的方法。oom_adj要降低该值APP被杀的可能性,以及oom_adj价值是如何实现的?接下来,我们需要了解Android系统回收内存中进程的规则:当进程在内存中时,主要有五种状态,即前台进程、可见进程、服务进程、后台进程和空进程。这些状态的处理优先级从高到低,oom_adj值从低到高(在ProcessList定义),然后Android该系统将基于当前系统资源和流程。oom_adj值回收相应的进程,前台进程通常不被回收,空进程最有可能被回收,这个管理规则是。"传说中"的Low Memory Killer。为了更直观地理解这套规则,我画了一张表:
注:优先级1表示最先进的普通流程oom_adj>=0,系统流程oom_adj<0,系统将根据相应的存储器阈值符合某一段。oom_adj价值的过程被循环利用。此外oom_adj值也会随着占用物理内存越大而增大,系统流程绝对不会被系统杀死。
**2.市场主流运动。APP分析
-
咕 咚(v 7.17.0)
(1) 一键清理/滑动清理
a. 当"咕咚"在停止状态下,其进程被终止,通知栏图标被清理,等待几分钟 自动重启,当重新输入“plump”时,将从欢迎界面重新进入;
b. 当"咕咚"在运动状态下,进程死亡,通知栏图标被清除,等待几分钟不会自动重新启动。然而,当重新进入“丰满”时,它直接显示运动界面,而不是从欢迎界面进入,运动时间和其他状态与被清洁时相同;
c. 当"咕咚"在运动暂停状态下,其过程为正常生存,通知栏图标正常显示。如果单独清理,进程将终止,通知栏图标将被清除。然而,当重新进入“丰满”时,它直接显示运动界面,而不是从欢迎界面进入,运动时间和其他状态与被清理时相同;
(2) 黑屏/锁屏**
a. 当"咕咚"在停止状态下,退到后台,将屏幕锁定到黑屏状态,等待5分钟,进程死亡,通知栏清除;
b. 当"咕咚"在运动状态下,退到后台,将屏幕锁定到黑屏状态,然后进入系统,“耿东”跑步界面自动弹出。再次锁定屏幕并等待20几分钟后,进程没有终止,"咕咚"跑步界面自动弹出,运动状态不变;
c. 当"咕咚"在运动暂停状态下,退到后台,将屏幕锁定到黑屏状态,然后进入系统,"咕咚"运行界面自动弹出。再次锁定屏幕。等待20几分钟后,进程没有终止,"咕咚"跑步界面自动弹出,运动状态不变;- 前提:"手机管家->锁屏清洁应用"关闭;
"手机管家->自启管理"关闭;
运动状态,禁用返回键,用户只能从。。Home钥匙退到背景。
运动界面文本闪烁或运动计时
断网 - 分析:当"咕咚"当处于停止状态时,一键清洁和黑屏状态将被终止,表明在进入运动界面之前,保持机制尚未启动。(也就是说,没有将运动界面切换到背景等。)。当“丰满”运动时,一键清洁和黑屏不会被杀死。(滑动清洁除外),表示维护机制已启动:①"咕咚"禁止使用返回键以确保移动。Activity未销毁;(2) 不断更新通知栏定时,以确保APP始终在前台,以防止系统回收;③"咕咚"它可以在清理后自动重新启动,删除后会自动弹出通知,表明可能还有其他事情发生。(进程或Service)侦听器移动Service(或进程)生存状态,当Service当它被摧毁时,它立即被拉起。(4) 在“丰满”被强制停止或清理并杀死后,进入界面,该界面将再次直接显示移动,并可以保持杀死前的移动状态,表明它可以使用配置文件记录相关状态;(5) 锁屏/解锁后,"咕咚"运动界面将自动弹出,表明它使用广播机制来监控屏幕锁定广播并弹出Activity确保流程始终在前台;
- 结论:居民通知栏、双进程监护人、广播锁屏、自定义锁屏
- 注:以上为华为Mate8(7.0)试验结果;其他如三星C9(6.0)最好保持活力,尤其是用一键清洁时,"阿甘将自动启动。据估计,使用进程守护程序策略,而三星使用本机系统,因此您知道结果;360F4(6.0)生存状况很差。他不是歹徒中的魁斗士。他以更为歹徒的方式杀死了歹徒。APP;
2. 乐动力(v7.3.2)
(1) 一键清理 / 滑动清理
三星C9(6.0):无论状态如何,"乐动力" 进程被终止,等待了几分钟,没有自动启动;
360F4(6.0):无论状态如何,"乐动力" 进程被终止,等待了几分钟,没有自动启动;
华为Mate8(7.0):无论状态如何,"乐动力" 进程被终止,等待了几分钟,没有自动启动;
(2) 锁屏/黑屏
a. 当"乐动力"在停止状态下,退到后台,锁定屏幕并等待。5分钟,进程死亡,通知栏清除;
b. 当"乐动力"在运动暂停状态下,退到后台,锁定屏幕然后打开,运动界面切换到前台,并强制弹出自定义锁定屏幕界面(覆盖系统屏幕锁定界面); 再次锁定屏幕并等待20分钟,应用程序生存期;
c. 当"乐动力"在运动状态下,退到后台,锁定屏幕然后打开,运动界面切换到前台,并强制弹出自定义锁定屏幕界面(覆盖系统屏幕锁定界面); 再次锁定屏幕并等待20分钟,应用程序生存期; - 前提:"手机管家->锁屏清洁应用"关闭;
"手机管家->自启管理"关闭;
运动状态,禁用返回键,用户只能从。。Home钥匙退到背景。
断网 - 分析:当"乐动力"当处于停止状态(黑屏状态)时,系统在短时间内将其关闭,这表明实时机制未启用,"乐动力"有一段时间没有被杀死,当屏幕被锁定时,"乐动力"将自动将运动界面切换到前台,此外,将强制弹出自定义屏幕锁定界面,显示"乐动力"直播机制可能会使用监控屏幕广播来强制相关界面切换到前台以进行改进。"乐动力"黑屏状态下的生存率。
- 结论:居民通知栏、广播锁定屏幕、自定义锁定屏幕
3. 悦动圈(v3.1.2.9)
(1) 一键清理 / 滑动清理
三星C9(6.0):效果与音乐力量一致;
360F4(6.0):效果与音乐力量一致;
华为Mate8(7.0):效果与音乐力量一致;
(2) 锁屏/黑屏
a. 当"悦动圈"在停止状态下,退到后台,锁定屏幕并等待。3分钟,进程死亡,通知栏清除;
b. 当"悦动圈"当处于运动暂停状态时,自定义锁定屏幕和前台的开关界面与丰满的音乐力量相同,效果相同;
c. 当"悦动圈"当你处于运动状态时,定制屏幕锁并将界面切换到前台的效果与丰满快乐的力量相同; - 结论:居民通知栏、广播锁定屏幕、自定义锁定屏幕
三、APP关于直播方案的讨论
经过以上讨论和分析,"咕咚"、"乐动力"等这类APP主要通过收听系统广播,如屏幕锁定和网络,将进程置于前台以提高进程的级别,从而防止进程不太容易被系统杀死。此外"咕咚"相关流程也可用于清理复活策略。当然,对于复活策略,我们将在下一篇文章中对其进行探讨,这篇文章将重点介绍上述内容。APP是将过程减少omm_adj值,以防止其被系统杀死。
为了达到同样的目的"咕咚"等APP与效果类似,我们模拟了一个场景,当用户登录测试时APP之后,不要先打开保活功能;当用户开始运行时,打开保活功能,然后在此基础上进行黑屏操作、一键清洁、强制停止等功能测试。也就是说,Android项目中SplashActivity、LoginActivity只要与我们合作"演戏"真的开始了APP保持生命的逻辑在于SportsActivity,将上演"后宫争宠"戏码。
好了,年轻人,开始表演吧!
1. 开启前台Service,“迫使国王站在顶端”
将Service设置为前景,目的是改进流程。Service的oom_adj价值,以减少其被系统回收的机会。该方案的原则是通过使用 startForeground()方法将是当前的。Service把它放在前台改进Service优先。需要注意的是API大于18而言 startForeground()方法需要弹出一个可见的通知。如果你觉得不舒服,你可以打开另一个。Service删除通知栏oom_adj该值未更改。实现代码如下:
a) DaemonService.java
/**前台Service,使用startForeground
- 这个Service尽量轻巧,不要占用太多系统资源,否则
- 当资源紧张时,系统仍然会杀死它。
- Created by jianddongguo on 2017/7/7.
-
http://blog.csdn.net/andrexpert */ public class DaemonService extends Service { private static final String TAG = "DaemonService"; public static final int NOTICE_ID = 100;
@Nullable @Override public IBinder onBind(Intent intent) { return null; }
@Override public void onCreate() { super.onCreate(); if(Contants.DEBUG) Log.d(TAG,"DaemonService---->onCreate调用,启动前台service"); //如果API大于18,需要弹出一个可见的通知 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){ Notification.Builder builder = new Notification.Builder(this); builder.setSmallIcon(R.mipmap.ic_launcher); builder.setContentTitle("KeepAppAlive"); builder.setContentText("DaemonService is runing..."); startForeground(NOTICE_ID,builder.build()); // 如果你觉得居民通知栏的体验不好 // 可以启动CancelNoticeService,删除通知,oom_adj值不变 Intent intent = new Intent(this,CancelNoticeService.class); startService(intent); }else{ startForeground(NOTICE_ID,new Notification()); } }
@Override public int onStartCommand(Intent intent, int flags, int startId) { // 如果Service被终止 // 当资源允许时,重新启动service return START_STICKY; }
@Override public void onDestroy() { super.onDestroy(); // 如果Service已终止、已终止通知 if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2){ NotificationManager mManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); mManager.cancel(NOTICE_ID); } if(Contants.DEBUG) Log.d(TAG,"DaemonService---->onDestroy,前台service被杀死"); // 重启自己 Intent intent = new Intent(getApplicationContext(),DaemonService.class); startService(intent); } }
- 前提:"手机管家->锁屏清洁应用"关闭;
解释:
这里还使用了两种技术:一种是onStartCommand方法返回START_STICKY,其角色是Service进程被kill之后,系统将尝试重新创建Service并将保留Service的状态为开始状态,但不保留已传递的Intent对象,onStartCommand必须再次调用该方法。第二个在onDestory重新启动自己的方法,即只要Service当它被摧毁时onDestory在这里,我们将重新启动它。
b) CancelNoticeService.java
/** 移除前台Service通知栏标志,此Service选择性使用
*
* Created by jianddongguo on 2017/7/7.
* http://blog.csdn.net/andrexpert
*/
public class CancelNoticeService extends Service {
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2){
Notification.Builder builder = new Notification.Builder(this);
builder.setSmallIcon(R.mipmap.ic_launcher);
startForeground(DaemonService.NOTICE_ID,builder.build());
// 启动线程以删除它DaemonService弹出通知
new Thread(new Runnable() {
@Override
public void run() {
// 延迟1s
SystemClock.sleep(1000);
// 取消CancelNoticeService的前台
stopForeground(true);
// 移除DaemonService弹出通知
NotificationManager manager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
manager.cancel(DaemonService.NOTICE_ID);
// 完成任务并终止自己
stopSelf();
}
}).start();
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
}
}
c) AndroidManifest.xml
解释:
众所周知Service没有独立的进程,它通常作为线程在其所在的应用程序进程中运行,应用程序进程名称与包名称相同。如果希望指定的组件和应用程序在指定的进程中运行,则需要通过。android:process属性为其创建进程,因此。android:process=":daemon_service"就是让DaemonService以“com.jiangdg.keepappalive:daemon_service“进行中;android:enabled属性的角色是Android系统是否实例化应用程序中的组件;android:exported属性的角色是当前组件(Service)它是否可以由应用程序中的组件而不是其本身启动。
d) 测试结果
接下来,让我们观察KeepAppAlive进程的oom_adj值变化:
首先,adb查看KeepAppAlive工艺的工艺编号;
E:\Android\StudioProject\KeepAppAlive>adb shell
shell@trltechn:/ $ su
root@trltechn:/ # ps | grep jiangdg

第二,观察KeepAppAlive处于不同状态的进程oom_adj值;
root@trltechn:/ # cat /proc/15689/oom_adj
root@trltechn:/ # cat /proc/16033/oom_adj

注:如果执行su命令,提示符"/system/bin/sh: su: not found",表示移动电话设备没有root。ps命令用于显示静态进程状态,top该命令实时监控进程,每次都启动。KeepAppAlive工艺编号不同。
2. 监视器锁屏广播,“Mf”1像素“悲剧”
a) ScreenReceiverUtil.java
/** 静态监控锁屏、解锁、开屏广播
* a) 当用户锁定屏幕时,SportsActivity把它放在前台,同时打开。1像素悬浮窗口;
* b) 当用户解锁时,关闭1像素悬浮窗口;
*
* Created by jianddongguo on 2017/7/8.
* http://blog.csdn.net/andrexpert
*/
public class ScreenReceiverUtil {
private Context mContext;
// 锁屏广播接收机
private SreenBroadcastReceiver mScreenReceiver;
// 屏幕状态更改回调界面
private SreenStateListener mStateReceiverListener;
public ScreenReceiverUtil(Context mContext){
this.mContext = mContext;
}
public void setScreenReceiverListener(SreenStateListener mStateReceiverListener){
this.mStateReceiverListener = mStateReceiverListener;
// 动态启动广播接收机
this.mScreenReceiver = new SreenBroadcastReceiver();
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_ON);
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
mContext.registerReceiver(mScreenReceiver,filter);
}
public void stopScreenReceiverListener(){
mContext.unregisterReceiver(mScreenReceiver);
}
public class SreenBroadcastReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.d("KeepAppAlive","SreenLockReceiver-->收听系统广播:"+action);
if(mStateReceiverListener == null){
return;
}
if(Intent.ACTION_SCREEN_ON.equals(action)){ // 开屏
mStateReceiverListener.onSreenOn();
}else if(Intent.ACTION_SCREEN_OFF.equals(action)){ // 锁屏
mStateReceiverListener.onSreenOff();
}else if(Intent.ACTION_USER_PRESENT.equals(action)){ // 解锁
mStateReceiverListener.onUserPresent();
}
}
}
// 监听sreen状态外部回调接口
public interface SreenStateListener {
void onSreenOn();
void onSreenOff();
void onUserPresent();
}
}
解释:
由于广播接收器的静态注册,无法接收系统的锁定屏幕。(Intent.ACTION_SCREEN_OFF)和开屏(Intent.ACTION_SCREEN_ON)广播,因此必须通过动态注册进行监控。此外,这里使用一个接口将侦听结果回调给调用者。
b) ScreenManager.java
/**1像素管理类
*
* Created by jianddongguo on 2017/7/8.
* http://blog.csdn.net/andrexpert
*/
public class ScreenManager {
private static final String TAG = "ScreenManager";
private Context mContext;
private static ScreenManager mSreenManager;
// 使用弱引用以防止内存泄漏。
private WeakReference mActivityRef;
private ScreenManager(Context mContext){
this.mContext = mContext;
}
// 单例模式
public static ScreenManager getScreenManagerInstance(Context context){
if(mSreenManager == null){
mSreenManager = new ScreenManager(context);
}
return mSreenManager;
}
// 获得SinglePixelActivity的引用
public void setSingleActivity(Activity mActivity){
mActivityRef = new WeakReference<>(mActivity);
}
// 启动SinglePixelActivity
public void startActivity(){
if(Contants.DEBUG)
Log.d(TAG,"准备启动SinglePixelActivity...");
Intent intent = new Intent(mContext,SinglePixelActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
mContext.startActivity(intent);
}
// 结束SinglePixelActivity
public void finishActivity(){
if(Contants.DEBUG)
Log.d(TAG,"准备结束SinglePixelActivity...");
if(mActivityRef != null){
Activity mActivity = mActivityRef.get();
if(mActivity != null){
mActivity.finish();
}
}
}
}
解释:
Java对对象的引用有四个级别:强引用、软引用、弱引用和虚拟引用。这里,我们使用弱引用WeakReference为了防止内存泄漏,为了解释这个问题,我们举一个例子:有两个类。class A和class B,分别实例化两个类以获取。a,b,其中a也作为实例化B当输入施工参数时,代码如下:
A a = new A();
B b = new B(a);
从这两行代码中,a是对象A的引用,b是对象B引用的对象B取决于对象A,对象A和对象B之间形成强烈的参照。什么时候a=null时,a未指向对象A通常,对象A未被其他对象引用。GC回收,但到期B也取决于对象A,对象A不会被GC回收,导致内存泄漏(除非b=null,对象A和对象B才会被GC同时回收)。如果使用弱引用,则对象A只会被WeakReference依赖,当a=null时,GC它被回收,从而避免了内存泄漏。
c) SinglePixelActivity.java
/**1像素Activity
*
* Created by jianddongguo on 2017/7/8.
*/
public class SinglePixelActivity extends AppCompatActivity {
private static final String TAG = "SinglePixelActivity";
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(Contants.DEBUG)
Log.d(TAG,"onCreate--->启动1像素保活");
// 获得activity的Window对象,设置其属性
Window mWindow = getWindow();
mWindow.setGravity(Gravity.LEFT | Gravity.TOP);
WindowManager.LayoutParams attrParams = mWindow.getAttributes();
attrParams.x = 0;
attrParams.y = 0;
attrParams.height = 1;
attrParams.width = 1;
mWindow.setAttributes(attrParams);
// 绑定SinglePixelActivity到ScreenManager
ScreenManager.getScreenManagerInstance(this).setSingleActivity(this);
}
@Override
protected void onDestroy() {
if(Contants.DEBUG)
Log.d(TAG,"onDestroy--->1像素保留已终止");
if(! SystemUtils.isAppAlive(this,Contants.PACKAGE_NAME)){
Intent intentAlive = new Intent(this, SportsActivity.class);
intentAlive.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(intentAlive);
Log.i(TAG,"SinglePixelActivity---->APP被杀了,我想重新启动");
}
super.onDestroy();
}
}
解释:
在UI在接口架构中Activity全部包含一个Window对象,在Android中Window物体通常是制成的PhoneWindow来实现,PhoneWindow将一个DecorView设置为整个应用程序窗口的根目录。View,用作窗口界面的顶层视图,并封装了许多操作窗口的常用方法。...好吧,既然我们已经知道了,那就别走远了Window中的对象Activity就位,我们通过。getWindow获取方法SinglePixelActivity 的Window对象,然后为其设置相关属性,如窗体的大小、位置、坐标等,以实现所需的"1像素"界面效果。
d) SportsActivity.java
/** 运动界面,开始监控屏幕锁定广播,判断是否切换1像素界面
*
* Created by jianddongguo on 2017/7/7.
* http://blog.csdn.net/andrexpert
*/
public class SportsActivity extends AppCompatActivity {
// 动态注册锁定屏幕和其他广播
private ScreenReceiverUtil mScreenListener;
// 1像素Activity管理类
private ScreenManager mScreenManager;
// 代码省略...
private ScreenReceiverUtil.SreenStateListener mScreenListenerer = new ScreenReceiverUtil.SreenStateListener() {
@Override
public void onSreenOn() {
// 移除"1像素"
mScreenManager.finishActivity();
}
@Override
public void onSreenOff() {
// 接收到屏幕锁定广播后,它将SportsActivity切换到可见模式
// "咕咚"、"乐动力"、"悦动圈"这就是怎么做的
// Intent intent = new Intent(SportsActivity.this,SportsActivity.class);
// startActivity(intent);
// 如果你想,直接跳出来SportActivity很不爽
// 然后,我们将制作一个"1像素"惨案
mScreenManager.startActivity();
}
@Override
public void onUserPresent() {
// 解锁,暂时不使用,保留
}
};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sports);
if(Contants.DEBUG)
Log.d(TAG,"--->onCreate");
// 1. 注册锁定屏幕广播侦听器
mScreenListener = new ScreenReceiverUtil(this);
mScreenManager = ScreenManager.getScreenManagerInstance(this);
mScreenListener.setScreenReceiverListener(mScreenListenerer);
}
// 代码省略...
}
e) AndroidManifest.xml
解释:
android:launchMode该该属性用于指定activity启动模式共分为四种,即standar模式,每次启动activity将创建其实例并将其添加到任务堆栈的堆栈顶部;singleTop模式,每次启动activity如果堆栈位于activity你不需要创造它,剩下的情况就是创造它。activity的实例;singleTask模式,如果activity如果的实例存在于堆栈中,则不需要创建它,只需将activity添加到堆栈的顶部并放置activity以上的activity实例全部pop;singleInstance模式,将创建activity实例被放置在单独的堆栈中,该堆栈只能存储此实例,并作为共享实例存在;
android:configChanges该属性用于捕获电话状态的变化,即当电话状态发生变化时(如切换水平和垂直屏幕、屏幕大小)重新启动更改时,将保存当前活动状态。Activity,由于SinglePixelActivity肩负着维持生命的特殊使命,用在这里android:configChanges属性防止Activity重新启动,它只是调用onConfigurationChanged(Configuration newConfig)通知移动电话状态的变化;
android:excludeFromRecents属性用于控制SinglePixelActivity未显示在最近的任务列表中;
android:finishOnTaskLaunch该属性用于标记用户再次启动应用程序的时间。(TASK)是否关闭已存在的Activity的实例,false表示未关闭;
android:theme该该属性用于指定Activity显示主题,在这里我们自定义主题。SingleActivityStyle
f) 测试结果
监视屏幕锁定广播,当锁定屏幕时,它将SportActivity置于前台(可见)

监视屏幕锁定广播,并在锁定屏幕时将其打开。SinglePixelActivity(1像素)

3 .循环无声音频,"创造坚不可摧的身体"
对于三星C9、Note4和华为4X例如,与前台结合Service和悬挂接口(1像素)在用户未主动清洁或强制杀戮的情况下,测试APP保持生命的效果仍然很好。然而,对于华为来说Mate8例如,效果仍然很差,尤其是当使用一键清除内存时,测试APP基本上无法生存。然后"咕咚"然而,我活得很好。我无法一键清理。我就是想不出来。"姐"时,一个"恶心"界面出现在我面前。尼玛!你看到下面的红色方框了吗,"咕咚"背景中播放无声音乐也就不足为奇了。好吧,不要纠结太多,这里只是从学习技术的角度来看,毕竟,用户对功耗仍然非常敏感,低于最后的手段或收敛点,不要这样做"风骚",用户体验非常重要,意外"泻"了你。
a) PlayerMusicService.java
/**循环无声音频,以提升进程优先级
*
* Created by jianddongguo on 2017/7/11.
* http://blog.csdn.net/andrexpert
*/
public class PlayerMusicService extends Service {
private final static String TAG = "PlayerMusicService";
private MediaPlayer mMediaPlayer;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
if(Contants.DEBUG)
Log.d(TAG,TAG+"---->onCreate,启动服务");
mMediaPlayer = MediaPlayer.create(getApplicationContext(), R.raw.silent);
mMediaPlayer.setLooping(true);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
new Thread(new Runnable() {
@Override
public void run() {
startPlayMusic();
}
}).start();
return START_STICKY;
}
private void startPlayMusic(){
if(mMediaPlayer != null){
if(Contants.DEBUG)
Log.d(TAG,"开始背景播放音乐");
mMediaPlayer.start();
}
}
private void stopPlayMusic(){
if(mMediaPlayer != null){
if(Contants.DEBUG)
Log.d(TAG,"关闭背景播放音乐");
mMediaPlayer.stop();
}
}
@Override
public void onDestroy() {
super.onDestroy();
stopPlayMusic();
if(Contants.DEBUG)
Log.d(TAG,TAG+"---->onCreate,停止服务");
// 重启
Intent intent = new Intent(getApplicationContext(),PlayerMusicService.class);
startService(intent);
}
}
b) AndroidManifest.xml
4. 测试结果
这里在cmd窗口使用"ps | grep jiangdg"如果进程存在于内存中,则命令将打印进程信息。每个模型的测试结果如下:
(1)华为Mate8(7.0):将测试APP置于背景、前景Service处于黑屏状态1几分钟内死亡,"1像素"悬浮Activity处于黑屏状态测试2效果仍然很好。然而,当用户一键清除最新的应用程序时,它将在后台打开时被关闭。Serive当播放无声音频循环时,一键清洁仍然有效,并且在背景中的黑屏模式下仍然有效。12小时或更长时间;
(2)三星C9(6.0):打开前台Service和1像素,KeepAppAlive在黑屏背景模式下生存9在一个多小时的时间里,本地系统似乎仍然温和。打开后台音频服务,用户将一键清理最近的应用程序以保持活跃;
(3)华为4X(6.0):效果同C9;
(4) 三星Note4(5.0):效果同C9;
注:Mate8循环无声音频,当用户点击一键清理最近应用时,KeepAppAlive它不会被杀死,但如果用户只选择清理KeepAppAlive也会被杀,这与"咕咚"保持生命的效果是一致的。
三星C9(6.0):运行Demo,背景黑屏实景效果

华为Mate8(7.0):运行Demo,黑屏和一键清洁和保持活力效果
Github项目地址: https://github.com/jiangdongguo/KeepingAppAlive
版权声明
所有资源都来源于爬虫采集,如有侵权请联系我们,我们将立即删除
itfan123


