Android应用保活实践
}
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)
 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? = 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 {
 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_NOTIFICATION
 val 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和JobService是安卓在api 21中增加的接口,用于在某些指定条件下执行后台任务。
定义一个JobService,开启本地服务和远程服务
@SuppressWarnings(value = [“unchecked”, “deprecation”])
 @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
 class JobHandlerService : JobService() {
private var mJobScheduler: JobScheduler? = null
override fun onStartCommand(intent: Intent?, flags: Int, 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, 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 = 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.播放无声音乐
 这里使用的是有声的mp3文件,只是在代码中把声音设置成了0;如果使用真正的无声的音乐文件,在oppo手机上按下返回键会被立刻杀死,并且在三星手机,华为nova2s强制杀死也会被杀死,所有使用了有声的文件。
#####5.提高Service优先级
 在onStartCommand()方法中开启一个通知,提高进程的优先级。注意:从Android 8.0(API级别26)开始,所有通知必须要分配一个渠道,对于每个渠道,可以单独设置视觉和听觉行为。然后用户可以在设置中修改这些设置,根据应用程序来决定哪些通知可以显示或者隐藏。
定义一个通知工具类,兼容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, 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)
 .setCon
 tentTitle(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)
 }
 }
###使用
 将保活的功能封装成了一个单独的库,依赖该库即可。
app中使用:
KeepLive.startWork(this, KeepLive.RunMode.ROGUE, ForegroundNotification(“Title”, “message”,
 R.mipmap.ic_launcher, object : ForegroundNotificationClickListener {
 override fun foregroundNotificationClick(context: Context, intent: Intent) {
 //点击通知回调
}
 }), object : KeepLiveService {
 override fun onStop() {
 //可能调用多次,跟onWorking匹配调用
 }
最后
小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
 一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取
 de fun onStop() {
 //可能调用多次,跟onWorking匹配调用
 }
最后
小编这些年深知大多数初中级Android工程师,想要提升自己,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
[外链图片转存中…(img-QhxIhI6s-1719083394703)]一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人
都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
资料⬅专栏获取
