Android: 后台进程在几分钟后停止,因为设备即使在使用唤醒锁时也会休眠
Android: background process stops after some minutes since device sleeps even when using wakelock
我正在构建一个应用程序来控制 LED 灯泡(Mi-Light、limitlessLed 等)。该应用程序的目的是产生用户可以 select 并让 运行 无限期使用的效果。例如,"candle light" 效果可能会或多或少随机地在黄色和红色阴影之间改变灯泡颜色,直到用户决定停止。
快速背景信息:控制这些灯泡的方式是通过通过 WiFi 网络发送的 UDP 数据包。因此,即使设备处于休眠状态,我也需要该应用程序继续发送此类 UDP 包。
经过一番研究,我最终使用唤醒锁让设备即使在睡眠时也能通过 WiFi 网络广播 UDP 包(如果有更好的方法请告诉我,我没有了解一下)。
几分钟内一切正常(也许 10 分钟?),直到设备似乎进入某种深度睡眠模式并停止通过网络发送包。
如何防止这种情况发生?更一般地说,为了完成上述内容,我应该采取什么好的方法?
仅供参考,下面是一段示例代码,它仅在 7 种颜色的数组中旋转:
[...]
private static boolean animationRunning = false;
private Timer timer = new Timer();
private PowerManager mgr = (PowerManager)getReactApplicationContext().getSystemService(Context.POWER_SERVICE);
private PowerManager.WakeLock wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
[...]
public void startAnimation(final int step) {
animationRunning = true;
wakeLock.acquire();
final int[] steps = { 0, 66, 99, 122, 166, 199, 255 };
resetTimer();
timer.scheduleAtFixedRate(new TimerTask(){
int curstep = 0;
@Override
public void run(){
byte[] Bytes = {64, (byte)steps[curstep], 85};
try {sendUdp(Bytes);} catch(IOException e) {};
curstep = curstep + 1;
if(curstep == steps.length) {
curstep = 1;
}
}
},0,1000);
}
我可以想到两种方法来完成您的要求。
如果您可以将您的应用程序保留在前台,则可以通过将 android:keepScreenOn="true"
添加到您的 activity 来防止设备进入睡眠状态(有关详细信息,请参阅 https://developer.android.com/training/scheduling/wakelock.html).
第二种方法是假装您的应用正在后台播放音乐,因此不会被 OS 杀死。您可以播放本地存储在应用程序中的无声 mp3,并无限次地重复播放。 OS 永远不会杀死你的应用程序,直到它耗尽电池:)
希望对您有所帮助。
我最终实现了前台服务 (startForeground()),正如所解释的那样 here
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
_acquireWakelock();
_showNotification();
[do your thing]
return START_REDELIVER_INTENT;
}
private void _showNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction("com.lightcontrollerrn2.action.main");
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.mipmap.ic_launcher);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Light Controller")
.setTicker("Light Controller")
.setContentText("Light effects are running")
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(1337,
notification);
}
我在此过程中发现的一些可能对其他人也有帮助的事情:
前台服务仍然需要唤醒锁,以便在设备空闲时保持 运行ning;
您可能 运行 遇到类似问题的另一个原因是 Android 6.0 (https://developer.android.com/training/monitoring-device-state/doze-standby.html) 中引入的新打盹模式。不过,就我而言,服务似乎持续了 运行ning 足够长的时间,而无需我处理设备进入打瞌睡模式的问题(我测试了大约一个小时,对此我很满意)。 编辑:为了完成,我也解决了这个问题。以下是您的操作方式(在服务本身内):
@Override
public void onCreate() {
super.onCreate();
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
String packageName = getPackageName();
if (Build.VERSION.SDK_INT >= 23 && !pm.isIgnoringBatteryOptimizations(packageName)) {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS);
intent.setData(Uri.parse("package:" + packageName));
startActivity(intent);
}
}
我正在构建一个应用程序来控制 LED 灯泡(Mi-Light、limitlessLed 等)。该应用程序的目的是产生用户可以 select 并让 运行 无限期使用的效果。例如,"candle light" 效果可能会或多或少随机地在黄色和红色阴影之间改变灯泡颜色,直到用户决定停止。
快速背景信息:控制这些灯泡的方式是通过通过 WiFi 网络发送的 UDP 数据包。因此,即使设备处于休眠状态,我也需要该应用程序继续发送此类 UDP 包。
经过一番研究,我最终使用唤醒锁让设备即使在睡眠时也能通过 WiFi 网络广播 UDP 包(如果有更好的方法请告诉我,我没有了解一下)。
几分钟内一切正常(也许 10 分钟?),直到设备似乎进入某种深度睡眠模式并停止通过网络发送包。
如何防止这种情况发生?更一般地说,为了完成上述内容,我应该采取什么好的方法?
仅供参考,下面是一段示例代码,它仅在 7 种颜色的数组中旋转:
[...]
private static boolean animationRunning = false;
private Timer timer = new Timer();
private PowerManager mgr = (PowerManager)getReactApplicationContext().getSystemService(Context.POWER_SERVICE);
private PowerManager.WakeLock wakeLock = mgr.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "MyWakeLock");
[...]
public void startAnimation(final int step) {
animationRunning = true;
wakeLock.acquire();
final int[] steps = { 0, 66, 99, 122, 166, 199, 255 };
resetTimer();
timer.scheduleAtFixedRate(new TimerTask(){
int curstep = 0;
@Override
public void run(){
byte[] Bytes = {64, (byte)steps[curstep], 85};
try {sendUdp(Bytes);} catch(IOException e) {};
curstep = curstep + 1;
if(curstep == steps.length) {
curstep = 1;
}
}
},0,1000);
}
我可以想到两种方法来完成您的要求。
如果您可以将您的应用程序保留在前台,则可以通过将
android:keepScreenOn="true"
添加到您的 activity 来防止设备进入睡眠状态(有关详细信息,请参阅 https://developer.android.com/training/scheduling/wakelock.html).第二种方法是假装您的应用正在后台播放音乐,因此不会被 OS 杀死。您可以播放本地存储在应用程序中的无声 mp3,并无限次地重复播放。 OS 永远不会杀死你的应用程序,直到它耗尽电池:)
希望对您有所帮助。
我最终实现了前台服务 (startForeground()),正如所解释的那样 here
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();
_acquireWakelock();
_showNotification();
[do your thing]
return START_REDELIVER_INTENT;
}
private void _showNotification() {
Intent notificationIntent = new Intent(this, MainActivity.class);
notificationIntent.setAction("com.lightcontrollerrn2.action.main");
notificationIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(this, 0,
notificationIntent, 0);
Bitmap icon = BitmapFactory.decodeResource(getResources(),
R.mipmap.ic_launcher);
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle("Light Controller")
.setTicker("Light Controller")
.setContentText("Light effects are running")
.setSmallIcon(R.mipmap.ic_launcher)
.setLargeIcon(Bitmap.createScaledBitmap(icon, 128, 128, false))
.setContentIntent(pendingIntent)
.setOngoing(true)
.build();
startForeground(1337,
notification);
}
我在此过程中发现的一些可能对其他人也有帮助的事情:
前台服务仍然需要唤醒锁,以便在设备空闲时保持 运行ning;
您可能 运行 遇到类似问题的另一个原因是 Android 6.0 (https://developer.android.com/training/monitoring-device-state/doze-standby.html) 中引入的新打盹模式。不过,就我而言,服务似乎持续了 运行ning 足够长的时间,而无需我处理设备进入打瞌睡模式的问题(我测试了大约一个小时,对此我很满意)。 编辑:为了完成,我也解决了这个问题。以下是您的操作方式(在服务本身内):
@Override public void onCreate() { super.onCreate(); PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); String packageName = getPackageName(); if (Build.VERSION.SDK_INT >= 23 && !pm.isIgnoringBatteryOptimizations(packageName)) { Intent intent = new Intent(); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS); intent.setData(Uri.parse("package:" + packageName)); startActivity(intent); } }