保活 进程唤醒_Android 8.0以上系统应用如何保活
黑客技术点击右侧关注,了解黑客的世界!
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以上系统应用如何保活相关推荐
- Android 8.0以上系统应用如何保活
最近在做一个埋点的sdk,由于埋点是分批上传的,不是每次都上传,所以会有个进程保活的机制,这也是自研推送的实现技术之一:如何保证Android进程的存活. 对于Android来说,保活主要有以下一些方 ...
- Android 进程常驻(2)----细数利用android系统机制的保活手段
这是一个轻量级的库,配置几行代码,就可以实现在android上实现进程常驻,也就是在系统强杀下,以及360获取root权限下,clean master获取root权限下都无法杀死进程 支持系统2.3到 ...
- Android 系统(266)---细数利用android系统机制的保活手段
Android 进程常驻(2)----细数利用android系统机制的保活手段 这是一个轻量级的库,配置几行代码,就可以实现在android上实现进程常驻,也就是在系统强杀下,以及360获取root权 ...
- Android进程守护,让APP在系统内存中常驻(二)
昨天晚上写了用系统服务等方法来实现应用保活.今天写一下用提高app的进程等级来实现应用保活.想看直接调用系统方法保活应用的可以点击Android进程守护,让APP在系统内存中常驻(一)进行跳转. 一: ...
- linux进程看门狗使用方式,Linux系统中基于看门狗的精细化进程监控方法及系统的制作方法...
Linux系统中基于看门狗的精细化进程监控方法及系统的制作方法 [技术领域] [0001] 本发明涉及Linux系统的进程监控技术领域,特别是涉及一种Linux系统中基于看 门狗的精细化进程监控方法及 ...
- 王道操作系统考研笔记——2.2.0 交互式系统调度算法
2.2.0 交互式系统调度算法 知识总览 2.2.0.1 时间片轮转 知识点 说明 英文名 RR,Round-Robin 算法思想 公平地.轮流地为各个进程服务,让每个进程在一定时间间隔内都可以得到响 ...
- linux清除僵尸进程,如何清理和避免linux系统僵尸进程
linux 如何清理僵尸进程?一些朋友在维护服务器的时候,发现有5个nova-novncproxy的僵尸进程,面对这些僵尸进程,我们该如何应对呢?其实也不难,下面小编教大家查杀和避免僵尸进程. 012 ...
- 三星5.0以上系统(亲测有效)激活XPOSED框架的方法
对于喜欢钻研手机的哥们而言,大多时候会玩到xposed框架和各种功能彪悍的模块,对于5.0以下的系统版本,只要手机能获得ROOT权限,安装和激活xposed框架是非常轻松的,但随着系统版本的持续更新, ...
- 小米5.0以上系统(亲测有效)激活Xposed框架的步骤
对于喜欢搞机的朋友来说,大多时候会使用上xposed框架及其各类功能牛逼的模块,对于5.0以下的系统版本,只要手机能获得root权限,安装和激活xposed框架是非常简单的,但随着系统版本的不断升级, ...
- Process Hacker(进程管理器) v3.0.2581绿色版便携版
点击下载来源:Process Hacker(进程管理器) v3.0.2581绿色版便携版 Process Hacker是一款免费开源的统进程管理和内存编辑器,它不仅能够帮助你查看管理进程,同时也能进行 ...
最新文章
- table row设置cell的html,display:table、display:table-row和display:table-cell的用法_html/css_WEB-ITnose...
- listview移动时 item背景颜色错位问题
- js实现简单的图片轮播
- 2017年读书计划(一)
- 腾讯的这款产品下架了
- 使用命令行刷新Magento索引管理 Rebuilt Magento Indexes in terminal with php-cli
- java中j 和 j啥区别_从字节码层次分析++j和j++的区别
- jvm学习笔记(2)——java对象的内存布局
- Harmony OS — PageSlider滑动页面
- 《javascript高级程序设计》读书笔记——作用域
- Docker网络实践运用
- 把网络图片URL转化为流
- 高级职称计算机考试要求考a级,全国职称计算机考试有哪些级别
- JAVA-计算两篇文章的相似度
- Reflection probes
- ios软件商店上架老被打回_iOS APP上架App Store常见被拒原因及解决方案
- 悼念!旷视首席科学家孙剑博士去世,享年 45 岁
- php嵌入图片代码,php如何添加图片
- html页面添加大于号,2.7 在 HTML5 页面中插入半角的大于号“ ” , 使用的标记符应该是( )...
- Gson解析空字符串异常的处理