Android 8.0以上系统应用如何保活
最近在做一个埋点的sdk,由于埋点是分批上传的,不是每次都上传,所以会有个进程保活的机制,这也是自研推送的实现技术之一:如何保证Android进程的存活。
对于Android来说,保活主要有以下一些方法:
- 开启前台Service(效果好,推荐)
- Service中循环播放一段无声音频(效果较好,但耗电量高,谨慎使用)
- 双进程守护(Android 5.0前有效)
- JobScheduler(Android 5.0后引入,8.0后失效)
- 1 像素activity保活方案(不推荐)
- 广播锁屏、自定义锁屏(不推荐)
- 第三方推送SDK唤醒(效果好,缺点是第三方接入)
下面是具体的实现方案:
1.监听锁屏广播,开启1个像素的Activity
最早见到这种方案的时候是2015年,有个FM的app为了向投资人展示月活,在Android应用中开启一个1像素的Activity。
由于Activity的级别是比较高的,所以开启1个像素的Activity的方式就可以保证进程是不容易被杀掉的。
具体来说,定义一个1像素的Activity,在该Activity中动态注册自定义的广播。
class OnePixelActivity : AppCompatActivity() {private lateinit var br: BroadcastReceiveroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)//设定一像素的activityval window = windowwindow.setGravity(Gravity.LEFT or Gravity.TOP)val params = window.attributesparams.x = 0params.y = 0params.height = 1params.width = 1window.attributes = params//在一像素activity里注册广播接受者 接受到广播结束掉一像素br = object : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {finish()}}registerReceiver(br, IntentFilter("finish activity"))checkScreenOn()}override fun onResume() {super.onResume()checkScreenOn()}override fun onDestroy() {try {//销毁的时候解锁广播unregisterReceiver(br)} catch (e: IllegalArgumentException) {}super.onDestroy()}/*** 检查屏幕是否点亮*/private fun checkScreenOn() {val pm = this@OnePixelActivity.getSystemService(Context.POWER_SERVICE) as PowerManagerval isScreenOn = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {pm.isInteractive} else {pm.isScreenOn}if (isScreenOn) {finish()}}
}
2, 双进程守护
双进程守护,在Android 5.0前是有效的,5.0之后就不行了。首先,我们定义定义一个本地服务,在该服务中播放无声音乐,并绑定远程服务
class LocalService : Service() {private var mediaPlayer: MediaPlayer? = nullprivate var mBilder: MyBilder? = nulloverride fun onCreate() {super.onCreate()if (mBilder == null) {mBilder = MyBilder()}}override fun onBind(intent: Intent): IBinder? {return mBilder}override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {//播放无声音乐if (mediaPlayer == null) {mediaPlayer = MediaPlayer.create(this, R.raw.novioce)//声音设置为0mediaPlayer?.setVolume(0f, 0f)mediaPlayer?.isLooping = true//循环播放play()}//启用前台服务,提升优先级if (KeepLive.foregroundNotification != null) {val intent2 = Intent(applicationContext, NotificationClickReceiver::class.java)intent2.action = NotificationClickReceiver.CLICK_NOTIFICATIONval notification = NotificationUtils.createNotification(this, KeepLive.foregroundNotification!!.getTitle(), KeepLive.foregroundNotification!!.getDescription(), KeepLive.foregroundNotification!!.getIconRes(), intent2)startForeground(13691, notification)}//绑定守护进程try {val intent3 = Intent(this, RemoteService::class.java)this.bindService(intent3, connection, Context.BIND_ABOVE_CLIENT)} catch (e: Exception) {}//隐藏服务通知try {if (Build.VERSION.SDK_INT < 25) {startService(Intent(this, HideForegroundService::class.java))}} catch (e: Exception) {}if (KeepLive.keepLiveService != null) {KeepLive.keepLiveService!!.onWorking()}return Service.START_STICKY}private fun play() {if (mediaPlayer != null && !mediaPlayer!!.isPlaying) {mediaPlayer?.start()}}private inner class MyBilder : GuardAidl.Stub() {@Throws(RemoteException::class)override fun wakeUp(title: String, discription: String, iconRes: Int) {}}private val connection = object : ServiceConnection {override fun onServiceDisconnected(name: ComponentName) {val remoteService = Intent(this@LocalService,RemoteService::class.java)this@LocalService.startService(remoteService)val intent = Intent(this@LocalService, RemoteService::class.java)this@LocalService.bindService(intent, this,Context.BIND_ABOVE_CLIENT)}override fun onServiceConnected(name: ComponentName, service: IBinder) {try {if (mBilder != null && KeepLive.foregroundNotification != null) {val guardAidl = GuardAidl.Stub.asInterface(service)guardAidl.wakeUp(KeepLive.foregroundNotification?.getTitle(), KeepLive.foregroundNotification?.getDescription(), KeepLive.foregroundNotification!!.getIconRes())}} catch (e: RemoteException) {e.printStackTrace()}}}override fun onDestroy() {super.onDestroy()unbindService(connection)if (KeepLive.keepLiveService != null) {KeepLive.keepLiveService?.onStop()}}
}
然后再定义一个远程服务,绑定本地服务。
class RemoteService : Service() {private var mBilder: MyBilder? = nulloverride fun onCreate() {super.onCreate()if (mBilder == null) {mBilder = MyBilder()}}override fun onBind(intent: Intent): IBinder? {return mBilder}override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {try {this.bindService(Intent(this@RemoteService, LocalService::class.java),connection, Context.BIND_ABOVE_CLIENT)} catch (e: Exception) {}return Service.START_STICKY}override fun onDestroy() {super.onDestroy()unbindService(connection)}private inner class MyBilder : GuardAidl.Stub() {@Throws(RemoteException::class)override fun wakeUp(title: String, discription: String, iconRes: Int) {if (Build.VERSION.SDK_INT < 25) {val intent = Intent(applicationContext, NotificationClickReceiver::class.java)intent.action = NotificationClickReceiver.CLICK_NOTIFICATIONval notification = NotificationUtils.createNotification(this@RemoteService, title, discription, iconRes, intent)this@RemoteService.startForeground(13691, notification)}}}private val connection = object : ServiceConnection {override fun onServiceDisconnected(name: ComponentName) {val remoteService = Intent(this@RemoteService,LocalService::class.java)this@RemoteService.startService(remoteService)this@RemoteService.bindService(Intent(this@RemoteService,LocalService::class.java), this, Context.BIND_ABOVE_CLIENT)}override fun onServiceConnected(name: ComponentName, service: IBinder) {}}}/*** 通知栏点击广播接受者*/
class NotificationClickReceiver : BroadcastReceiver() {companion object {const val CLICK_NOTIFICATION = "CLICK_NOTIFICATION"}override fun onReceive(context: Context, intent: Intent) {if (intent.action == NotificationClickReceiver.CLICK_NOTIFICATION) {if (KeepLive.foregroundNotification != null) {if (KeepLive.foregroundNotification!!.getForegroundNotificationClickListener() != null) {KeepLive.foregroundNotification!!.getForegroundNotificationClickListener()?.foregroundNotificationClick(context, intent)}}}}
}
3,JobScheduler
JobScheduler是Android从5.0增加的支持一种特殊的任务调度机制,可以用它来实现进程保活,不过在Android8.0系统中,此种方法也失效。
首先,我们定义一个JobService,开启本地服务和远程服务。
@SuppressWarnings(value = ["unchecked", "deprecation"])
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
class JobHandlerService : JobService() {private var mJobScheduler: JobScheduler? = nulloverride fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {var startId = startIdstartService(this)if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {mJobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobSchedulerval builder = JobInfo.Builder(startId++,ComponentName(packageName, JobHandlerService::class.java.name))if (Build.VERSION.SDK_INT >= 24) {builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS) //执行的最小延迟时间builder.setOverrideDeadline(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS) //执行的最长延时时间builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS)builder.setBackoffCriteria(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS, JobInfo.BACKOFF_POLICY_LINEAR)//线性重试方案} else {builder.setPeriodic(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS)}builder.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)builder.setRequiresCharging(true) // 当插入充电器,执行该任务mJobScheduler?.schedule(builder.build())}return Service.START_STICKY}private fun startService(context: Context) {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {if (KeepLive.foregroundNotification != null) {val intent = Intent(applicationContext, NotificationClickReceiver::class.java)intent.action = NotificationClickReceiver.CLICK_NOTIFICATIONval notification = NotificationUtils.createNotification(this, KeepLive.foregroundNotification!!.getTitle(), KeepLive.foregroundNotification!!.getDescription(), KeepLive.foregroundNotification!!.getIconRes(), intent)startForeground(13691, notification)}}//启动本地服务val localIntent = Intent(context, LocalService::class.java)//启动守护进程val guardIntent = Intent(context, RemoteService::class.java)startService(localIntent)startService(guardIntent)}override fun onStartJob(jobParameters: JobParameters): Boolean {if (!isServiceRunning(applicationContext, "com.xiyang51.keeplive.service.LocalService") || !isServiceRunning(applicationContext, "$packageName:remote")) {startService(this)}return false}override fun onStopJob(jobParameters: JobParameters): Boolean {if (!isServiceRunning(applicationContext, "com.xiyang51.keeplive.service.LocalService") || !isServiceRunning(applicationContext, "$packageName:remote")) {startService(this)}return false}private fun isServiceRunning(ctx: Context, className: String): Boolean {var isRunning = falseval activityManager = ctx.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManagerval servicesList = activityManager.getRunningServices(Integer.MAX_VALUE)val l = servicesList.iterator()while (l.hasNext()) {val si = l.next()if (className == si.service.className) {isRunning = true}}return isRunning}
}
4,提高Service优先级
在onStartCommand()方法中开启一个通知,提高进程的优先级。注意:从Android 8.0(API级别26)开始,所有通知必须要分配一个渠道,对于每个渠道,可以单独设置视觉和听觉行为。然后用户可以在设置中修改这些设置,根据应用程序来决定哪些通知可以显示或者隐藏。
首先,定义一个通知工具类,此工具栏兼容Android 8.0。
class NotificationUtils(context: Context) : ContextWrapper(context) {private var manager: NotificationManager? = nullprivate var id: String = context.packageName + "51"private var name: String = context.packageNameprivate var context: Context = contextprivate var channel: NotificationChannel? = nullcompanion object {@SuppressLint("StaticFieldLeak")private var notificationUtils: NotificationUtils? = nullfun createNotification(context: Context, title: String, content: String, icon: Int, intent: Intent): Notification? {if (notificationUtils == null) {notificationUtils = NotificationUtils(context)}var notification: Notification? = nullnotification = if (Build.VERSION.SDK_INT >= 26) {notificationUtils?.createNotificationChannel()notificationUtils?.getChannelNotification(title, content, icon, intent)?.build()} else {notificationUtils?.getNotification_25(title, content, icon, intent)?.build()}return notification}}@RequiresApi(api = Build.VERSION_CODES.O)fun createNotificationChannel() {if (channel == null) {channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_MIN)channel?.enableLights(false)channel?.enableVibration(false)channel?.vibrationPattern = longArrayOf(0)channel?.setSound(null, null)getManager().createNotificationChannel(channel)}}private fun getManager(): NotificationManager {if (manager == null) {manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager}return manager!!}@RequiresApi(api = Build.VERSION_CODES.O)fun getChannelNotification(title: String, content: String, icon: Int, intent: Intent): Notification.Builder {//PendingIntent.FLAG_UPDATE_CURRENT 这个类型才能传值val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)return Notification.Builder(context, id).setContentTitle(title).setContentText(content).setSmallIcon(icon).setAutoCancel(true).setContentIntent(pendingIntent)}fun getNotification_25(title: String, content: String, icon: Int, intent: Intent): NotificationCompat.Builder {val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)return NotificationCompat.Builder(context, id).setContentTitle(title).setContentText(content).setSmallIcon(icon).setAutoCancel(true).setVibrate(longArrayOf(0)).setSound(null).setLights(0, 0, 0).setContentIntent(pendingIntent)}
}
5,Workmanager方式
Workmanager是Android JetPac中的一个API,借助Workmanager,我们可以用它来实现应用饿保活。使用前,我们需要依赖Workmanager库,如下:
implementation "android.arch.work:work-runtime:1.0.0-alpha06"
Worker是一个抽象类,用来指定需要执行的具体任务。
public class KeepLiveWork extends Worker {private static final String TAG = "KeepLiveWork";@NonNull@Overridepublic WorkerResult doWork() {Log.d(TAG, "keep-> doWork: startKeepService");//启动job服务startJobService();//启动相互绑定的服务startKeepService();return WorkerResult.SUCCESS;}
}
然后,启动keepWork方法,
public void startKeepWork() {WorkManager.getInstance().cancelAllWorkByTag(TAG_KEEP_WORK);Log.d(TAG, "keep-> dowork startKeepWork");OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(KeepLiveWork.class).setBackoffCriteria(BackoffPolicy.LINEAR, 5, TimeUnit.SECONDS).addTag(TAG_KEEP_WORK).build();WorkManager.getInstance().enqueue(oneTimeWorkRequest);}
关于WorkManager,可以通过下面的文章来详细了解:WorkManager浅谈
Android 8.0以上系统应用如何保活相关推荐
- Android 4.0.4系统下实现apk的静默安装和启动
转自http://www.linuxidc.com/Linux/2013-02/79403.htm 未亲测 最近在Android 4.0.4系统下实现apk的静默安装和启动的功能,这里和大家分享一下, ...
- android 7原生动态壁纸,手机里都是定制OS?谷歌Android 7.0原生系统壁纸邀你来尝鲜!...
原标题:手机里都是定制OS?谷歌Android 7.0原生系统壁纸邀你来尝鲜! 安卓系统的碎片化一直是被人们所诟病,苹果手机IOS系统升级的速度和占比一直都非常高,而安卓手机却相比都非常差,三星,华为 ...
- 小米刷 android o,一代神机!五年前的小米2刷入Android 8.0原生系统
五年前的小米手机2竟然能刷Android 8.0原生系统.做个比较,2012年推出的iPhone 5已经不能升级最新的iOS11了. 小米2是小米于2012年8月发布的一款产品,距今已经超过5年零三个 ...
- tiny4412开发板实现uboot引导启动android 5.0.2系统
1目的 实现uboot引导启动android 5.0.2系统. 2 现有环境 友善之臂tiny 4412开发板,superboot引导启动android5.0.2系统,内核版本为linux3.0.8. ...
- Android 9.0 显示系统导航栏(左/右/底部)
Android 9.0 显示系统导航栏(左/右/底部) 显示系统导航栏 一般有:HOME BACK 最近应用列表 等组成. 先看下效果图: 显示在底部(最常见的做法): 显示在右边(平板): (请 ...
- android 8.0 调系统拍照_Android通知栏微技巧,8.0系统中通知栏的适配
为什么要进行通知栏适配? 不得不说,通知栏真是一个让人又爱又恨的东西. 通知栏是Android系统原创的一个功能,虽说乔布斯一直认为Android系统是彻彻底底抄袭iOS的一个产品,但是通知栏确实是A ...
- android4.0 菜单,Android 4.0.4系统曝光 增新Power菜单
[IT168 资讯]尽管Android4.0.3已经推出很久了,但是目前为止,三星Galaxy Nexus运行的仍旧是Android4.0.2的系统更新,与4.0.3相比,4.0.2版本不仅应用界面不 ...
- Android 最新 ios 11,差异性越来越小!iOS 11/Android 8.0最新系统对比
北京时间9月13日凌晨1点,苹果在他的新飞船总部举办新品发布会.会上除了发布了一系列的智能设备外,更是发布了iOS 11操作系统的正式版本.iOS 11版本在今年6月份的苹果开发者大会正式亮相,但是之 ...
- nexus+7+android+5.0++wifi+代理,二代Nexus 7获Android 5.0.2系统更新
据外媒Android Police报道,谷歌日前已经正式开始推送Android 5.0.2更新,作为亲儿子的Nexus 7(2013)已经推送,Nexus 10的更新也即将到来 日前,据外媒Andro ...
最新文章
- 让我去健身的不是漂亮小姐姐,居然是贝叶斯统计!
- linux桌面天气,Ubuntu桌面美化:添加Gmail +天气预报插件[图文]
- oracle如何复制表的索引,Oracle表与索引管理
- 36--斐波那契数列
- splunk中 如何隐藏input_翻糖制作中,如何避免裂缝,如何隐藏裂缝,如何防粘?...
- 【今日CS 视觉论文速览】Part2, 16 Jan 2019
- java textfield方法,Java TextField求教育
- 手把手教你在友善之臂tiny4412上用uboot启动Linux内核
- 我能想到的圆角背景的实现方法
- php 多级分成手机版,PHP 层级菜单数组处理,由一级数组转换为多级数组的递归实现...
- EIGRP路由协议实现网络互联
- 计算机管理磁盘分区,一分钟搞定电脑磁盘分区,再也不求人!
- SQL防注入大全——史上最全的 SQL 注入资料
- 源代码静态检测分析技术浅析
- 下载并安装windows版本的Redis
- 基于FPGA扰码的实现
- 海信电视云账号连不上服务器,海信云账号如何使用?图文教程详解
- A、B、H、S、N股 各是什么意思
- i710700和i510400f哪个好
- GF无菌动物实验室设计SICOLAB