美文网首页
Android应用保活实践

Android应用保活实践

作者: 小可心_4790 | 来源:发表于2020-01-13 10:01 被阅读0次

    最近在做的项目中需要app在后台常驻,用于实时上传一些健康信息数据,便于后台实时查看用户的健康状况。自从Android7.0以上后台常驻实现越来越难,尤其是8.0及以上。关于保活的文章比比皆是,但是效果并不理想,关于保活的方法也就常说的哪几种,重点在于怎么组合运用。最终实现效果为:用户不主动强制杀死的话,能够一直存活(小米,华为,vivo,oppo,三星)。其中三星s8,华为nova2s用户强制杀死也能存活。


    项目结构

    常见的保活方案

    关于Android应用保活的文章很多,这里不再阐述,可自行百度。重点在于运用这样方案来实现保活功能。

    代码实现

    1.监听锁屏广播,开启1个像素的Activity。

    在锁屏的时候启动一个1个像素的Activity,当用户解锁以后将这个Activity结束掉。

    定义一个1像素的Activity,在该Activity中动态注册自定义的广播。

    class OnePixelActivity : AppCompatActivity() {

    private lateinit var br: BroadcastReceiver

    override fun onCreate(savedInstanceState: Bundle?) {

    super.onCreate(savedInstanceState)

    //设定一像素的activity

            with(window){

                setGravity(Gravity.START or Gravity.TOP)

    attributes =attributes.apply {

                    this.x =0

                    this.y =0

                    this.height =1

                    this.width =1

                }

    }

            //注册广播

            registerDestroyReceiver()

    }

    /** 注册1像素页面销毁广播*/

        private fun registerDestroyReceiver() {

    //在一像素activity里注册广播接受者    接受到广播结束掉一像素

            br =object : BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {

    finish()

    }

    }

    registerReceiver(br, IntentFilter("finish activity"))

    checkScreenOn()

    }

    /**  检查屏幕是否点亮 */

        private fun checkScreenOn() {

    val pm =application.getSystemService(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()

    }

    }

    override fun onResume() {

    super.onResume()

    checkScreenOn()

    }

    override fun onDestroy() {

    try {

    //销毁的时候解锁广播

                unregisterReceiver(br)

    }catch (e: IllegalArgumentException) {

    }

    super.onDestroy()

    }

    }

    2.双进程守护

    定义一个本地服务,在该服务中播放无声音乐,并绑定远程服务。

    class LocalService : Service() {

    private lateinit var mediaPlayer: MediaPlayer

    private lateinit var mBinder: MyBinder

    override fun onCreate() {

    super.onCreate()

    mBinder = MyBinder()

    }

    override fun onBind(intent: Intent): IBinder? {

    return mBinder

        }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {

    try {

    //播放无声音乐

                playMusic()

    //启用前台服务,提升优先级

                startForegroundService()

    //绑定守护进程

                bindGuardService()

    //隐藏服务通知

                hideServiceNotification()

    KeepLive.keepLiveService.onWorking()

    }catch (e: Exception) {

    }

    return START_STICKY

        }

    /** 隐藏服务通知*/

        private fun hideServiceNotification() {

    if (Build.VERSION.SDK_INT <25)

    startService(Intent(this, HideForegroundService::class.java))

    }

    /** 绑定守护进程*/

        private fun bindGuardService() {

    val intent = Intent(this, RemoteService::class.java)

    bindService(intent, connection, Context.BIND_ABOVE_CLIENT)

    }

    /** 启动前台服务*/

        private fun startForegroundService() {

    val intent = Intent(applicationContext, NotificationClickReceiver::class.java)

    intent.action = NotificationClickReceiver.CLICK_NOTIFICATION

            val notification = NotificationUtils.createNotification(

    this,

                    KeepLive.foregroundNotification.title,

                    KeepLive.foregroundNotification.description,

                    KeepLive.foregroundNotification.iconRes,

                    intent)

    startForeground(13691, notification)

    }

    /** 播放音乐*/

        private fun playMusic() {

    mediaPlayer = MediaPlayer.create(this, R.raw.novioce)

    //声音设置为0

            mediaPlayer.setVolume(0f, 0f)

    mediaPlayer.isLooping =true//循环播放

            if (!mediaPlayer.isPlaying)mediaPlayer.start()

    }

    private inner class MyBinder : 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)

    this@LocalService.bindService(remoteService, this, Context.BIND_ABOVE_CLIENT)

    }

    override fun onServiceConnected(name: ComponentName, service: IBinder) {

    try {

    val guardAidl = GuardAidl.Stub.asInterface(service)

    guardAidl.wakeUp(

    KeepLive.foregroundNotification.title,

                            KeepLive.foregroundNotification.description,

                            KeepLive.foregroundNotification.iconRes)

    }catch (e: RemoteException) {

    e.printStackTrace()

    }

    }

    }

    override fun onDestroy() {

    super.onDestroy()

    //解绑服务

            unbindService(connection)

    KeepLive.keepLiveService.onStop()

    }

    }

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

    class RemoteService : Service() {

    private lateinit var mBinder: MyBinder

    override fun onCreate() {

    super.onCreate()

    mBinder = MyBinder()

    }

    override fun onBind(intent: Intent): IBinder? =mBinder

        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 START_STICKY

        }

    override fun onDestroy() {

    super.onDestroy()

    unbindService(connection)

    }

    private inner class MyBinder : 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(remoteService, this, Context.BIND_ABOVE_CLIENT)

    }

    override fun onServiceConnected(name: ComponentName, service: IBinder) {}

    }

    }

    3.JobScheduler

    JobScheduler和JobService是安卓在api 21中增加的接口,用于在某些指定条件下执行后台任务。

    定义一个JobService,开启本地服务和远程服务

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)

    class JobHandlerService : JobService() {

    private var mJobScheduler: JobScheduler? =null

        override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {

    startService(this)

    startJobScheduler(startId)

    return Service.START_STICKY

        }

    /** 启动 JobScheduler*/

        private fun startJobScheduler(id: Int) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

    mJobScheduler = getSystemService(Context.JOB_SCHEDULER_SERVICE)as JobScheduler

    val builder = JobInfo.Builder(id, 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())

    }

    }

    /**

    * 启动服务

        * @param context Context

    */

        private fun startService(context: Context) {

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {

    val intent = Intent(applicationContext, NotificationClickReceiver::class.java)

    intent.action = NotificationClickReceiver.CLICK_NOTIFICATION

                val notification = NotificationUtils.createNotification(

    this, KeepLive.foregroundNotification.title,

                        KeepLive.foregroundNotification.description,

                        KeepLive.foregroundNotification.iconRes,

                        intent)

    startForeground(13691, notification)

    }

    //启动本地服务

            val localIntent = Intent(context, LocalService::class.java)

    //启动守护进程

            val guardIntent = Intent(context, RemoteService::class.java)

    startService(localIntent)

    startService(guardIntent)

    }

    /**

    * 启动job

        * @param jobParameters JobParameters

        * @return Boolean

    */

        override fun onStartJob(jobParameters: JobParameters): Boolean {

    if (!isServiceRunning(applicationContext, "com.xiyang51.keeplive.service.LocalService") || !isServiceRunning(applicationContext, "$packageName:remote")) {

    startService(this)

    }

    return false

        }

    /**

    * 停止job

        * @param jobParameters JobParameters

        * @return Boolean

    */

        override fun onStopJob(jobParameters: JobParameters): Boolean {

    if (!isServiceRunning(applicationContext, "com.xiyang51.keeplive.service.LocalService") || !isServiceRunning(applicationContext, "$packageName:remote")) {

    startService(this)

    }

    return false

        }

    /**

    * 服务是否正在运行

        * @param ctx Context

        * @param className String

        * @return Boolean

    */

        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 channel: NotificationChannel? =null

        companion object {

    private var notificationUtils: NotificationUtils? =null

            /**

    * 创建一个通知

            * @param context Context

            * @param title String

            * @param content String

            * @param icon Int

            * @param intent Intent

            * @return Notification

    */

            fun createNotification(context: Context, title: String, content: String, icon: Int, intent: Intent): Notification {

    if (notificationUtils ==null)notificationUtils = NotificationUtils(context)

    return if (Build.VERSION.SDK_INT >=26) {

    notificationUtils!!.createNotificationChannel()

    notificationUtils!!.getChannelNotification(title, content, icon, intent).build()

    }else {

    notificationUtils!!.getNotification25(title, content, icon, intent).build()

    }

    }

    }

    /**

    * 创建通知渠道8.0及以上

    */

        @RequiresApi(api = Build.VERSION_CODES.O)

    fun createNotificationChannel() {

    if (channel ==null) {

    channel = NotificationChannel(id, name, NotificationManager.IMPORTANCE_MIN)

    channel?.let {

                    it.enableLights(false)

    it.enableVibration(false)

    it.vibrationPattern =longArrayOf(0)

    it.setSound(null, null)

    }

                getManager().createNotificationChannel(channel!!)

    }

    }

    /**

    * 获取NotificationManager

        * @return NotificationManager

    */

        private fun getManager(): NotificationManager {

    if (manager ==null) {

    manager = getSystemService(Context.NOTIFICATION_SERVICE)as NotificationManager

    }

    return manager!!

    }

    /**

    * 获取渠道通知

        * @param title String

        * @param content String

        * @param icon Int

        * @param intent Intent

        * @return Notification.Builder

    */

        @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(baseContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)

    return Notification.Builder(baseContext, id)

    .setContentTitle(title)

    .setContentText(content)

    .setSmallIcon(icon)

    .setAutoCancel(true)

    .setContentIntent(pendingIntent)

    }

    /**

    * 获取通知

        * @param title String

        * @param content String

        * @param icon Int

        * @param intent Intent

        * @return NotificationCompat.Builder

    */

        fun getNotification25(title: String, content: String, icon: Int, intent: Intent): NotificationCompat.Builder {

    val pendingIntent = PendingIntent.getBroadcast(baseContext, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)

    return NotificationCompat.Builder(baseContext, id)

    .setContentTitle(title)

    .setContentText(content)

    .setSmallIcon(icon)

    .setAutoCancel(true)

    .setVibrate(longArrayOf(0))

    .setSound(null)

    .setLights(0, 0, 0)

    .setContentIntent(pendingIntent)

    }

    }

    使用

    将保活的功能封装成了一个单独的库,依赖该库即可。

    app中使用:

    class AppApplication : Application(),KeepLiveService {

    override fun onCreate() {

    super.onCreate()

    //启动保活服务

            KeepLive.startWork(this, KeepLive.RunMode.ROGUE, getNotification(),this)

    }

    //一直存活,可能调用多次

        override fun onWorking() {

    Log.e("KeepLive","onWorking()")

    }

    //可能调用多次,跟onWorking匹配调用

        override fun onStop() {

    Log.e("KeepLive","onStop()")

    }

    /**

    * 创建通知

        * @return ForegroundNotification

    */

        private fun getNotification(): ForegroundNotification {

    return ForegroundNotification("Title", "message", R.mipmap.ic_launcher,

                    object : ForegroundNotificationClickListener {

    override fun foregroundNotificationClick(context: Context, intent: Intent) {

    //点击通知回调

                        }

    })

    }

    }

    清单文件配置:

    <!--权限配置-->

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <uses-permission android:name="android.permission.GET_TASKS" />

    <uses-permission android:name="android.permission.REORDER_TASKS" />

    <!--保活相关配置-->

    <receiver android:name="com.xiyang51.keeplive.receiver.NotificationClickReceiver" />

    <activity android:name="com.xiyang51.keeplive.activity.OnePixelActivity" />

    <service android:name="com.xiyang51.keeplive.service.LocalService" />

    <service android:name="com.xiyang51.keeplive.service.HideForegroundService" />

        android:name="com.xiyang51.keeplive.service.JobHandlerService"

        android:permission="android.permission.BIND_JOB_SERVICE" />

        android:name="com.xiyang51.keeplive.service.RemoteService"

        android:process=":remote" />

    代码地址

    github

    相关文章

      网友评论

          本文标题:Android应用保活实践

          本文链接:https://www.haomeiwen.com/subject/sadgactx.html