关于Android 1112和13服务保活问题
Posted 小红妹
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于Android 1112和13服务保活问题相关的知识,希望对你有一定的参考价值。
物联网环境,为了解决不同厂商、不同设备、不同网络情况下使用顺畅,同时也考虑到节约成本,缩小应用体积的好处,我们需要一个服务应用一直存在系统中,保活它以提供服务给其他客户端调用。
开机自启动,通过广播通信,
必要权限
<!--允许查看所有未启动的应用-->
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="QueryAllPackagesPermission" />
<!--// 添加接收开机广播的权限-->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<!--前台服务-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
开机自启动Service相关代码
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 接收开机广播、开机自启动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"
/** 标记服务是否启动 */
private var serviceIsLive = false
/** 唯一前台通知ID */
private val NOTIFICATION_ID = 1000
override fun onCreate()
super.onCreate()
mContext = this
//前台显示服务
// 获取服务通知
val notification: Notification = createForegroundNotification()
//将服务置于启动状态 ,NOTIFICATION_ID指的是创建的通知的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)//阻塞时间
//receiverRegist()
RfidUHF.initUHF()
ShareUtil.putString("AES_key", intent?.getStringExtra("key"), mContext)
Log.e(TAG, "onStartCommand: "+ intent?.getStringExtra("key"))
// 标记前台服务启动
serviceIsLive = true
return super.onStartCommand(intent, flags, startId)
private fun receiverRegist()
deviceBroadcastReceiver = DeviceBroadcastReceiver()
val filter = IntentFilter()
filter.addAction("deviceCall")
registerReceiver(deviceBroadcastReceiver, filter)
/**
* 创建前台服务通知
*/
private fun createForegroundNotification(): Notification
val notificationManager = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
// 唯一的通知通道的id.
val notificationChannelId = "notification_channel_id_01"
// Android8.0以上的系统,新建消息通道
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
//震动
notificationChannel.vibrationPattern = longArrayOf(0, 1000, 500, 1000)
notificationChannel.enableVibration(true)
notificationManager?.createNotificationChannel(notificationChannel)
val builder = NotificationCompat.Builder(this, notificationChannelId)
//通知小图标
builder.setSmallIcon(R.mipmap.ic_launcher)
//通知标题
builder.setContentTitle("AndroidServer")
//通知内容
builder.setContentText("AndroidServer服务正在运行中")
//设定通知显示的时间
builder.setWhen(System.currentTimeMillis())
//设定启动的内容
val activityIntent = Intent(this, MainActivity::class.java)
val pendingIntent = PendingIntent.getActivity(
this,
1,
activityIntent,
PendingIntent.FLAG_IMMUTABLE
) /*FLAG_UPDATE_CURRENT*/
builder.setContentIntent(pendingIntent)
//创建通知并返回
return builder.build()
override fun onDestroy()
//unregisterReceiver(deviceBroadcastReceiver)
super.onDestroy()
// 标记服务关闭
serviceIsLive = false
// 移除通知
stopForeground(true)
注意
1、Android 8.0后台运行服务需要开启前台显示服务
2、Android 8.0 不再允许后台进程直接通过startService方式去启动服务,改为startForegroundService方式启动。
对应错误提示如下
Context.startForegroundService() did not then call Service.startForeground():
ServiceRecord24fafff u0 com.xxx.xxx.end/.DeviceService
3、Android O 后台应用想启动服务调用:调用startForegroundService()后 切记调用startForeground(),这个时候会有一个Notification常驻,也就是上面说的1。
权限提示:
Permission Denial: startForeground from pid=2406, uid=10134 requires
android.permission.FOREGROUND_SERVICE
4、Android 11以上启动服务不能只是这样简单的调用//context?.startService(Intent(context, DeviceService::class.java))
不然会报错,
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 UidRecord103aaa1 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: [StandaloneCoroutineCancelling@e0016fc, Dispatchers.Default]
5、Android 12 四大组件含有< intent-filter >< /intent-filter >的需要添加android:exported=“true”,更多情况情况着这篇文章 Android 12适配安全组件导出设置android:exported
指定显式值”
6、Android 11引入了包可见性 ,要么添加QUERY_ALL_PACKAGES权限,要么这样写
<queries>
//你要交互的service的包名
<package android:name="com.XXX.XXX" />
//...等等包名
</queries>
广播通信的前提,1.应用APP要启动过一次,2、要有至少有一个activity ,3、注册广播方式
这就是为什么我们需要服务的意思,首先需要开机自启动服务,这会我们可以在启动的服务中动态注册广播,测试静态注册也可以。
对了,广播的静态注册效果随着版本的升高,效果大打折扣,为了防止小人作弊,系统把君子和小人都设防了。
若是用户手动从后台杀掉应用程序,那么广播无法再次启动服务,哈哈哈哈哈哈,那就想办法让用户无法删除服务吧!
关于Android的Build类——获取Android手机设备各种信息
经常遇到要获取Android手机设备的相关信息,来进行业务的开发,比如经常会遇到要获取CPU的类型来进行so库的动态的下载。而这些都是在Android的Build类里面。相关信息如下:
private String loadSystemInfo() {
StringBuilder sb = new StringBuilder();
sb.append("主板:" + Build.BOARD + "\n");
sb.append(
"系统启动程序版本号:" + Build.BOOTLOADER + "\n");
sb.append(
"系统定制商:" + Build.BRAND + "\n");
sb.append("cpu指令集:" + Build.CPU_ABI + "\n");
sb.append(
"cpu指令集2" + Build.CPU_ABI2 + "\n");
sb.append(
"设置参数: " + Build.DEVICE + "\n");
sb.append(
"显示屏参数:" + Build.DISPLAY + "\n");
sb.append(
"无线电固件版本:" + Build.getRadioVersion() + "\n");
sb.append(
"硬件识别码:" + Build.FINGERPRINT + "\n");
sb.append(
" 硬件名称: " + Build.HARDWARE + "\n");
sb.append(
" HOST: " + Build.HOST + "\n");
sb.append(
" 修订版本列表:" + Build.ID + "\n");
sb.append(
" 硬件制造商:" + Build.MANUFACTURER + "\n");
sb.append(
" 版本:" + Build.MODEL + "\n");
sb.append(
" 硬件序列号:" + Build.SERIAL + "\n");
sb.append(
" 手机制造商:" + Build.PRODUCT + "\n");
sb.append(
" 描述Build的标签:" + Build.TAGS + "\n");
sb.append(
" TIME:" + Build.TIME + "\n");
sb.append(
" builder类型:" + Build.TYPE + "\n");
sb.append(
" USER:" + Build.USER + "\n");
MyLog.d(sb.toString());
return sb.toString();
}
结束语:用到的知识最好还是要自己跑跑,写一写。
以上是关于关于Android 1112和13服务保活问题的主要内容,如果未能解决你的问题,请参考以下文章
2023年Android黑科技保活方案,应用永生,拒绝强制杀死 最高适配Android 13 小米 华为 Oppo vivo 等最新机型 拒绝强杀 开机自启动 附demo apk 附研究资料