zoukankan      html  css  js  c++  java
  • Android 8.0以上系统应用如何保活

     对于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 = this@OnePixelActivity.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()
            }
        }
    }
    

    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 &amp;&amp; !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 &amp;&amp; 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是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?, 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,提高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, 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
        @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);
    
        }
  • 相关阅读:
    gw经销商上传部分代码
    lib
    【转】sql server的随机函数newID()和RAND()
    【源码】仿qq记住登录信息
    关于ConfigurationManager类
    oracle 10 协议适配器错误解决办法
    配置对象数据源时,无法找到业务对象的解决办法
    private的由来,让能你很容易地区分private与protected的用法!
    大树扎根不稳,缘何不倒?
    Html服务器控件常用属性InnerHtml属性及InnerText属性的区别
  • 原文地址:https://www.cnblogs.com/endv/p/13424201.html
Copyright © 2011-2022 走看看