物聯(lián)網(wǎng)環(huán)境,為了解決不同廠商、不同設(shè)備、不同網(wǎng)絡(luò)情況下使用順暢,同時(shí)也考慮到節(jié)約成本,縮小應(yīng)用體積的好處,我們需要一個(gè)服務(wù)應(yīng)用一直存在系統(tǒng)中,保活它以提供服務(wù)給其他客戶端調(diào)用。
開機(jī)自啟動(dòng),通過(guò)廣播通信,
必要權(quán)限
<!--允許查看所有未啟動(dòng)的應(yīng)用-->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<!--// 添加接收開機(jī)廣播的權(quán)限-->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!--前臺(tái)服務(wù)-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
開機(jī)自啟動(dòng)Service相關(guān)代碼
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.os.Build
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
/**
* @date 2023/2/28
* @email L2279833535@163.com
* @author 小紅妹
* @package com.xxx.xxx.receiver
* @describe 接收開機(jī)廣播、開機(jī)自啟動(dòng)Service
* @copyright
*/
class BootBroadcastReceiver : BroadcastReceiver() {
private val ACTION_BOOT = "android.intent.action.BOOT_COMPLETED"
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == ACTION_BOOT) {
GlobalScope.launch(Dispatchers.Main) {
delay(20000L)
val intent = Intent()
intent.component =
ComponentName("com.xxx.xxx.end", "com.xxx.xxx.end.DeviceService")
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context?.startForegroundService(intent)
} else {
context?.startService(intent)
}
}
}
}
}
import android.app.*
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Color
import android.os.Build
import android.os.IBinder
import android.util.Log
import androidx.core.app.NotificationCompat
import com.ccbft.pda.reader.RfidUHF
import com.krd.ricemachine.uits.ShareUtil
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
/**
* @date 2023/3/16
* @email L2279833535@163.com
* @author 小紅妹
* @package com.xxx.xxx.end
* @describe
* @copyright
*/
class DeviceService : Service() {
private lateinit var deviceBroadcastReceiver : DeviceBroadcastReceiver
private lateinit var mContext: Context
private val TAG = "DeviceService"
/** 標(biāo)記服務(wù)是否啟動(dòng) */
private var serviceIsLive = false
/** 唯一前臺(tái)通知ID */
private val NOTIFICATION_ID = 1000
override fun onCreate() {
super.onCreate()
mContext = this
//前臺(tái)顯示服務(wù)
// 獲取服務(wù)通知
val notification: Notification = createForegroundNotification()
//將服務(wù)置于啟動(dòng)狀態(tài) ,NOTIFICATION_ID指的是創(chuàng)建的通知的ID
startForeground(NOTIFICATION_ID, notification)
}
override fun onBind(p0: Intent?): IBinder? {
return null
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
CoroutineScope(Dispatchers.Main).launch {
//delay(1000L)//阻塞時(shí)間
//receiverRegist()
RfidUHF.initUHF()
ShareUtil.putString("AES_key", intent?.getStringExtra("key"), mContext)
Log.e(TAG, "onStartCommand: "+ intent?.getStringExtra("key"))
}
// 標(biāo)記前臺(tái)服務(wù)啟動(dòng)
serviceIsLive = true
return super.onStartCommand(intent, flags, startId)
}
private fun receiverRegist() {
deviceBroadcastReceiver = DeviceBroadcastReceiver()
val filter = IntentFilter()
filter.addAction("deviceCall")
registerReceiver(deviceBroadcastReceiver, filter)
}
/**
* 創(chuàng)建前臺(tái)服務(wù)通知
*/
private fun createForegroundNotification(): Notification {
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
// 唯一的通知通道的id.
val notificationChannelId = "notification_channel_id_01"
// Android8.0以上的系統(tǒng),新建消息通道
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//用戶可見的通道名稱
val channelName = "Foreground Service Notification"
//通道的重要程度
val importance = NotificationManager.IMPORTANCE_HIGH
val notificationChannel =
NotificationChannel(notificationChannelId, channelName, importance)
notificationChannel.description = "Channel description"
//LED燈
notificationChannel.enableLights(true)
notificationChannel.lightColor = Color.RED
//震動(dòng)
notificationChannel.vibrationPattern = longArrayOf(0, 1000, 500, 1000)
notificationChannel.enableVibration(true)
notificationManager?.createNotificationChannel(notificationChannel)
}
val builder = NotificationCompat.Builder(this, notificationChannelId)
//通知小圖標(biāo)
builder.setSmallIcon(R.mipmap.ic_launcher)
//通知標(biāo)題
builder.setContentTitle("AndroidServer")
//通知內(nèi)容
builder.setContentText("AndroidServer服務(wù)正在運(yùn)行中")
//設(shè)定通知顯示的時(shí)間
builder.setWhen(System.currentTimeMillis())
//設(shè)定啟動(dòng)的內(nèi)容
val activityIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
1,
activityIntent,
PendingIntent.FLAG_IMMUTABLE
) /*FLAG_UPDATE_CURRENT*/
builder.setContentIntent(pendingIntent)
//創(chuàng)建通知并返回
return builder.build()
}
override fun onDestroy() {
//unregisterReceiver(deviceBroadcastReceiver)
super.onDestroy()
// 標(biāo)記服務(wù)關(guān)閉
serviceIsLive = false
// 移除通知
stopForeground(true)
}
注意
1、Android 8.0后臺(tái)運(yùn)行服務(wù)需要開啟前臺(tái)顯示服務(wù)
2、Android 8.0 不再允許后臺(tái)進(jìn)程直接通過(guò)startService方式去啟動(dòng)服務(wù),改為startForegroundService方式啟動(dòng)。
對(duì)應(yīng)錯(cuò)誤提示如下
Context.startForegroundService() did not then call Service.startForeground():
ServiceRecord{24fafff u0 com.xxx.xxx.end/.DeviceService}
3、Android O 后臺(tái)應(yīng)用想啟動(dòng)服務(wù)調(diào)用:調(diào)用startForegroundService()后 切記調(diào)用startForeground(),這個(gè)時(shí)候會(huì)有一個(gè)Notification常駐,也就是上面說(shuō)的1。
權(quán)限提示:
Permission Denial: startForeground from pid=2406, uid=10134 requires
android.permission.FOREGROUND_SERVICE
4、Android 11以上啟動(dòng)服務(wù)不能只是這樣簡(jiǎn)單的調(diào)用//context?.startService(Intent(context, DeviceService::class.java))
不然會(huì)報(bào)錯(cuò),
Process: com.xuanyi.webserver, PID: 2455
java.lang.IllegalStateException: Not allowed to start service Intent { cmp=com.xxx.xxx/.service.WebService }: app is in background uid UidRecord{103aaa1 u0a138 CEM idle change:cached procs:1 seq(0,0,0)}
at android.app.ContextImpl.startServiceCommon(ContextImpl.java:1715)
at android.app.ContextImpl.startService(ContextImpl.java:1670)
at android.content.ContextWrapper.startService(ContextWrapper.java:720)
at android.content.ContextWrapper.startService(ContextWrapper.java:720)
at com.xxx.xxx.receiver.BootBroadcastReceiver$onReceive$1.invokeSuspend(BootBroadcastReceiver.kt:26)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at kotlinx.coroutines.scheduling.CoroutineScheduler.runSafely(CoroutineScheduler.kt:570)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.executeTask(CoroutineScheduler.kt:749)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.runWorker(CoroutineScheduler.kt:677)
at kotlinx.coroutines.scheduling.CoroutineScheduler$Worker.run(CoroutineScheduler.kt:664)
Suppressed: kotlinx.coroutines.DiagnosticCoroutineContextException: [StandaloneCoroutine{Cancelling}@e0016fc, Dispatchers.Default]
5、Android 12 四大組件含有< intent-filter >< /intent-filter >的需要添加android:exported=“true”,更多情況情況著這篇文章 Android 12適配安全組件導(dǎo)出設(shè)置android:exported
指定顯式值”
6、Android 11引入了包可見性 ,要么添加QUERY_ALL_PACKAGES權(quán)限,要么這樣寫
<queries>
//你要交互的service的包名
<package android:name="com.XXX.XXX" />
//...等等包名
</queries>
廣播通信的前提,1.應(yīng)用APP要啟動(dòng)過(guò)一次,2、要有至少有一個(gè)activity ,3、注冊(cè)廣播方式
這就是為什么我們需要服務(wù)的意思,首先需要開機(jī)自啟動(dòng)服務(wù),這會(huì)我們可以在啟動(dòng)的服務(wù)中動(dòng)態(tài)注冊(cè)廣播,測(cè)試靜態(tài)注冊(cè)也可以。
對(duì)了,廣播的靜態(tài)注冊(cè)效果隨著版本的升高,效果大打折扣,為了防止小人作弊,系統(tǒng)把君子和小人都設(shè)防了。文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-405021.html
若是用戶手動(dòng)從后臺(tái)殺掉應(yīng)用程序,那么廣播無(wú)法再次啟動(dòng)服務(wù),哈哈哈哈哈哈,那就想辦法讓用戶無(wú)法刪除服務(wù)吧!文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-405021.html
到了這里,關(guān)于關(guān)于Android 11、12和13服務(wù)?;顔?wèn)題的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!