详解Android 8.0以上系统应用如何保活

前端之家收集整理的这篇文章主要介绍了详解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中动态注册自定义的广播。

  1. class OnePixelActivity : AppCompatActivity() {
  2.  
  3. private lateinit var br: BroadcastReceiver
  4.  
  5. override fun onCreate(savedInstanceState: Bundle?) {
  6. super.onCreate(savedInstanceState)
  7. //设定一像素的activity
  8. val window = window
  9. window.setGravity(Gravity.LEFT or Gravity.TOP)
  10. val params = window.attributes
  11. params.x = 0
  12. params.y = 0
  13. params.height = 1
  14. params.width = 1
  15. window.attributes = params
  16. //在一像素activity里注册广播接受者 接受到广播结束掉一像素
  17. br = object : BroadcastReceiver() {
  18. override fun onReceive(context: Context,intent: Intent) {
  19. finish()
  20. }
  21. }
  22. registerReceiver(br,IntentFilter("finish activity"))
  23. checkScreenOn()
  24. }
  25.  
  26. override fun onResume() {
  27. super.onResume()
  28. checkScreenOn()
  29. }
  30.  
  31. override fun onDestroy() {
  32. try {
  33. //销毁的时候解锁广播
  34. unregisterReceiver(br)
  35. } catch (e: IllegalArgumentException) {
  36. }
  37. super.onDestroy()
  38. }
  39.  
  40. /**
  41. * 检查屏幕是否点亮
  42. */
  43. private fun checkScreenOn() {
  44. val pm = this@OnePixelActivity.getSystemService(Context.POWER_SERVICE) as PowerManager
  45. val isScreenOn = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT_WATCH) {
  46. pm.isInteractive
  47. } else {
  48. pm.isScreenOn
  49. }
  50. if (isScreenOn) {
  51. finish()
  52. }
  53. }
  54. }

2, 双进程守护

双进程守护,在Android 5.0前是有效的,5.0之后就不行了。首先,我们定义定义一个本地服务,在该服务中播放无声音乐,并绑定远程服务

  1. class LocalService : Service() {
  2. private var mediaPlayer: MediaPlayer? = null
  3. private var mBilder: MyBilder? = null
  4.  
  5. override fun onCreate() {
  6. super.onCreate()
  7. if (mBilder == null) {
  8. mBilder = MyBilder()
  9. }
  10. }
  11.  
  12. override fun onBind(intent: Intent): IBinder? {
  13. return mBilder
  14. }
  15.  
  16. override fun onStartCommand(intent: Intent,flags: Int,startId: Int): Int {
  17. //播放无声音乐
  18. if (mediaPlayer == null) {
  19. mediaPlayer = MediaPlayer.create(this,R.raw.novioce)
  20. //声音设置为0
  21. mediaPlayer?.setVolume(0f,0f)
  22. mediaPlayer?.isLooping = true//循环播放
  23. play()
  24. }
  25. //启用前台服务,提升优先级
  26. if (KeepLive.foregroundNotification != null) {
  27. val intent2 = Intent(applicationContext,NotificationClickReceiver::class.java)
  28. intent2.action = NotificationClickReceiver.CLICK_NOTIFICATION
  29. val notification = NotificationUtils.createNotification(this,KeepLive.foregroundNotification!!.getTitle(),KeepLive.foregroundNotification!!.getDescription(),KeepLive.foregroundNotification!!.getIconRes(),intent2)
  30. startForeground(13691,notification)
  31. }
  32. //绑定守护进程
  33. try {
  34. val intent3 = Intent(this,RemoteService::class.java)
  35. this.bindService(intent3,connection,Context.BIND_ABOVE_CLIENT)
  36. } catch (e: Exception) {
  37. }
  38.  
  39. //隐藏服务通知
  40. try {
  41. if (Build.VERSION.SDK_INT < 25) {
  42. startService(Intent(this,HideForegroundService::class.java))
  43. }
  44. } catch (e: Exception) {
  45. }
  46.  
  47. if (KeepLive.keepLiveService != null) {
  48. KeepLive.keepLiveService!!.onWorking()
  49. }
  50. return Service.START_STICKY
  51. }
  52.  
  53. private fun play() {
  54. if (mediaPlayer != null && !mediaPlayer!!.isPlaying) {
  55. mediaPlayer?.start()
  56. }
  57. }
  58.  
  59. private inner class MyBilder : GuardAidl.Stub() {
  60.  
  61. @Throws(RemoteException::class)
  62. override fun wakeUp(title: String,discription: String,iconRes: Int) {
  63.  
  64. }
  65. }
  66.  
  67. private val connection = object : ServiceConnection {
  68.  
  69. override fun onServiceDisconnected(name: ComponentName) {
  70. val remoteService = Intent(this@LocalService,RemoteService::class.java)
  71. this@LocalService.startService(remoteService)
  72. val intent = Intent(this@LocalService,RemoteService::class.java)
  73. this@LocalService.bindService(intent,this,Context.BIND_ABOVE_CLIENT)
  74. }
  75.  
  76. override fun onServiceConnected(name: ComponentName,service: IBinder) {
  77. try {
  78. if (mBilder != null && KeepLive.foregroundNotification != null) {
  79. val guardAidl = GuardAidl.Stub.asInterface(service)
  80. guardAidl.wakeUp(KeepLive.foregroundNotification?.getTitle(),KeepLive.foregroundNotification?.getDescription(),KeepLive.foregroundNotification!!.getIconRes())
  81. }
  82. } catch (e: RemoteException) {
  83. e.printStackTrace()
  84. }
  85.  
  86. }
  87. }
  88.  
  89. override fun onDestroy() {
  90. super.onDestroy()
  91. unbindService(connection)
  92. if (KeepLive.keepLiveService != null) {
  93. KeepLive.keepLiveService?.onStop()
  94. }
  95. }
  96. }

然后再定义一个远程服务,绑定本地服务。

  1. class RemoteService : Service() {
  2.  
  3. private var mBilder: MyBilder? = null
  4.  
  5. override fun onCreate() {
  6. super.onCreate()
  7. if (mBilder == null) {
  8. mBilder = MyBilder()
  9. }
  10. }
  11.  
  12. override fun onBind(intent: Intent): IBinder? {
  13. return mBilder
  14. }
  15.  
  16. override fun onStartCommand(intent: Intent,startId: Int): Int {
  17. try {
  18. this.bindService(Intent(this@RemoteService,LocalService::class.java),Context.BIND_ABOVE_CLIENT)
  19. } catch (e: Exception) {
  20. }
  21. return Service.START_STICKY
  22. }
  23.  
  24. override fun onDestroy() {
  25. super.onDestroy()
  26. unbindService(connection)
  27. }
  28.  
  29. private inner class MyBilder : GuardAidl.Stub() {
  30. @Throws(RemoteException::class)
  31. override fun wakeUp(title: String,iconRes: Int) {
  32. if (Build.VERSION.SDK_INT < 25) {
  33. val intent = Intent(applicationContext,NotificationClickReceiver::class.java)
  34. intent.action = NotificationClickReceiver.CLICK_NOTIFICATION
  35. val notification = NotificationUtils.createNotification(this@RemoteService,title,discription,iconRes,intent)
  36. this@RemoteService.startForeground(13691,notification)
  37. }
  38. }
  39. }
  40.  
  41. private val connection = object : ServiceConnection {
  42. override fun onServiceDisconnected(name: ComponentName) {
  43. val remoteService = Intent(this@RemoteService,LocalService::class.java)
  44. this@RemoteService.startService(remoteService)
  45. this@RemoteService.bindService(Intent(this@RemoteService,service: IBinder) {}
  46. }
  47.  
  48. }
  49.  
  50. /**
  51. * 通知栏点击广播接受者
  52. */
  53. class NotificationClickReceiver : BroadcastReceiver() {
  54.  
  55. companion object {
  56. const val CLICK_NOTIFICATION = "CLICK_NOTIFICATION"
  57. }
  58.  
  59. override fun onReceive(context: Context,intent: Intent) {
  60. if (intent.action == NotificationClickReceiver.CLICK_NOTIFICATION) {
  61. if (KeepLive.foregroundNotification != null) {
  62. if (KeepLive.foregroundNotification!!.getForegroundNotificationClickListener() != null) {
  63. KeepLive.foregroundNotification!!.getForegroundNotificationClickListener()?.foregroundNotificationClick(context,intent)
  64. }
  65. }
  66. }
  67. }
  68. }

3,JobScheduler

JobScheduler是Android从5.0增加支持一种特殊的任务调度机制,可以用它来实现进程保活,不过在Android8.0系统中,此种方法也失效。

首先,我们定义一个JobService,开启本地服务和远程服务。

  1. @SuppressWarnings(value = ["unchecked","deprecation"])
  2. @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
  3. class JobHandlerService : JobService() {
  4.  
  5. private var mJobScheduler: JobScheduler? = null
  6.  
  7. override fun onStartCommand(intent: Intent?,startId: Int): Int {
  8. var startId = startId
  9. startService(this)
  10. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  11. mJobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE) as JobScheduler
  12. val builder = JobInfo.Builder(startId++,ComponentName(packageName,JobHandlerService::class.java.name))
  13. if (Build.VERSION.SDK_INT >= 24) {
  14. builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS) //执行的最小延迟时间
  15. builder.setOverrideDeadline(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS) //执行的最长延时时间
  16. builder.setMinimumLatency(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS)
  17. builder.setBackoffCriteria(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS,JobInfo.BACKOFF_POLICY_LINEAR)//线性重试方案
  18. } else {
  19. builder.setPeriodic(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS)
  20. }
  21. builder.setrequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
  22. builder.setRequiresCharging(true) // 当插入充电器,执行该任务
  23. mJobScheduler?.schedule(builder.build())
  24. }
  25. return Service.START_STICKY
  26. }
  27.  
  28. private fun startService(context: Context) {
  29. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
  30. if (KeepLive.foregroundNotification != null) {
  31. val intent = Intent(applicationContext,NotificationClickReceiver::class.java)
  32. intent.action = NotificationClickReceiver.CLICK_NOTIFICATION
  33. val notification = NotificationUtils.createNotification(this,intent)
  34. startForeground(13691,notification)
  35. }
  36. }
  37. //启动本地服务
  38. val localIntent = Intent(context,LocalService::class.java)
  39. //启动守护进程
  40. val guardIntent = Intent(context,RemoteService::class.java)
  41. startService(localIntent)
  42. startService(guardIntent)
  43. }
  44.  
  45. override fun onStartJob(jobParameters: JobParameters): Boolean {
  46. if (!isServiceRunning(applicationContext,"com.xiyang51.keeplive.service.LocalService") || !isServiceRunning(applicationContext,"$packageName:remote")) {
  47. startService(this)
  48. }
  49. return false
  50. }
  51.  
  52. override fun onStopJob(jobParameters: JobParameters): Boolean {
  53. if (!isServiceRunning(applicationContext,"$packageName:remote")) {
  54. startService(this)
  55. }
  56. return false
  57. }
  58.  
  59. private fun isServiceRunning(ctx: Context,className: String): Boolean {
  60. var isRunning = false
  61. val activityManager = ctx
  62. .getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
  63. val servicesList = activityManager
  64. .getRunningServices(Integer.MAX_VALUE)
  65. val l = servicesList.iterator()
  66. while (l.hasNext()) {
  67. val si = l.next()
  68. if (className == si.service.className) {
  69. isRunning = true
  70. }
  71. }
  72. return isRunning
  73. }
  74. }

4,提高Service优先级

在onStartCommand()方法中开启一个通知,提高进程的优先级。注意:从Android 8.0(API级别26)开始,所有通知必须要分配一个渠道,对于每个渠道,可以单独设置视觉和听觉行为。然后用户可以在设置中修改这些设置,根据应用程序来决定哪些通知可以显示或者隐藏。

首先,定义一个通知工具类,此工具栏兼容Android 8.0。

  1. class NotificationUtils(context: Context) : ContextWrapper(context) {
  2.  
  3. private var manager: NotificationManager? = null
  4. private var id: String = context.packageName + "51"
  5. private var name: String = context.packageName
  6. private var context: Context = context
  7. private var channel: NotificationChannel? = null
  8.  
  9. companion object {
  10. @SuppressLint("StaticFieldLeak")
  11. private var notificationUtils: NotificationUtils? = null
  12.  
  13. fun createNotification(context: Context,title: String,content: String,icon: Int,intent: Intent): Notification? {
  14. if (notificationUtils == null) {
  15. notificationUtils = NotificationUtils(context)
  16. }
  17. var notification: Notification? = null
  18. notification = if (Build.VERSION.SDK_INT >= 26) {
  19. notificationUtils?.createNotificationChannel()
  20. notificationUtils?.getChannelNotification(title,content,icon,intent)?.build()
  21. } else {
  22. notificationUtils?.getNotification_25(title,intent)?.build()
  23. }
  24. return notification
  25. }
  26. }
  27.  
  28. @RequiresApi(api = Build.VERSION_CODES.O)
  29. fun createNotificationChannel() {
  30. if (channel == null) {
  31. channel = NotificationChannel(id,name,NotificationManager.IMPORTANCE_MIN)
  32. channel?.enableLights(false)
  33. channel?.enableVibration(false)
  34. channel?.vibrationPattern = longArrayOf(0)
  35. channel?.setSound(null,null)
  36. getManager().createNotificationChannel(channel)
  37. }
  38. }
  39.  
  40. private fun getManager(): NotificationManager {
  41. if (manager == null) {
  42. manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
  43. }
  44. return manager!!
  45. }
  46.  
  47. @RequiresApi(api = Build.VERSION_CODES.O)
  48. fun getChannelNotification(title: String,intent: Intent): Notification.Builder {
  49. //PendingIntent.FLAG_UPDATE_CURRENT 这个类型才能传值
  50. val pendingIntent = PendingIntent.getBroadcast(context,intent,PendingIntent.FLAG_UPDATE_CURRENT)
  51. return Notification.Builder(context,id)
  52. .setContentTitle(title)
  53. .setContentText(content)
  54. .setSmallIcon(icon)
  55. .setAutoCancel(true)
  56. .setContentIntent(pendingIntent)
  57. }
  58.  
  59. fun getNotification_25(title: String,intent: Intent): NotificationCompat.Builder {
  60. val pendingIntent = PendingIntent.getBroadcast(context,PendingIntent.FLAG_UPDATE_CURRENT)
  61. return NotificationCompat.Builder(context,id)
  62. .setContentTitle(title)
  63. .setContentText(content)
  64. .setSmallIcon(icon)
  65. .setAutoCancel(true)
  66. .setVibrate(longArrayOf(0))
  67. .setSound(null)
  68. .setLights(0,0)
  69. .setContentIntent(pendingIntent)
  70. }
  71. }

5,Workmanager方式

Workmanager是Android JetPac中的一个API,借助Workmanager,我们可以用它来实现应用饿保活。使用前,我们需要依赖Workmanager库,如下:

  1. implementation "android.arch.work:work-runtime:1.0.0-alpha06"

Worker是一个抽象类,用来指定需要执行的具体任务。

  1. public class KeepLiveWork extends Worker {
  2. private static final String TAG = "KeepLiveWork";
  3.  
  4. @NonNull
  5. @Override
  6. public WorkerResult doWork() {
  7. Log.d(TAG,"keep-> doWork: startKeepService");
  8. //启动job服务
  9. startJobService();
  10. //启动相互绑定的服务
  11. startKeepService();
  12. return WorkerResult.SUCCESS;
  13. }
  14. }

然后,启动keepWork方法

  1. public void startKeepWork() {
  2. WorkManager.getInstance().cancelAllWorkByTag(TAG_KEEP_WORK);
  3. Log.d(TAG,"keep-> dowork startKeepWork");
  4. OneTimeWorkRequest oneTimeWorkRequest = new OneTimeWorkRequest.Builder(KeepLiveWork.class)
  5. .setBackoffCriteria(BackoffPolicy.LINEAR,5,TimeUnit.SECONDS)
  6. .addTag(TAG_KEEP_WORK)
  7. .build();
  8. WorkManager.getInstance().enqueue(oneTimeWorkRequest);
  9.  
  10. }

关于WorkManager,可以通过下面的文章来详细了解:WorkManager浅谈

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

猜你在找的Android相关文章