Android 如何实现保活 app(电池医生实现) 基础理论 保活Service
需要做什么
应用被关闭的时候保活(最难)
在内存占用过大,系统自动释放内存时保活(优先杀死占用较高的Service)
重启手机后自动开启Service
手机息屏后不被释放内存
手机清理内存时保活
进程的五个常用级别:
前台进程(Foreground process):前台进程就是用户当前要处理的所有事情都必须要使用的进程。满足下面的各种情况则认为是前台进程。 杀死前台进程需要用户交互,前台进程的优先级最高
进程持有一个正在和用户交互的 Activity。
进程持有一个 Service,这个 Service 处于这几种状态:1. Service 与用户正在交互的 Activity 绑定。 2. Service 是在前台运行的。 3. Service 正在执行它的生命周期 onCreate()
,onStrarCommand
,onDestroy
。 4. 进程持有一个 BroadcastReceiver
这个 BroadcastReceiver
正在执行它的 onReceiver
方法。
可见进程(Visible process):如果一个进程不含任何前台的组件,但仍可被用户在屏幕上看到。当满足下面任意一条的时候,进程被认为是可见的。 可见的进程也被认为很重要,一般不会被销毁,除非是为了保证所有前台进程的运行而不得已不杀死可见进程的时候
进程持有一个 activity,这个 activity 不在前台。但是仍然可见的情况。
进程持有一个 Service ,这个 Service 与一个可见的 Activity 绑定。
服务进程(Service process):如果一个进程中运行着一个 Service,这个 Service 是通过 startService()
开启的,并且不属于上面两种较高优先级的情况下,这个进程就是一个服务进程。尽管服务进程没有和用户可以看到的东西绑定,但是它们一般在做的事情是用户关心的,比如后台播放音乐,后台下载数据等。所以系统会尽量维持它们的运行,除非系统内存不足以维持前台进程和可见进程的运行需要(这句话和没说一样)
后台进程(Background process):如果进程不属于上面三种情况,但是进程持有一个用户不可见的 activity (activity 的 onStop
被调用,但是 onDestroy
没有被调用的状态)就认为进程是一个后台进程。 后台进程不直接影响用户体验,系统会为了前台进程、可见进程、服务进程而任意杀死后台进程,通常情况下会有很多后台进程存在,他们会被保存在一个 LRU(least recently used)列表中,这样就可以确保用户最近使用的 Activity 最后被销毁,先销毁时间最远的 Activity。
空进程:如果一个进程不包含任何活跃的应用组件,则认为是空进程。例如:一个进程当中已经没有数据运行了,但是内存当中还为这个应用保留了一个进程空间。保存这种进程的唯一理由是为了缓存的需要,为了加快下次启动这个进程中组件的启动时间,这种空进程经常被杀死。
为了提高优先级我们可以使用startForeground()
方法将Service设置为前台进程。
BindService()
方法中的 flag 参数:
BIND_AUOT_CREATE: 只要绑定存在就会自动创建这个 Service,虽然创建了 Service,但是它的 `onStartCommand` 方法是不会调用的,因为这个方法只有在 `startService` 的时候被调用。 在 Android 4.0 以前,不提供这个标志的话,会影响系统判定当前 Service 进程的重要性(会把它认为是后台进程),当要设置的时候,告诉系统进程重要性的唯一方式是,通过 `bindService` 来实现,在这种情况下,只有 Activity 在前台才会起作用。(这样 Service 进程的优先级等同于启动它的进程的优先级)。 现在要想把 Service 进程的优先级降低,必须提供新的` falg` (BIND_ADJUST_WITH_ACTIVITY)。考虑到兼容性,如果没有指定 BIND_AUTO_CREATE 的时候,系统会自动加上 BIND_WAIVE_PRIORITY 和 BIND_ADJUST_WITH_ACTIVITY 来实现降低优先级的效果。因为在 Android 4.0 以前 Service 的优先级默认是后台进程,在 Android 4.0 之后默认是等同于宿主进程,所以只有设置了 BIND_WAIVE_PRIORITY 后才会 4.0 和 4.0以前都兼容起来被当做后台任务对待。
BIND_DEBUG_UNBIND: 用来 debug 使用的
BIND_NOT_FOREGROUND 不允许将绑定的 Service 的进程提升到前台进程的优先级,它将仍然拥有和客户端同样的内存优先级,所以在宿主进程没有被杀死的情况下,Service 的进程也是不会被杀死的。但是 cpu
可能会把它放在后台执行。仅仅在这种情况下会有作用,宿主进程在前端,Service 进程在后台
BIND_ABOVE_CLIENT 在这种情况下,Service 进程比 App 本身的进程还有重要,当设置后,内存溢出的时候,将会在关闭 Service 进程前关闭 App 进程。但是这种情况不能保证。
BIND_ALLOW_OOM_MANAGEMENT 允许内存管理系统管理 Service 的进程,允许系统在内存不足的时候删除这个进程。
BIND_WAIVE_PRIORITY 不影响 Service 进程的优先级的情况下,允许 Service 进程被加入后台队列中。
BIND_IMPORITANT 这个服务对于这个客户端来说是非常重要的,所以应该提升到前台进程的级别。一般这个进程 会提升到可见的级别,甚至客户端在后台的时候。
BIND_ADJUST_WITH_ACTIVITY 如果从一个 Activity 绑定,则这个 Service 进程的优先级和 Activity 是否对用户可见有关。
实现具体步骤 实现步骤简述
正常一般只需要开启前台服务
要求高保活,可以通过双进程服务互相持有
在AndroidManifest
中添加Service 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 <service android:name ="com.atom.battery.service.BatteryKeepService" android:priority ="1000" //设置高优先级 android:enabled ="true" android:exported ="true" /> <service android:name ="com.atom.battery.support.keepalive.KeepService" android:process =":resident" //放入新的进程 /> <service android:name ="com.atom.battery.support.keepalive.AssistantService" //辅助服务 android:enabled ="true" android:exported ="true" android:process =":remote" //放入新的进程 />
双进程服务互相绑定保护 创建aidl
实现跨进程通信 1 2 3 4 5 6 7 8 9 10 interface IKeepAlive { String getName () ; }
创建当前主要服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 class BatteryKeepService : Service (), BatteryStateUtil.BatteryChangeLinstener { private var mNotificationManager: NotificationManagerCompat? = null private var mChannel: NotificationChannelCompat? = null private var mRemoteViews: RemoteViews? = null private var mBuilder: NotificationCompat.Builder? = null private var mNotification: Notification? = null private var mBatteryStatus: Int ? = null private var mIsCharging: Boolean = false private var mChargeToXxNotify: Int = 100 override fun onCreate () { super .onCreate() bindAssistantService() createChannel() BatteryStateUtil.addLinstener(javaClass.simpleName,this ) } override fun onStartCommand (intent: Intent ?, flags: Int , startId: Int ) : Int { LanguageUtil.checkAndSetLanguage(this ) createForegroundNotification() return START_STICKY } private fun createForegroundNotification () { val needCreate = mNotificationManager == null || mNotification == null || mRemoteViews == null if (needCreate) { createChannel() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mNotificationManager?.getNotificationChannel(mChannel?.id!!)?.lockscreenVisibility = Notification.VISIBILITY_PUBLIC } val nfIntent = Intent(this , MainActivity::class .java) val pendingIntent = PendingIntent.getActivity(this , 1 , nfIntent, 0 ) mRemoteViews = RemoteViews(packageName, R.layout.notification_keep_remoteview) mRemoteViews?.setTextViewText( R.id.notify_keep_rv_tv, getString(R.string.widget_app_running) ) mBuilder = NotificationCompat.Builder(this , Constant.CHANNEL_ID_FOREGROUND) .setShowWhen(true ) .setContentIntent(pendingIntent) .setWhen(0 ) .setCustomContentView(mRemoteViews) .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) .setDefaults(NotificationCompat.FLAG_ONLY_ALERT_ONCE) .setVibrate(null ) .setLights(0 , 0 , 0 ) .setSound(null ) .setSmallIcon(R.drawable.atombattery_logo_small_25) .setPriority(NotificationCompat.PRIORITY_HIGH) mNotification = mBuilder?.build() mNotification!!.flags = mNotification!!.flags or NotificationCompat.FLAG_ONGOING_EVENT mNotification!!.flags = mNotification!!.flags or NotificationCompat.FLAG_NO_CLEAR } startForeground(Constant.NOTIFICATION_ID_KEEP_ALIVE, mNotification) } private fun createChannel () { mNotificationManager = NotificationManagerCompat.from(this ) if (mChannel == null ) { mChannel = NotificationChannelCompat.Builder( Constant.CHANNEL_ID_FOREGROUND, NotificationManagerCompat.IMPORTANCE_DEFAULT ) .setName(getString(R.string.widget_permanent_notification_name)) .setLightsEnabled(false ) .setVibrationEnabled(false ) .setVibrationPattern(longArrayOf(0 )) .setSound(null , null ) .setShowBadge(false ) .build() } mNotificationManager?.createNotificationChannel(mChannel!!) } override fun onDestroy () { super .onDestroy() try { stopForeground(true ) val intent = Intent(CommonUtil.getApp(), BatteryKeepService::class .java) Log.d("zfr" , "KeepAliveService destroy and restart" ) ContextCompat.startForegroundService(this ,intent) createForegroundNotification() } catch (tr: Throwable) { } } override fun onBind (intent: Intent ?) : IBinder? { Timber.tag("zfr" ).d("onBind" ) return keepAliveStub } override fun onRebind (intent: Intent ?) { } override fun onUnbind (intent: Intent ?) : Boolean { return true } private val keepAliveStub: IKeepAlive.Stub = object : IKeepAlive.Stub() { override fun getName () : String = "我是 KeepAliveService" } private var serviceConnection: ServiceConnection = object : ServiceConnection { override fun onServiceConnected (name: ComponentName , service: IBinder ) { val iAssistant: IKeepAlive = IKeepAlive.Stub.asInterface(service) if (iAssistant != null ) { try { Timber.tag("zfr" ).d("收到AssistantService的数据:name=" + iAssistant.getName()) } catch (e: RemoteException) { e.printStackTrace() } } } override fun onServiceDisconnected (name: ComponentName ) { bindAssistantService() } } fun bindAssistantService () { val intent = Intent(CommonUtil.getApp(), AssistantService::class .java) bindService(intent, serviceConnection, BIND_AUTO_CREATE) } }
创建辅助服务 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 public class AssistantService extends Service { @Nullable @Override public IBinder onBind (Intent intent) { return assistantStub; } @Override public void onCreate () { super .onCreate(); Log.d("zfr" , "onCreate AssistantService" ); Notification notification = new Notification (); notification.flags = Notification.FLAG_ONGOING_EVENT; notification.flags |= Notification.FLAG_NO_CLEAR; notification.flags |= Notification.FLAG_FOREGROUND_SERVICE; bindKeepAliveService(); } @Override public int onStartCommand (Intent intent, int flags, int startId) { return Service.START_STICKY; } @Override public void onDestroy () { Log.d("zfr" , "Assistant Destroy" ); try { Intent intent = new Intent (CommonUtil.INSTANCE.getApp(), AssistantService.class); startService(intent); } catch (Throwable tr) { } super .onDestroy(); } @Override public boolean onUnbind (Intent intent) { Log.d("zfr" , "Assistant onUnbind" ); return super .onUnbind(intent); } private IKeepAlive iKeepAlive; private IKeepAlive.Stub assistantStub = new IKeepAlive .Stub() { @Override public String getName () throws RemoteException { return "我是 AssistantService" ; } }; private ServiceConnection serviceConnection = new ServiceConnection () { @Override public void onServiceConnected (ComponentName name, IBinder service) { iKeepAlive = IKeepAlive.Stub.asInterface(service); if (iKeepAlive != null ) { try { Log.d("zfr" , "收到KeepAliveService的数据:name=" + iKeepAlive.getName()); } catch (RemoteException e) { e.printStackTrace(); } } } @Override public void onServiceDisconnected (ComponentName name) { bindKeepAliveService(); } }; private void bindKeepAliveService () { try { Intent clientIntent = new Intent (CommonUtil.INSTANCE.getApp(), BatteryKeepService.class); bindService(clientIntent, serviceConnection, Context.BIND_AUTO_CREATE); } catch (Throwable tr) { } } }
返回参数含义:orange
START_STICKY:orange 在Service被关闭后,重新开启Service
START_NOT_STICKY:服务被异常杀掉后,系统将会被设置为started状态,系统不会重启该服务,直到startService(Intent intent)
方法再次被调用。 START_REDELIVER_INTENT:重传Intent,使用这个返回值时,如果在执行完onStartCommand后,服务被异常kill掉,系统会自动重启该服务,并将Intent的值传入。 START_STICKY_COMPATIBILITY:START_STICKY的兼容版本,但不保证服务被kill后一定能重启。
在AndroidManifest
中添加Service和权限 1 2 3 4 <service android:name ="com.atom.battery.support.keepalive.AliveJobService" android:exported ="true" android:permission ="android.permission.BIND_JOB_SERVICE" />
JobService
代码 (具体就是为了保证当app被杀死时重新启动服务) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public class AliveJobService extends JobService { private final static String TAG = "zfr" ; private volatile static Service mKeepAliveService = null ; public static boolean isJobServiceAlive () { return mKeepAliveService != null ; } private static final int MESSAGE_ID_TASK = 0x01 ; private Handler mHandler = new Handler (new Handler .Callback() { @Override public boolean handleMessage (Message msg) { if (AppUtil.isAppRunning(CommonUtil.INSTANCE.getApp(), CommonUtil.INSTANCE.getApp().getPackageName())) { Log.d(TAG, "KeepAliveService----->APP活着的..." ); } else { Intent alive = new Intent (getApplicationContext(), BatteryKeepService.class); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { startForegroundService(alive); } else { startService(alive); } Log.d(TAG, "KeepAliveService----->APP被杀死,重启..." ); } jobFinished((JobParameters) msg.obj, false ); return true ; } }); @Override public boolean onStartJob (JobParameters params) { Log.d(TAG, "KeepAliveService----->JobService服务被启动..." ); mKeepAliveService = this ; Message msg = Message.obtain(mHandler, MESSAGE_ID_TASK, params); mHandler.sendMessage(msg); return true ; } @Override public boolean onStopJob (JobParameters params) { mHandler.removeMessages(MESSAGE_ID_TASK); Log.d(TAG, "KeepAliveService----->JobService服务被关闭" ); return false ; } }
JobSchedulerManger
管理类代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 public class JobSchedulerManager { private static final int JOB_ID = 1 ; private static JobSchedulerManager mJobManager; private JobScheduler mJobScheduler; private static Context mContext; @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private JobSchedulerManager (Context ctxt) { this .mContext = ctxt; mJobScheduler = (JobScheduler)ctxt.getSystemService(Context.JOB_SCHEDULER_SERVICE); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public final static JobSchedulerManager getJobSchedulerInstance (Context ctxt) { if (mJobManager == null ){ mJobManager = new JobSchedulerManager (ctxt); } return mJobManager; } @TargetApi(21) public void startJobScheduler () { if (AliveJobService.isJobServiceAlive() || isBelowLOLLIPOP()){ return ; } JobInfo.Builder builder = new JobInfo .Builder(JOB_ID,new ComponentName (mContext, AliveJobService.class)); builder.setPeriodic(30_000 ); builder.setPersisted(true ); builder.setRequiresCharging(true ); JobInfo info = builder.build(); mJobScheduler.schedule(info); } @TargetApi(21) public void stopJobScheduler () { if (isBelowLOLLIPOP()) return ; mJobScheduler.cancelAll(); } private boolean isBelowLOLLIPOP () { return Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP; } }
引入第三方库KeepAlive
保活 在Application中
初始化 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private fun initKeepAlive (base: Context ) { val configs = KeepAliveConfigs(KeepAliveConfigs.Config(base.packageName + ":resident" , KeepService::class .java.canonicalName)) configs.setOnBootReceivedListener { context, intent -> Timber.d("onReceive boot" ) try { context.startService(Intent(context, KeepService::class .java)) } catch (e: Exception) { } } KeepAlive.init (base, configs) }
创建保活服务用于检查主服务是否死了 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 public class KeepService extends KeepAliveService_2 { private static final String TAG = KeepService.class.getSimpleName(); private FlowableEmitter<Integer> mEmitter; private Subscription mSubscription; @Override public void onCreate () { super .onCreate(); Timber.tag(TAG).d("KeepService--onCreate..." ); } @Override public int onStartCommand (Intent intent, int flags, int startId) { int result = START_STICKY; Timber.tag(TAG).d("onStartCommand--%d" ,startId); boolean agreed = (boolean ) SPUtil.get(CommonUtil.INSTANCE.getApp(), SpConstant.INSTANCE.POLICY_AGREE,false ); if (!agreed) return result; if (mEmitter != null ){ mEmitter.onNext(startId); }else { checkKeepAliveService(); } return result; } private void checkKeepAliveService () { try { Flowable.create(new FlowableOnSubscribe <Integer>() { @Override public void subscribe (@NonNull FlowableEmitter<Integer> emitter) { mEmitter = emitter; } }, BackpressureStrategy.LATEST) .map(new Function <Integer, Integer>() { @Override public Integer apply (@NonNull Integer integer) { try { ActivityManager activityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE); if (activityManager != null ) { List<ActivityManager.RunningServiceInfo> runningServices = activityManager.getRunningServices(Integer.MAX_VALUE); if (runningServices.size() < 0 ) { return 0 ; } for (int i = 0 ; i < runningServices.size(); i++) { ComponentName service = runningServices.get(i).service; if (service.getClassName().equals(BatteryKeepService.class.getName())) { return 0 ; } } return 1 ; } }finally { return 0 ; } } }).compose(RxUtil.INSTANCE.ioAndMainFlowable()) .subscribe(new FlowableSubscriber <Integer>() { @Override public void onSubscribe (@NonNull Subscription s) { mSubscription = s; mSubscription.request(1 ); } @Override public void onNext (Integer integer) { Timber.tag(TAG).d("onNext--%d" ,integer); try { if (integer == 1 ){ Timber.tag(TAG).d("KeepAliveService死了---准备拉起。。。" ); Intent alive = new Intent (KeepService.this , BatteryKeepService.class); ContextCompat.startForegroundService(KeepService.this ,alive); } }finally { mSubscription.request(1 ); } } @Override public void onError (Throwable t) { Timber.tag(TAG).e(t); } @Override public void onComplete () { } }); }catch (Exception e){} } @Override public void onDestroy () { super .onDestroy(); if (mSubscription != null ){ mSubscription.cancel(); } mEmitter = null ; mSubscription = null ; } }
确保App保活需要的其他操作 开启忽略电池优化权限 1 2 <uses-permission android:name ="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
开启无障碍服务 1 2 3 <uses-permission android:name ="android.permission.BIND_ACCESSIBILITY_SERVICE" tools:ignore ="ProtectedPermissions" />
还需要创建服务并开启
1 2 3 4 5 6 7 8 9 10 11 12 13 14 class BatteryAccessibilityService : AccessibilityService () { override fun onServiceConnected () { super .onServiceConnected() } override fun onUnbind (intent: Intent ) : Boolean { return super .onUnbind(intent) } override fun onAccessibilityEvent (event: AccessibilityEvent ) {} override fun onInterrupt () {} }
开启自启动权限 使用第三方工具类,让用户开启