詳解Android 8.0以上系統(tǒng)應(yīng)用如何?;?/h1>
更新時(shí)間:2019年08月26日 15:18:48 作者:xiangzhihong8
這篇文章主要介紹了詳解Android 8.0以上系統(tǒng)應(yīng)用如何保活,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
最近在做一個(gè)埋點(diǎn)的sdk,由于埋點(diǎn)是分批上傳的,不是每次都上傳,所以會(huì)有個(gè)進(jìn)程?;畹臋C(jī)制,這也是自研推送的實(shí)現(xiàn)技術(shù)之一:如何保證Android進(jìn)程的存活。
對(duì)于Android來(lái)說(shuō),保活主要有以下一些方法:
- 開(kāi)啟前臺(tái)Service(效果好,推薦)
- Service中循環(huán)播放一段無(wú)聲音頻(效果較好,但耗電量高,謹(jǐn)慎使用)
- 雙進(jìn)程守護(hù)(Android 5.0前有效)
- JobScheduler(Android 5.0后引入,8.0后失效)
- 1 像素activity?;罘桨福ú煌扑])
- 廣播鎖屏、自定義鎖屏(不推薦)
- 第三方推送SDK喚醒(效果好,缺點(diǎn)是第三方接入)
下面是具體的實(shí)現(xiàn)方案:
1.監(jiān)聽(tīng)鎖屏廣播,開(kāi)啟1個(gè)像素的Activity
最早見(jiàn)到這種方案的時(shí)候是2015年,有個(gè)FM的app為了向投資人展示月活,在Android應(yīng)用中開(kāi)啟一個(gè)1像素的Activity。
由于Activity的級(jí)別是比較高的,所以開(kāi)啟1個(gè)像素的Activity的方式就可以保證進(jìn)程是不容易被殺掉的。
具體來(lái)說(shuō),定義一個(gè)1像素的Activity,在該Activity中動(dòng)態(tài)注冊(cè)自定義的廣播。
class OnePixelActivity : AppCompatActivity() {
private lateinit var br: BroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//設(shè)定一像素的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里注冊(cè)廣播接受者 接受到廣播結(jié)束掉一像素
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 {
//銷毀的時(shí)候解鎖廣播
unregisterReceiver(br)
} catch (e: IllegalArgumentException) {
}
super.onDestroy()
}
/**
* 檢查屏幕是否點(diǎn)亮
*/
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, 雙進(jìn)程守護(hù)
雙進(jìn)程守護(hù),在Android 5.0前是有效的,5.0之后就不行了。首先,我們定義定義一個(gè)本地服務(wù),在該服務(wù)中播放無(wú)聲音樂(lè),并綁定遠(yuǎn)程服務(wù)
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 {
//播放無(wú)聲音樂(lè)
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(this, R.raw.novioce)
//聲音設(shè)置為0
mediaPlayer?.setVolume(0f, 0f)
mediaPlayer?.isLooping = true//循環(huán)播放
play()
}
//啟用前臺(tái)服務(wù),提升優(yōu)先級(jí)
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)
}
//綁定守護(hù)進(jìn)程
try {
val intent3 = Intent(this, RemoteService::class.java)
this.bindService(intent3, connection, Context.BIND_ABOVE_CLIENT)
} catch (e: Exception) {
}
//隱藏服務(wù)通知
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()
}
}
}
然后再定義一個(gè)遠(yuǎn)程服務(wù),綁定本地服務(wù)。
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) {}
}
}
/**
* 通知欄點(diǎn)擊廣播接受者
*/
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增加的支持一種特殊的任務(wù)調(diào)度機(jī)制,可以用它來(lái)實(shí)現(xiàn)進(jìn)程?;睿贿^(guò)在Android8.0系統(tǒng)中,此種方法也失效。
首先,我們定義一個(gè)JobService,開(kāi)啟本地服務(wù)和遠(yuǎn)程服務(wù)。
@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) //執(zhí)行的最小延遲時(shí)間
builder.setOverrideDeadline(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS) //執(zhí)行的最長(zhǎng)延時(shí)時(shí)間
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) // 當(dāng)插入充電器,執(zhí)行該任務(wù)
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)
}
}
//啟動(dòng)本地服務(wù)
val localIntent = Intent(context, LocalService::class.java)
//啟動(dòng)守護(hù)進(jìn)程
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優(yōu)先級(jí)
在onStartCommand()方法中開(kāi)啟一個(gè)通知,提高進(jìn)程的優(yōu)先級(jí)。注意:從Android 8.0(API級(jí)別26)開(kāi)始,所有通知必須要分配一個(gè)渠道,對(duì)于每個(gè)渠道,可以單獨(dú)設(shè)置視覺(jué)和聽(tīng)覺(jué)行為。然后用戶可以在設(shè)置中修改這些設(shè)置,根據(jù)應(yīng)用程序來(lái)決定哪些通知可以顯示或者隱藏。
首先,定義一個(gè)通知工具類,此工具欄兼容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 這個(gè)類型才能傳值
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中的一個(gè)API,借助Workmanager,我們可以用它來(lái)實(shí)現(xiàn)應(yīng)用餓?;睢J褂们?,我們需要依賴Workmanager庫(kù),如下:
implementation "android.arch.work:work-runtime:1.0.0-alpha06"
Worker是一個(gè)抽象類,用來(lái)指定需要執(zhí)行的具體任務(wù)。
public class KeepLiveWork extends Worker {
private static final String TAG = "KeepLiveWork";
@NonNull
@Override
public WorkerResult doWork() {
Log.d(TAG, "keep-> doWork: startKeepService");
//啟動(dòng)job服務(wù)
startJobService();
//啟動(dòng)相互綁定的服務(wù)
startKeepService();
return WorkerResult.SUCCESS;
}
}
然后,啟動(dòng)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);
}
關(guān)于WorkManager,可以通過(guò)下面的文章來(lái)詳細(xì)了解:WorkManager淺談
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
-
Anroid ListView分組和懸浮Header實(shí)現(xiàn)方法
這篇文章主要介紹了Anroid ListView分組和懸浮Header實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。 2016-11-11
-
Android優(yōu)化之啟動(dòng)頁(yè)去黑屏實(shí)現(xiàn)秒啟動(dòng)
本文的內(nèi)容主要是講Android啟動(dòng)頁(yè)優(yōu)化,去黑屏實(shí)現(xiàn)秒啟動(dòng)的功能,有需要的小伙伴們可以參考學(xué)習(xí)。 2016-08-08
-
詳解Android壁紙服務(wù)的啟動(dòng)過(guò)程
你有設(shè)置過(guò)手機(jī)的壁紙嗎,你知道壁紙是什么樣的程序它是怎么在后臺(tái)運(yùn)行的嗎?這篇文章主要介紹了詳解Android系統(tǒng)壁紙服務(wù)的啟動(dòng)過(guò)程 2021-08-08
-
android尺子的自定義view——RulerView詳解
這篇文章主要介紹了android尺子的自定義view——RulerView詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧 2020-03-03
-
Android 中Volley二次封裝并實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求緩存
這篇文章主要介紹了Android 中Volley二次封裝并實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求緩存的相關(guān)資料,希望通過(guò)本文能幫助到大家,徹底會(huì)使用Volley,需要的朋友可以參考下 2017-09-09
-
Android 使用 RxJava2 實(shí)現(xiàn)倒計(jì)時(shí)功能的示例代碼
本篇文章主要介紹了Android 使用 RxJava2 實(shí)現(xiàn)倒計(jì)時(shí)功能的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧
2018-03-03
-
Android使用Gallery實(shí)現(xiàn)照片拖動(dòng)的特效
這篇文章主要介紹了Android如何使用Gallery實(shí)現(xiàn)照片拖動(dòng)的特效,幫助大家更好的理解和利用Android進(jìn)行開(kāi)發(fā),感興趣的朋友可以了解下 2021-01-01
-
Flutter實(shí)現(xiàn)PopupMenu彈出式菜單按鈕詳解
這篇文章主要介紹了Flutter實(shí)現(xiàn)PopupMenu彈出式菜單按鈕,PopupMenuButton是一個(gè)用于創(chuàng)建彈出菜單的小部件,當(dāng)用戶點(diǎn)擊觸發(fā)按鈕時(shí),PopupMenuButton會(huì)在屏幕上方或下方彈出一個(gè)菜單,感興趣想要詳細(xì)了解可以參考下文 2023-05-05
最新評(píng)論
最近在做一個(gè)埋點(diǎn)的sdk,由于埋點(diǎn)是分批上傳的,不是每次都上傳,所以會(huì)有個(gè)進(jìn)程?;畹臋C(jī)制,這也是自研推送的實(shí)現(xiàn)技術(shù)之一:如何保證Android進(jìn)程的存活。
對(duì)于Android來(lái)說(shuō),保活主要有以下一些方法:
- 開(kāi)啟前臺(tái)Service(效果好,推薦)
- Service中循環(huán)播放一段無(wú)聲音頻(效果較好,但耗電量高,謹(jǐn)慎使用)
- 雙進(jìn)程守護(hù)(Android 5.0前有效)
- JobScheduler(Android 5.0后引入,8.0后失效)
- 1 像素activity?;罘桨福ú煌扑])
- 廣播鎖屏、自定義鎖屏(不推薦)
- 第三方推送SDK喚醒(效果好,缺點(diǎn)是第三方接入)
下面是具體的實(shí)現(xiàn)方案:
1.監(jiān)聽(tīng)鎖屏廣播,開(kāi)啟1個(gè)像素的Activity
最早見(jiàn)到這種方案的時(shí)候是2015年,有個(gè)FM的app為了向投資人展示月活,在Android應(yīng)用中開(kāi)啟一個(gè)1像素的Activity。
由于Activity的級(jí)別是比較高的,所以開(kāi)啟1個(gè)像素的Activity的方式就可以保證進(jìn)程是不容易被殺掉的。
具體來(lái)說(shuō),定義一個(gè)1像素的Activity,在該Activity中動(dòng)態(tài)注冊(cè)自定義的廣播。
class OnePixelActivity : AppCompatActivity() {
private lateinit var br: BroadcastReceiver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//設(shè)定一像素的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里注冊(cè)廣播接受者 接受到廣播結(jié)束掉一像素
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 {
//銷毀的時(shí)候解鎖廣播
unregisterReceiver(br)
} catch (e: IllegalArgumentException) {
}
super.onDestroy()
}
/**
* 檢查屏幕是否點(diǎn)亮
*/
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, 雙進(jìn)程守護(hù)
雙進(jìn)程守護(hù),在Android 5.0前是有效的,5.0之后就不行了。首先,我們定義定義一個(gè)本地服務(wù),在該服務(wù)中播放無(wú)聲音樂(lè),并綁定遠(yuǎn)程服務(wù)
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 {
//播放無(wú)聲音樂(lè)
if (mediaPlayer == null) {
mediaPlayer = MediaPlayer.create(this, R.raw.novioce)
//聲音設(shè)置為0
mediaPlayer?.setVolume(0f, 0f)
mediaPlayer?.isLooping = true//循環(huán)播放
play()
}
//啟用前臺(tái)服務(wù),提升優(yōu)先級(jí)
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)
}
//綁定守護(hù)進(jìn)程
try {
val intent3 = Intent(this, RemoteService::class.java)
this.bindService(intent3, connection, Context.BIND_ABOVE_CLIENT)
} catch (e: Exception) {
}
//隱藏服務(wù)通知
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()
}
}
}
然后再定義一個(gè)遠(yuǎn)程服務(wù),綁定本地服務(wù)。
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) {}
}
}
/**
* 通知欄點(diǎn)擊廣播接受者
*/
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增加的支持一種特殊的任務(wù)調(diào)度機(jī)制,可以用它來(lái)實(shí)現(xiàn)進(jìn)程?;睿贿^(guò)在Android8.0系統(tǒng)中,此種方法也失效。
首先,我們定義一個(gè)JobService,開(kāi)啟本地服務(wù)和遠(yuǎn)程服務(wù)。
@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) //執(zhí)行的最小延遲時(shí)間
builder.setOverrideDeadline(JobInfo.DEFAULT_INITIAL_BACKOFF_MILLIS) //執(zhí)行的最長(zhǎng)延時(shí)時(shí)間
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) // 當(dāng)插入充電器,執(zhí)行該任務(wù)
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)
}
}
//啟動(dòng)本地服務(wù)
val localIntent = Intent(context, LocalService::class.java)
//啟動(dòng)守護(hù)進(jìn)程
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優(yōu)先級(jí)
在onStartCommand()方法中開(kāi)啟一個(gè)通知,提高進(jìn)程的優(yōu)先級(jí)。注意:從Android 8.0(API級(jí)別26)開(kāi)始,所有通知必須要分配一個(gè)渠道,對(duì)于每個(gè)渠道,可以單獨(dú)設(shè)置視覺(jué)和聽(tīng)覺(jué)行為。然后用戶可以在設(shè)置中修改這些設(shè)置,根據(jù)應(yīng)用程序來(lái)決定哪些通知可以顯示或者隱藏。
首先,定義一個(gè)通知工具類,此工具欄兼容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 這個(gè)類型才能傳值
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中的一個(gè)API,借助Workmanager,我們可以用它來(lái)實(shí)現(xiàn)應(yīng)用餓?;睢J褂们?,我們需要依賴Workmanager庫(kù),如下:
implementation "android.arch.work:work-runtime:1.0.0-alpha06"
Worker是一個(gè)抽象類,用來(lái)指定需要執(zhí)行的具體任務(wù)。
public class KeepLiveWork extends Worker {
private static final String TAG = "KeepLiveWork";
@NonNull
@Override
public WorkerResult doWork() {
Log.d(TAG, "keep-> doWork: startKeepService");
//啟動(dòng)job服務(wù)
startJobService();
//啟動(dòng)相互綁定的服務(wù)
startKeepService();
return WorkerResult.SUCCESS;
}
}
然后,啟動(dòng)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);
}
關(guān)于WorkManager,可以通過(guò)下面的文章來(lái)詳細(xì)了解:WorkManager淺談
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Anroid ListView分組和懸浮Header實(shí)現(xiàn)方法
這篇文章主要介紹了Anroid ListView分組和懸浮Header實(shí)現(xiàn)方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2016-11-11
Android優(yōu)化之啟動(dòng)頁(yè)去黑屏實(shí)現(xiàn)秒啟動(dòng)
本文的內(nèi)容主要是講Android啟動(dòng)頁(yè)優(yōu)化,去黑屏實(shí)現(xiàn)秒啟動(dòng)的功能,有需要的小伙伴們可以參考學(xué)習(xí)。2016-08-08
詳解Android壁紙服務(wù)的啟動(dòng)過(guò)程
你有設(shè)置過(guò)手機(jī)的壁紙嗎,你知道壁紙是什么樣的程序它是怎么在后臺(tái)運(yùn)行的嗎?這篇文章主要介紹了詳解Android系統(tǒng)壁紙服務(wù)的啟動(dòng)過(guò)程2021-08-08
android尺子的自定義view——RulerView詳解
這篇文章主要介紹了android尺子的自定義view——RulerView詳解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android 中Volley二次封裝并實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求緩存
這篇文章主要介紹了Android 中Volley二次封裝并實(shí)現(xiàn)網(wǎng)絡(luò)請(qǐng)求緩存的相關(guān)資料,希望通過(guò)本文能幫助到大家,徹底會(huì)使用Volley,需要的朋友可以參考下2017-09-09
Android 使用 RxJava2 實(shí)現(xiàn)倒計(jì)時(shí)功能的示例代碼
本篇文章主要介紹了Android 使用 RxJava2 實(shí)現(xiàn)倒計(jì)時(shí)功能的示例代碼,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-03-03
Android使用Gallery實(shí)現(xiàn)照片拖動(dòng)的特效
這篇文章主要介紹了Android如何使用Gallery實(shí)現(xiàn)照片拖動(dòng)的特效,幫助大家更好的理解和利用Android進(jìn)行開(kāi)發(fā),感興趣的朋友可以了解下2021-01-01
Flutter實(shí)現(xiàn)PopupMenu彈出式菜單按鈕詳解
這篇文章主要介紹了Flutter實(shí)現(xiàn)PopupMenu彈出式菜單按鈕,PopupMenuButton是一個(gè)用于創(chuàng)建彈出菜單的小部件,當(dāng)用戶點(diǎn)擊觸發(fā)按鈕時(shí),PopupMenuButton會(huì)在屏幕上方或下方彈出一個(gè)菜單,感興趣想要詳細(xì)了解可以參考下文2023-05-05

