As the 文档 says:
在打瞌睡模式下,系统尝试通过限制
应用程序对网络和 CPU 密集型服务的访问。它还可以防止
应用程序访问网络并推迟其作业、同步和
标准警报。
系统会定期退出 Doze 状态一段时间,让应用程序
完成他们推迟的活动。在此维护窗口期间,
系统运行所有待处理的同步、作业和警报,并让应用程序
访问网络。
简而言之,在 Doze 模式下,系统暂停网络访问、忽略唤醒锁、停止从传感器获取数据、将 AlarmManager 作业推迟到下一个 Doze 维护时段(调用频率逐渐降低),并且 WiFi 扫描、JobScheduler 作业和同步适配器也不会运行.
对于每个应用程序,setAndAllowWhileIdle() 和 setExactAndAllowWhileIdle() 都不能每 9(?) 分钟多次触发警报。
看来前台服务也参与了这场“打瞌睡的戏剧”,至少在 MarshMellow (M) 中是这样。
为了在这种情况下生存,至少需要审查大量的申请。您能想象一个简单的 mp3 播放器在设备进入打瞌睡模式时停止播放音乐吗?
当设备拔掉电源并放在桌子上大约 1 小时左右时,或者甚至更早,当用户单击电源按钮关闭屏幕时,打瞌睡模式会自动启动,但我认为这可能取决于设备制造商也是如此。
我尝试了很多对策,其中一些真的很搞笑。
在测试结束时,我得出了一个可能的解决方案:
即使主机设备处于打瞌睡模式,也可以让应用程序运行的一种可能(也可能是唯一)的方法基本上是有一个前台服务(即使是假的,根本不做任何工作)运行另一个过程具有后天获得的部分唤醒锁定.
您需要做的基本上如下(您可以创建一个简单的项目来测试它):
- 1 - 在您的新项目中,创建一个扩展应用程序 (myApp) 的新类,或使用
新项目的主要活动。
- 2 - 在 myApp onCreate() 中启动一个服务 (myAntiDozeService)
- 3 - 在 myAntiDozeService onStartCommand() 中,创建通知
需要将服务启动为前台服务,启动
使用 startForeground(id, notification) 的服务并获取
部分唤醒锁。
记住!这会起作用,但这只是一个起点,因为您必须小心此方法将产生的“副作用”:
好的,让我们一起做。
myApp.java
public class myApp extends Application {
private static final String STARTFOREGROUND_ACTION = "STARTFOREGROUND_ACTION";
private static final String STOPFOREGROUND_ACTION = "STOPFOREGROUND_ACTION";
@Override
public void onCreate() {
super.onCreate();
// start foreground service
startForeService();
}
private void stopForeService() {
Intent service = new Intent(this, myAntiDozeService.class);
service.setAction(STOPFOREGROUND_ACTION);
stopService(service);
}
private void startForeService(){
Intent service = new Intent(this, myAntiDozeService.class);
service.setAction(STARTFOREGROUND_ACTION);
startService(service);
}
@Override
public void onTerminate() {
stopForeService();
super.onTerminate();
}
}
myAntiDozeService.java
public class myAntiDozeService extends Service {
private static final String TAG = myAntiDozeService.class.getName();
private static boolean is_service_running = false;
private Context mContext;
private PowerManager.WakeLock mWakeLock;
private static final int NOTIFICATION_ID = 12345678;
private static final String STARTFOREGROUND_ACTION = "STARTFOREGROUND_ACTION";
private static final String STOPFOREGROUND_ACTION = "STOPFOREGROUND_ACTION";
@Override
public void onCreate() {
super.onCreate();
mContext = getApplicationContext();
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if (!is_service_running && STARTFOREGROUND_ACTION.equals(intent.getAction())) {
Log.i(TAG, "Received Start Foreground Intent ");
showNotification();
is_service_running = true;
acquireWakeLock();
} else if (is_service_running && STOPFOREGROUND_ACTION.equals(intent.getAction())) {
Log.i(TAG, "Received Stop Foreground Intent");
is_service_running = false;
stopForeground(true);
stopSelf();
}
return START_STICKY;
}
@Override
public void onDestroy() {
releaseWakeLock();
super.onDestroy();
}
private void showNotification(){
Intent notificationIntent = new Intent(mContext, ActivityMain.class);
notificationIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0);
Notification notification = new NotificationCompat.Builder(mContext)
.setContentTitle("myApp")
.setTicker("myApp")
.setContentText("Application is running")
.setSmallIcon(R.drawable.ic_launcher)
.setContentIntent(pendingIntent)
.build();
// starts this service as foreground
startForeground(NOTIFICATION_ID, notification);
}
public void acquireWakeLock() {
final PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
releaseWakeLock();
//Acquire new wake lock
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG+"PARTIAL_WAKE_LOCK");
mWakeLock.acquire();
}
public void releaseWakeLock() {
if (mWakeLock != null && mWakeLock.isHeld()) {
mWakeLock.release();
mWakeLock = null;
}
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
}
AndroidManifest.xml 更改。
在 AndroidManifest.xml 中添加此权限:
<uses-permission android:name="android.permission.WAKE_LOCK" />
不要忘记在中添加您的应用程序的名称<application>
tag:
<application
....
android:name=".myApp"
....
最后将运行的前台服务添加到另一个进程中:
<service
android:name=".myAntiDozeService"
android:process=":MyAntiDozeProcessName">
</service>
一些注释。
-
在前面的示例中,单击时创建的通知,
打开测试项目的 ActivityMain 活动。
Intent notificationIntent = new Intent(mContext, ActivityMain.class)
;
但你也可以使用另一种意图。
- 要测试它,您必须添加一些要执行的作业到您的
ActivityMain.java,例如一些重复闹钟(这是
当设备进入打瞌睡模式时通常会停止),或者成熟
网络访问,或者播放定时提示音,或者......任何你想要的。
- 请记住,主要活动执行的作业必须运行
永远因为要测试此 AntiDoze,您需要等待至少 1
小时以确保设备进入打瞌睡模式。
要进入打瞌睡模式,设备必须保持安静并拔掉插头,因此
调试时无法测试它。首先调试您的应用程序,
检查一切是否正在运行,然后停止它,拔掉插头,重新启动
再次应用程序,然后将设备安静地放在办公桌上。
The adb
文档建议的命令模拟打瞌睡
和待机模式可以或不能给你正确的结果
(我想,这取决于设备制造商、驱动程序、bla
布拉)。请以真实行为进行测试。
在我的第一个测试中,我使用 AlarmManager 和音频发生器每 10 分钟播放一次音调,只是为了了解我的应用程序仍然处于活动状态。
从大约 18 小时开始,它仍然在运行,每隔 10 分钟就会发出一声响亮的声音,让我的耳朵受伤。 :-)
快乐编码!