黑客技术点击右侧关注,了解黑客的世界!

Java开发进阶点击右侧关注,掌握进阶之路!

Python开发点击右侧关注,探讨技术话题!作者丨xiangzhihong来源:https://segmentfault.com/a/1190000020159573最近在做一个埋点的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)

        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

        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)

            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是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 {

        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");

        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);

    }

 推荐↓↓↓ 

?16个技术公众号】都在这里!

涵盖:程序员大咖、源码共读、程序员共读、数据结构与算法、黑客技术和网络安全、大数据科技、编程前端、Java、Python、Web编程开发、Android、iOS开发、Linux、数据库研发、幽默程序员等。

万水千山总是情,点个 “在看” 行不行

保活 进程唤醒_Android 8.0以上系统应用如何保活相关推荐

  1. Android 8.0以上系统应用如何保活

    最近在做一个埋点的sdk,由于埋点是分批上传的,不是每次都上传,所以会有个进程保活的机制,这也是自研推送的实现技术之一:如何保证Android进程的存活. 对于Android来说,保活主要有以下一些方 ...

  2. Android 进程常驻(2)----细数利用android系统机制的保活手段

    这是一个轻量级的库,配置几行代码,就可以实现在android上实现进程常驻,也就是在系统强杀下,以及360获取root权限下,clean master获取root权限下都无法杀死进程 支持系统2.3到 ...

  3. Android 系统(266)---细数利用android系统机制的保活手段

    Android 进程常驻(2)----细数利用android系统机制的保活手段 这是一个轻量级的库,配置几行代码,就可以实现在android上实现进程常驻,也就是在系统强杀下,以及360获取root权 ...

  4. Android进程守护,让APP在系统内存中常驻(二)

    昨天晚上写了用系统服务等方法来实现应用保活.今天写一下用提高app的进程等级来实现应用保活.想看直接调用系统方法保活应用的可以点击Android进程守护,让APP在系统内存中常驻(一)进行跳转. 一: ...

  5. linux进程看门狗使用方式,Linux系统中基于看门狗的精细化进程监控方法及系统的制作方法...

    Linux系统中基于看门狗的精细化进程监控方法及系统的制作方法 [技术领域] [0001] 本发明涉及Linux系统的进程监控技术领域,特别是涉及一种Linux系统中基于看 门狗的精细化进程监控方法及 ...

  6. 王道操作系统考研笔记——2.2.0 交互式系统调度算法

    2.2.0 交互式系统调度算法 知识总览 2.2.0.1 时间片轮转 知识点 说明 英文名 RR,Round-Robin 算法思想 公平地.轮流地为各个进程服务,让每个进程在一定时间间隔内都可以得到响 ...

  7. linux清除僵尸进程,如何清理和避免linux系统僵尸进程

    linux 如何清理僵尸进程?一些朋友在维护服务器的时候,发现有5个nova-novncproxy的僵尸进程,面对这些僵尸进程,我们该如何应对呢?其实也不难,下面小编教大家查杀和避免僵尸进程. 012 ...

  8. 三星5.0以上系统(亲测有效)激活XPOSED框架的方法

    对于喜欢钻研手机的哥们而言,大多时候会玩到xposed框架和各种功能彪悍的模块,对于5.0以下的系统版本,只要手机能获得ROOT权限,安装和激活xposed框架是非常轻松的,但随着系统版本的持续更新, ...

  9. 小米5.0以上系统(亲测有效)激活Xposed框架的步骤

    对于喜欢搞机的朋友来说,大多时候会使用上xposed框架及其各类功能牛逼的模块,对于5.0以下的系统版本,只要手机能获得root权限,安装和激活xposed框架是非常简单的,但随着系统版本的不断升级, ...

  10. Process Hacker(进程管理器) v3.0.2581绿色版便携版

    点击下载来源:Process Hacker(进程管理器) v3.0.2581绿色版便携版 Process Hacker是一款免费开源的统进程管理和内存编辑器,它不仅能够帮助你查看管理进程,同时也能进行 ...

最新文章

  1. table row设置cell的html,display:table、display:table-row和display:table-cell的用法_html/css_WEB-ITnose...
  2. listview移动时 item背景颜色错位问题
  3. js实现简单的图片轮播
  4. 2017年读书计划(一)
  5. 腾讯的这款产品下架了
  6. 使用命令行刷新Magento索引管理 Rebuilt Magento Indexes in terminal with php-cli
  7. java中j 和 j啥区别_从字节码层次分析++j和j++的区别
  8. jvm学习笔记(2)——java对象的内存布局
  9. Harmony OS — PageSlider滑动页面
  10. 《javascript高级程序设计》读书笔记——作用域
  11. Docker网络实践运用
  12. 把网络图片URL转化为流
  13. 高级职称计算机考试要求考a级,全国职称计算机考试有哪些级别
  14. JAVA-计算两篇文章的相似度
  15. Reflection probes
  16. ios软件商店上架老被打回_iOS APP上架App Store常见被拒原因及解决方案
  17. 悼念!旷视首席科学家孙剑博士去世,享年 45 岁
  18. php嵌入图片代码,php如何添加图片
  19. html页面添加大于号,2.7 在 HTML5 页面中插入半角的大于号“ ” , 使用的标记符应该是( )...
  20. Gson解析空字符串异常的处理

热门文章

  1. html img 自动缩放,网页HTML 5图片自适应屏幕 img等比例缩放大小代码
  2. 使用 SpringBoot 写 RESTful风格 增删改查接口
  3. css - 布局 - rem布局
  4. linux下用C计算某一个目录总大小
  5. 通过JDBC连接Oracle数据库中的十大技巧
  6. shell基础入门1.1shell特性
  7. offsetTop和scrollTop差异
  8. nginx平滑重启与平滑升级的方法
  9. Java常用集合类详解
  10. [转载]VS2005 不能创建 win32 smart device project