1. Android14新特性
1.1. 场景
** 在Android14(targetSDK=34)系统手机开启前台service服务崩溃**
javascript
ATAL EXCEPTION: main
Process: com.inspur.lbrd, PID: 15634
java.lang.RuntimeException: Unable to create service com.inspur.lbrd.service.KeepAliveService: android.app.MissingForegroundServiceTypeException: Starting FGS without a type callerApp=ProcessRecord{957facf 15634:com.inspur.lbrd/u0a352} targetSDK=34
at android.app.ActivityThread.handleCreateService(ActivityThread.java:5182)
at android.app.ActivityThread.-$$Nest$mhandleCreateService(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2638)
at android.os.Handler.dispatchMessage(Handler.java:108)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:328)
at android.app.ActivityThread.main(ActivityThread.java:9128)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
Caused by: android.app.MissingForegroundServiceTypeException: Starting FGS without a type callerApp=ProcessRecord{957facf 15634:com.inspur.lbrd/u0a352} targetSDK=34
at android.app.MissingForegroundServiceTypeException$1.createFromParcel(MissingForegroundServiceTypeException.java:53)
at android.app.MissingForegroundServiceTypeException$1.createFromParcel(MissingForegroundServiceTypeException.java:49)
at android.os.Parcel.readParcelableInternal(Parcel.java:4884)
at android.os.Parcel.readParcelable(Parcel.java:4866)
at android.os.Parcel.createExceptionOrNull(Parcel.java:3066)
at android.os.Parcel.createException(Parcel.java:3055)
at android.os.Parcel.readException(Parcel.java:3038)
at android.os.Parcel.readException(Parcel.java:2980)
at android.app.IActivityManager$Stub$Proxy.setServiceForeground(IActivityManager.java:7415)
at android.app.Service.startForeground(Service.java:775)
at com.inspur.lbrd.service.KeepAliveService.setForeground(SourceFile:118)
at com.inspur.lbrd.service.KeepAliveService.onCreate(SourceFile:32)
at android.app.ActivityThread.handleCreateService(ActivityThread.java:5169)
at android.app.ActivityThread.-$$Nest$mhandleCreateService(Unknown Source:0)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2638)
at android.os.Handler.dispatchMessage(Handler.java:108)
at android.os.Looper.loopOnce(Looper.java:226)
at android.os.Looper.loop(Looper.java:328)
at android.app.ActivityThread.main(ActivityThread.java:9128)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:586)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1099)
1.2. 解决方案
1.2.1. 在清单文件AndroidManifest.xml添加权限和配置
javascript
<!-- android14前台常住服务权限-->
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION" />
javascript
<service
android:name=".service.KeepAliveService"
android:foregroundServiceType="location" />
1.2.2. service服务
javascript
public class KeepAliveService extends Service {
private final String TAG = "szyj_GridTraceS-";
public KeepAliveService() {
}
@Override
public void onCreate() {
super.onCreate();
// 添加常驻通知栏
setForeground();
// startXcService();
}
private void startXcService() {
try {
String patrolStatus = SpUtil.getInstance(this).getString(
GridTraceConstant.SP_PATROL_STATUS,
GridTraceConstant.SP_PATROL_STATUS_FALSE);
//巡查服务已开启
if (TextUtils.equals(patrolStatus, GridTraceConstant.SP_PATROL_STATUS_TRUE)) {
if (!ServiceUtil.isServiceRunning(this, GridTraceService.class.getName())) {
startService(new Intent(this, GridTraceService.class));
}
} else {//未开启巡查服务
if (ServiceUtil.isServiceRunning(this, GridTraceService.class.getName())) {
stopService(new Intent(this, GridTraceService.class));
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
//可将onStartCommand() 方法的返回值设为 START_STICKY或START_REDELIVER_INTENT ,
//该值表示服务在内存资源紧张时被杀死后,在内存资源足够时再恢复。
//也可将Service设置为前台服务,这样就有比较高的优先级,在内存资源紧张时也不会被杀掉。
return START_STICKY;
//return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
// 删除图标
stopForeground(true);
}
@Override
public IBinder onBind(Intent intent) {
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* 添加常驻通知栏
*/
private void setForeground() {
NotificationManager notificationManager = (NotificationManager) getSystemService
(Context.NOTIFICATION_SERVICE);
String notificationId = "serviceid";
String notificationName = "servicename";
int noticeId = 2;
Notification.Builder builder = new Notification.Builder(this);
//创建NotificationChannel
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(notificationId,
notificationName, NotificationManager.IMPORTANCE_HIGH);
channel.enableLights(true);//设置高亮(选填)
channel.setShowBadge(true);//设置角标(选填)
//设置锁屏可见(选填)
channel.setLockscreenVisibility(Notification.VISIBILITY_PUBLIC);
notificationManager.createNotificationChannel(channel);
builder.setChannelId(notificationId);
}
Intent intent = new Intent(KeepAliveService.this, MainActivity.class);
PendingIntent pendingIntent;
//Android12
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
pendingIntent = PendingIntent.getActivity(this,
123, intent, PendingIntent.FLAG_IMMUTABLE);
} else {
pendingIntent = PendingIntent.getActivity(this,
123, intent, PendingIntent.FLAG_ONE_SHOT
| PendingIntent.FLAG_MUTABLE);
}
builder.setSmallIcon(R.mipmap.icon_app)
.setLargeIcon(BitmapFactory.decodeResource(getResources(),
R.mipmap.icon_app))
.setContentTitle(getString(R.string.app_name))//选填
.setContentText(getString(R.string.app_name))//选填
.setWhen(System.currentTimeMillis())
.setContentIntent(pendingIntent);
Notification notification = builder.build();
startForeground(noticeId, notification);
}
}
1.2.2. 启动service服务
javascript
if (!ServiceUtil.isServiceRunning(this, KeepAliveService.class.getName())) {
startService(new Intent(this, KeepAliveService.class));
}
&emsp;&emsp;判断服务是否开启
javascript
public class ServiceUtil {
/**
* @param context
* @param className service后台服务名称
* @return
* @desc 查询service是否在运行
*/
public static boolean isServiceRunning(Context context, String className) {
ActivityManager activityManager = (ActivityManager) context
.getSystemService(Context.ACTIVITY_SERVICE);
List<ActivityManager.RunningServiceInfo> serviceList = activityManager
.getRunningServices(Integer.MAX_VALUE);
if (!(serviceList.size() > 0)) {
return false;
}
for (int i = 0; i < serviceList.size(); i++) {
ActivityManager.RunningServiceInfo serviceInfo = serviceList.get(i);
ComponentName serviceName = serviceInfo.service;
if (serviceName.getClassName().equals(className)) {
return true;
}
}
return false;
}
}