赞
踩
众所周知,日活率是一款App的核心绩效指标,日活量不仅反应了应用的受欢迎程度,同时反应了产品的变现能力,进而直接影响盈利能力和企业估值。为了抢占市场,谁都不会放过任何一个可以提高应用日活的方法,所以App进程保活都是各大厂商,特别是头部应用开发商永恒的追求,毕竟一旦 App 进程死亡,那就再也无法在用户的手机上开展任何业务,所有的商业模型在用户侧都没有立足之地。
早期的Android系统不完善,从而导致有很多空子可以钻,它们用着各种各样的方式进行保活,长期以来被人诟病耗电、卡顿,也滋生了很多流氓应用,拖垮Android 平台的流畅性,建议不要这么做,本文只作技术性的探讨。
随着 Android 系统的发展,这一切都在往好的方向演变。
随着Android系统日渐完善,单单通过自己拉活自己逐渐变得不可能了;
因此后面的所谓「保活」基本上是两条路:
进程保活方案:
1、最好的方案那肯定是跟各大系统厂商建立合作关系,把App加入系统内存清理的白名单;比如微信,降低oom_adj值,尽量保证进程不被系统杀死。
那问题又来了:什么是oom_adj?
Android有一个oom的机制,系统会根据进程的优先级,给每个进程一个oom权重值,当系统内存不足时,系统会根据这个优先级去选择将哪些进程杀掉,以腾出空间保证更高优先级的进程能正常运行。要想让进程长期存活,提高优先级是个不二之选。这个可以在adb中,通过以下命令查看:su cat /proc/pid/oom_adj , 这个值越小,说明进程的优先级越高,越不容易被进程kill掉。
如果是负数,表示该进程为系统进程,肯定不会被杀掉,
如果是0,表示是前台进程,即当前用户正在操作的进程,除非万不得已,也不会被杀掉;
如果是1,表示是可见进程,通常表示有一个前台服务,会在通知栏有一个划不掉的通知,比如放歌,下载文件什么的;
再增大,则优先级逐渐降低,顺序为服务进程,缓存进程,空进程等等。
2、我们常常将保活方法进行分类:白色保活、灰色保活、黑色保活。
白色保活
灰色保活
黑色保活
3、实现过程:
1)、用startForeground()启动前台服务
前台Service,使用startForeground这个Service尽量要轻,不要占用过多的系统资源,否则系统在资源紧张时,照样会将其杀死。
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(); Log.i(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()); } 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(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { NotificationManager mManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); mManager.cancel(NOTICE_ID); } Log.d(TAG, "DaemonService---->onDestroy,前台service被杀死"); // 重启自己 Intent intent = new Intent(getApplicationContext(), DaemonService.class); startService(intent); } }
<service
android:name=".service.DaemonService"
android:enabled="true"
android:exported="true"
android:process=":daemon_service" />
Intent i = new Intent(this, DaemonService.class);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
startForegroundService(i);
} else {
startService(i);
}
这种保活方式,会在通知栏常驻一条通知。
2)、开启前台Service
这个其实跟(1)是相同的,区别在于这个方式将常驻通知栏移除了
@Override public void onCreate() { super.onCreate(); Log.i(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()); } }
/** 移除前台Service通知栏标志,这个Service选择性使用 */ 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(); } }
<service
android:name=".service.DaemonService"
android:enabled="true"
android:exported="true"
android:process=":daemon_service" />
<service
android:name=".service.CancelNoticeService"
android:enabled="true"
android:exported="true"
android:process=":service" />
同时启动两个service,共享同一个NotificationID,并且将他们同时置为前台状态,此时会出现两个前台服务,但通知管理器里只有一个关联的通知。这时我们在其中一个服务中调用 stopForeground(true),这个服务前台状态会被取消,同时状态栏通知也被移除。另外一个服务并没有受到影响,还是前台服务状态,但是此时,状态栏通知已经没了!
3)、1 像素activity保活方案
屏幕关闭的时候打开一个1px的透明的activity,屏幕开启的时候再去finsh掉这个activty即可
public class OnepxActivity extends Activity { private BroadcastReceiver br; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Window window = getWindow(); window.setGravity(Gravity.LEFT | Gravity.TOP); WindowManager.LayoutParams params = window.getAttributes(); params.x = 0; params.y = 0; params.height = 1; params.width = 1; window.setAttributes(params); //结束该页面的广播 br = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { LogUtils.d("OnepxActivity finish ================"); finish(); } }; registerReceiver(br, new IntentFilter("finish activity")); //检查屏幕状态 checkScreenOn("onCreate"); } /** * 检查屏幕状态 isScreenOn为true 屏幕“亮”结束该Activity * */ private void checkScreenOn(String methodName) { LogUtils.d("from call method: " + methodName); PowerManager pm = (PowerManager) OnepxActivity.this.getSystemService(Context.POWER_SERVICE); boolean isScreenOn = pm.isScreenOn(); LogUtils.i("isScreenOn: "+isScreenOn); if(isScreenOn){ finish(); } } @Override protected void onDestroy() { LogUtils.i("===onDestroy==="); try{ unregisterReceiver(br); }catch (IllegalArgumentException e){ LogUtils.e("receiver is not resisted: "+e); } super.onDestroy(); } @Override protected void onResume() { super.onResume(); checkScreenOn("onResume"); } }
public class OnepxReceiver extends BroadcastReceiver { private static OnepxReceiver receiver; @Override public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {//屏幕被关闭 Intent it = new Intent(context, OnepxActivity.class); it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); context.startActivity(it); LogUtils.i("1px--screen off-"); } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {//屏幕被打开 context.sendBroadcast(new Intent("finish activity")); LogUtils.i("1px--screen on-"); } } public static void register1pxReceiver(Context context) { if (receiver == null) { receiver = new OnepxReceiver(); } context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_OFF)); context.registerReceiver(receiver, new IntentFilter(Intent.ACTION_SCREEN_ON)); } public static void unregister1pxReceiver(Context context) { context.unregisterReceiver(receiver); } }
4)、Service中循环播放一段无声音频
新建一个播放音乐的Service类,将播放模式改为无限循环播放。在其onDestroy方法中对自己重新启动。
public class PlayerMusicService extends Service { private final static String TAG = PlayerMusicService.class.getSimpleName(); private MediaPlayer mMediaPlayer; @Nullable @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { super.onCreate(); Logger.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) { Logger.d(TAG, "启动后台播放音乐"); mMediaPlayer.start(); } } private void stopPlayMusic() { if (mMediaPlayer != null) { Logger.d(TAG, "关闭后台播放音乐"); mMediaPlayer.stop(); } } @Override public void onDestroy() { super.onDestroy(); stopPlayMusic(); Logger.d(TAG, TAG + "---->onCreate,停止服务"); // 重启 Intent intent = new Intent(getApplicationContext(), PlayerMusicService.class); startService(intent); } }
<service
android:name=".service.PlayerMusicService"
android:enabled="true"
android:exported="true"
android:process=":music_service" />
Intent intent = new Intent(this, PlayerMusicService.class);
startService(intent);
这里就介绍保活的几种方式啦,到这里就介绍完啦。
小编整理了一份Android电子书籍,需要的童鞋关注公众号回复:“e_books” 即可获取哦!
欢迎关注公众号(longxuanzhigu),获得更多福利、精彩内容哦!
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。