[英]My Android 11 app stops running when the user puts it into the background unless the phone is connected to power
我編寫了一個前台服務,以確保我的應用程序在進入后台時可以繼續運行。 該應用程序需要在后台運行,因為在其計時器結束后,它會發出提示音並振動以提醒用戶。 但是,當按下電源或主頁按鈕時,應用程序的計時器會在大約 15 分鍾后停止運行,除非手機已插入電源。 我測試時手機已充滿電。
順便說一句,在閱讀各種網站后,我還將應用程序設置為不針對電池壽命進行優化,以確保應用程序繼續運行。 從我閱讀的所有內容來看,我做的一切都是正確的,但我仍然無法讓它發揮作用。 我在 Pixel 2 上運行 Android 11。我知道 Google 限制了更高版本的 Android 的前台處理,但是將應用程序設置為不優化電池壽命應該可以解決這個問題,不是嗎? 為了安全起見,當應用程序啟動時,它會要求用戶批准后台操作:
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(APPLICATION_ID)) {
// Ask user to allow app to not optimize battery life. This will keep
// the app running when the user puts it in the background by pressing
// the Power or Home button.
Intent intent = new Intent();
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + APPLICATION_ID));
startActivity(intent);
}
因此,當應用程序運行並針對電池進行優化時,用戶會看到以下內容:
我按如下方式啟動前台服務:
private void startForegroundMonitoring() {
broadcastIntent = new Intent(context, BroadcastService.class);
broadcastIntent.putExtra(ALLOWEDTIME, allowed_time);
broadcastIntent.putExtra(BEEP, beep.isChecked());
broadcastIntent.putExtra(VIBRATE, vibrate.isChecked());
broadcastIntent.putExtra(NOTIFY, notify_monitor.isChecked());
broadcastIntent.putExtra(CURFEW, curfew_config.isChecked());
broadcastIntent.putExtra(CURFEWSTARTTIME, curfew_start_time);
broadcastIntent.putExtra(CURFEWENDTIME, curfew_end_time);
startService(broadcastIntent);
}
更新:這是一些演示問題的代碼:
主要活動:
package com.testapp.showbug;
import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import static com.testapp.showbug.BuildConfig.APPLICATION_ID;
public class MainActivity extends AppCompatActivity {
private Context context;
private Intent broadcastIntent;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
context = getApplicationContext();
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
if (!pm.isIgnoringBatteryOptimizations(APPLICATION_ID)) {
// Ask user to allow app to not optimize battery life. This will keep
// the app running when the user puts it in the background by pressing
// the Power or Home button.
Intent intent = new Intent();
intent.setAction(
Settings.
ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" +
APPLICATION_ID));
startActivity(intent);
}
broadcastIntent = new Intent(context,
BroadcastService.class);
startService(broadcastIntent);
}
public void onDestroy() {
super.onDestroy();
stopService(broadcastIntent);
}
}
廣播服務:
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Build;
import android.os.CountDownTimer;
import android.os.IBinder;
import android.widget.Toast;
import androidx.core.app.NotificationCompat;
import androidx.core.app.NotificationManagerCompat;
import static android.content.pm.ServiceInfo.
FOREGROUND_SERVICE_TYPE_LOCATION;
public class BroadcastService extends Service {
private static final int ONE_MINUTE = 60000;
private int allowed_time = 30, tickCounter;
private CountDownTimer countDown;
private NotificationManagerCompat notificationManager;
private NotificationCompat.Builder notification;
@Override
public void onCreate() {
super.onCreate();
// Clear all notifications sent earlier.
notificationManager =
NotificationManagerCompat.from(this);
notificationManager.cancelAll();
createNotificationChannel();
}
@Override
public void onDestroy() {
super.onDestroy();
}
@Override
public int onStartCommand(Intent intent, int flags,
int startId) {
if (intent == null) return START_STICKY;
Intent notificationIntent = new Intent(this,
BroadcastService.class);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, 0,
notificationIntent, 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
notification = new
NotificationCompat.Builder(this,
getString(
R.string.default_notification_channel_id))
.setContentTitle(
getText(R.string.notification_title))
.setContentText(
getText(R.string.notification_message))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(
getText(R.string.notification_message)))
.setContentIntent(PendingIntent.getActivity(
this, 0, new Intent(), 0))
.setSmallIcon(R.mipmap.ic_launcher_round)
.setLocalOnly(true)
.setContentIntent(pendingIntent);
} else {
notification = new
NotificationCompat.Builder(this,
getString(
R.string.default_notification_channel_id))
.setContentTitle(
getText(R.string.notification_title))
.setContentText(
getText(R.string.notification_message))
.setStyle(new NotificationCompat.BigTextStyle()
.bigText(
getText(R.string.notification_message)))
.setContentIntent(PendingIntent.getActivity(
this, 0, new Intent(), 0))
.setSmallIcon(R.mipmap.ic_launcher_round)
.setContentIntent(pendingIntent);
}
startForeground(FOREGROUND_SERVICE_TYPE_LOCATION, notification.build());
tickCounter = -1;
// Start countdown timer for allowed time.
countDown = new CountDownTimer(allowed_time * ONE_MINUTE, ONE_MINUTE) {
@Override
public void onTick(long millisUntilFinished) {
tickCounter++;
Toast.makeText(getApplicationContext(), "tickCounter = " + tickCounter, Toast.LENGTH_LONG).show();
}
@Override
public void onFinish() {
Toast.makeText(getApplicationContext(), "tickCounter = " + allowed_time, Toast.LENGTH_LONG).show();
}
}.start();
return START_STICKY;
}
private void createNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.channel_name);
String description = getString(R.string.channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(getString(R.string.default_notification_channel_id), name, importance);
channel.setDescription(description);
notificationManager.createNotificationChannel(channel);
}
}
@Override
public IBinder onBind(Intent arg0) {
return null;
}
}
上面的代碼啟動了一個前台服務,該服務又啟動了一個 CountDownTimer,它每分鍾將刻度數增加 1 並打印結果。 30 分鍾后,它應該顯示 30 個滴答聲。 相反,它會提前停止,通常在 15-16 個滴答聲之后。
以下是運行代碼的方法:
感謝您對此的任何幫助。 在我看來,它就像 Android SDK 中的一個錯誤,看起來不太可能。 我沒有看到任何其他原因。 順便說一句,我在 Pixel 2 和三星 Tab A 上測試了這段代碼,它們都運行 Android 11(我擁有的唯一設備),所以我不知道這個錯誤是否發生在早期版本的 Android 或其他設備上。
我終於解決了這個問題,使用喚醒鎖。 喚醒鎖確保按下電源按鈕后 CPU 繼續運行。 我所要做的就是在 BroadcastService.java 中添加以下代碼:
在 onCreate() 中:
PowerManager pm = (PowerManager)getSystemService(POWER_SERVICE);
wakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "PeriSecure:MyWakeLock");
在 onStartCommand() 中:
wakeLock.acquire(allowed_time * ONE_MINUTE);
在 onDestroy() 中:
wakeLock.release();
就這樣。 后台服務現在按原樣運行。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.