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

详解Android 8.0以上系统应用如何保活,第1张

概述详解Android 8.0以上系统应用如何保活 最近在做一个埋点的sdk,由于埋点是分批上传的,不是每次都上传,所以会有个进程保活的机制,这也是自研推送的实现技术之一:如何保证Android进程的存活. 对于Android来说,保活主要有以下一些方法: 开启前台Service(效果好,推荐) Service中循环播放一段无声音频(效果较好,但耗电量高,谨慎使用) 双进程守护(Android 5.0前有效) JobScheduler(Android 5.0后引入,8.0后失效) 1 像素activity保活方案(不推荐) 广播锁屏.自定义锁屏(

最近在做一个埋点的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: broadcastReceiver  overrIDe fun onCreate(savedInstanceState: Bundle?) {    super.onCreate(savedInstanceState)    //设定一像素的activity    val window = window    window.setGravity(Gravity.left or Gravity.top)    val params = window.attributes    params.x = 0    params.y = 0    params.height = 1    params.wIDth = 1    window.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 = [email protected](Context.POWER_SERVICE) as PowerManager    val 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? = null  private var mBilder: MyBilder? = null  overrIDe 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)      //声音设置为0      mediaPlayer?.setVolume(0f,0f)      mediaPlayer?.isLooPing = true//循环播放      play()    }    //启用前台服务,提升优先级    if (Keeplive.foregroundNotification != null) {      val intent2 = Intent(applicationContext,NotificationClickReceiver::class.java)      intent2.action = NotificationClickReceiver.CliCK_NOTIFICATION      val 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)      [email protected](remoteService)      val intent = Intent(this@LocalService,RemoteService::class.java)      [email protected](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? = null  overrIDe fun onCreate() {    super.onCreate()    if (mBilder == null) {      mBilder = MyBilder()    }  }  overrIDe fun onBind(intent: Intent): IBinder? {    return mBilder  }  overrIDe fun onStartCommand(intent: Intent,startID: Int): Int {    try {      this.bindService(Intent(this@RemoteService,LocalService::class.java),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,iconRes: Int) {      if (Build.VERSION.SDK_INT < 25) {        val intent = Intent(applicationContext,NotificationClickReceiver::class.java)        intent.action = NotificationClickReceiver.CliCK_NOTIFICATION        val notification = NotificationUtils.createNotification(this@RemoteService,Title,discription,iconRes,intent)        [email protected](13691,notification)      }    }  }  private val connection = object : ServiceConnection {    overrIDe fun onServicedisconnected(name: Componentname) {      val remoteService = Intent(this@RemoteService,LocalService::class.java)      [email protected](remoteService)      [email protected](Intent(this@RemoteService,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? = null  overrIDe fun onStartCommand(intent: Intent?,startID: Int): Int {    var startID = startID    startService(this)    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LolliPOP) {      mJobScheduler = getSystemService(Context.JOB_SCHEDulER_SERVICE) as JobScheduler      val 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_NOTIFICATION        val notification = NotificationUtils.createNotification(this,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,"$packagename:remote")) {      startService(this)    }    return false  }  private fun isServiceRunning(ctx: Context,classname: String): Boolean {    var isRunning = false    val activityManager = ctx        .getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager    val 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? = null  private var ID: String = context.packagename + "51"  private var name: String = context.packagename  private var context: Context = context  private var channel: NotificationChannel? = null  companion object {    @Suppresslint("StaticFIEldLeak")    private var notificationUtils: NotificationUtils? = null    fun createNotification(context: Context,Title: String,content: String,icon: Int,intent: Intent): Notification? {      if (notificationUtils == null) {        notificationUtils = NotificationUtils(context)      }      var notification: Notification? = null      notification = if (Build.VERSION.SDK_INT >= 26) {        notificationUtils?.createNotificationChannel()        notificationUtils?.getChannelNotification(Title,content,icon,intent)?.build()      } else {        notificationUtils?.getNotification_25(Title,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,intent: Intent): Notification.Builder {    //PendingIntent.FLAG_UPDATE_CURRENT 这个类型才能传值    val pendingIntent = PendingIntent.getbroadcast(context,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,intent: Intent): NotificationCompat.Builder {    val pendingIntent = PendingIntent.getbroadcast(context,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)        .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  @OverrIDe  public 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 8.0以上系统应用如何保活所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/web/1145788.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-31
下一篇 2022-05-31

发表评论

登录后才能评论

评论列表(0条)

保存