您好,登錄后才能下訂單哦!
小編給大家分享一下Android 8.0以上系統應用怎么保活,相信大部分人都還不怎么了解,因此分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后大有收獲,下面讓我們一起去了解一下吧!
最近在做一個埋點的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) //設定一像素的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 && !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 { //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); }
關于WorkManager,可以通過下面的文章來詳細了解:WorkManager淺談
以上是“Android 8.0以上系統應用怎么保活”這篇文章的所有內容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內容對大家有所幫助,如果還想學習更多知識,歡迎關注億速云行業資訊頻道!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。