需求
有一个用户需要这样一个功能,要求是APP能在充电的时候自动进入APP的一个界面
我寻思着,这玩意用普通权限做不了呀,不过APP有root权限倒也无妨,于是便决定采用Service去做后台服务
问题
某天,在Bugly看到如下的错误报告
java.lang.IllegalStateException:
Not allowed to start service Intent
app is in background uid UidRecord
提示我无法在后台启动这个服务
于是便开始着手解决这个问题
解决问题
使用startForegroundService()
根据不同的SDK版本采用不同的启动方案
/**
* 开启用户服务 需要在目标Service的 onCreate方法中加入startForeground(1, new Notication())
*
* @param cls class
* @param context 上下文
*/
public static void startUseService(Context context, Class cls) {
Intent intent = new Intent(context, cls);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
context.startForegroundService(intent);
} else {
context.startService(intent);
}
}
解决方法:使用startForegroundService()
必须在5秒内调用该服务的 startForeground(int id, Notification notification) 方法,否则将停止服务并抛出 *android.app.RemoteServiceException:Context.startForegroundService() did not then call Service.startForeground()*异常。
Bad notification for startForeground
结果发现调用了**startForeground(int id, Notification notification)**依旧不起作用,提示
FATAL EXCEPTION: main
android.app.RemoteServiceException: Bad notification for startForeground: java.lang.RuntimeException: invalid channel for service notification: Notification(channel=null pri=0 contentView=null vibrate=null sound=null defaults=0x0 flags=0x40 color=0x00000000 vis=PRIVATE)
解决方法 :自定义notification
Android 8.0 以上不能用空的通知了 , 必须自己创建通知通道
/** 启动前台服务 */
private void startForeground() {
Log.d(TAG, "startForeground: ");
String channelId = null;
// 8.0 以上需要特殊处理
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
channelId = createNotificationChannel("fuck.foreground", "ForegroundService");
} else {
channelId = "";
}
NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId);
Notification notification =
builder
.setContentText("充电控制服务已启动!")
.setSmallIcon(R.mipmap.fuck)
.setPriority(PRIORITY_MIN)
.setCategory(Notification.CATEGORY_SERVICE)
.build();
// 设置 ID 为 0 , 就不显示已通知了 , 但是 oom_adj 值会变成后台进程 11
// 设置 ID 为 大于0 , 会在通知栏显示该前台服务
startForeground(0, notification);
}
/**
* 创建通知通道
*
* @param channelId
* @param channelName
* @return
*/
@RequiresApi(Build.VERSION_CODES.O)
private String createNotificationChannel(String channelId, String channelName) {
NotificationChannel chan =
new NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_NONE);
chan.setLightColor(Color.BLUE);
chan.setLockscreenVisibility(Notification.VISIBILITY_PRIVATE);
NotificationManager service =
(NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
service.createNotificationChannel(chan);
return channelId;
}
以为这样就可以在不弹出通知的情况下去启动前台服务
解决通知问题
由于上述的startForeground(0, notification)
会不显示通知,结果出现了新的问题
android.app.ForegroundServiceDidNotStartInTimeException:
Context.startForegroundService() did not then call Service.startForeground(): ServiceRecord
原来是转为了后台服务,前台服务启动了个寂寞
这个时候就必须使用startForeground(大于零, notification)
通知会影响到用户的体验,如何去避免呢
这个时候在网上看到有人在说可以删除管道的方式
本人实际测试并不能解决问题
失败方案1:
NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
manager.cancel(1);
失败方案2
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
NotificationChannel channel = new NotificationChannel(channelId,
"Channel for notification",
NotificationManager.IMPORTANCE_NONE);
mNotificationManager.createNotificationChannel(channel);
}
会出现以下之类的问题,无法去操作前台服务的channel
Not allowed to delete channel fuck.foreground with a foreground service
解决方案 :stopForeground(true)
用定时线程池之类的去定一个延迟任务删除前台通知
由于通知发出是有延迟的,基本上在发出去之前删除掉,
可以实现调用了startForeground(???, notification)而又不会弹出通知消息烦恼用户的情况文章来源:https://www.toymoban.com/news/detail-432038.html
// 延迟2s删除前台通知
executorService.schedule(() ->
stopForeground(true),
2,
TimeUnit.SECONDS);
就这样,问题愉快的解决了!文章来源地址https://www.toymoban.com/news/detail-432038.html
总结
- 使用startForegroundService(intent)并且在目标service中自己去创建notification
- 在自己创建的channel中去使用startForeground(大于零, notification)
- 如果需要没有通知,使用stopForeground(true)去停止前台服务
到了这里,关于Android 后台启动startService()相关问题的解决的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!