必要
このような機能を必要とするユーザーがいます。要件は、アプリが充電中に自動的にアプリのインターフェースに入ることができることです。
質問
ある日、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()を使用します。
サービスのstartForeground(int id, Notice notification)メソッドは5 秒以内に呼び出す必要があります。そうしないと、サービスが停止され、*android.app.RemoteServiceException: Context.startForegroundService() が Service.startForeground()* を呼びませんでした。例外がスローされます。
startForeground の不正な通知
**startForeground(int id, Notice 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)
解決策: カスタム通知
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 (0 より大きい、通知)
通知を使用する必要があります。通知はユーザー エクスペリエンスに影響します。これを回避するにはどうすればよいですか?
このとき、インターネット上でパイプラインを削除できるという情報を見かけたのですが、
実際に試してみても問題は解決しませんでした。
失敗シナリオ 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);
}
以下の問題が発生します。フォアグラウンドサービスのチャンネルを操作できません。
フォアグラウンドサービスでチャンネルfuck.foregroundを削除することはできません。
解決策: stopForeground(true)
タイムスレッドプールを使用して、フォアグラウンド通知を削除する遅延タスクを設定します。
通知は遅延するため、基本的には送信前に削除されます。
通知メッセージをポップアップ表示せずに startForeground(???, notification) を呼び出すことができます。ユーザーに迷惑をかけるもの
// 延迟2s删除前台通知
executorService.schedule(() ->
stopForeground(true),
2,
TimeUnit.SECONDS);
このようにして、問題はめでたく解決されます。
要約する
- startForegroundService(intent) を使用して、ターゲット サービスで通知を自分で作成します
- 作成したチャネルで startForeground(ゼロより大きい、通知) を使用します
- 通知が必要ない場合は、stopForeground(true) を使用してフォアグラウンド サービスを停止します。