1

我的一位同行开发人员编写了intent service一个 API 调用,然后休眠 2 分钟。唤醒后,它再次发送。

下面是代码:

public class GpsTrackingService extends IntentService {

....

@Override
    protected void onHandleIntent(Intent intent) {
      do{
        try{
          //make API call here

           //then go to sleep for 2 mins
          TimeUnit.SECONDS.sleep(120);
       
        } catch(InterruptedException ex){
            ex.printStackTrace();
        }
      } while (preferences.shouldSendGps()); //till the user can send gps.

    }

....

}


Manifest

<service android:name=".commons.GpsTrackingService" />

当手机处于活动状态时,这工作正常。但是,只要手机进入打盹模式,它就无法唤醒。

使用警报管理器可以WAKE permission解决这个问题吗?

我刚刚获得了代码库,需要在今天解决这个问题。如果有人可以提供帮助,那就太好了。

4

2 回答 2

7

正如文档所说:

在打盹模式下,系统会尝试通过限制应用程序访问网络和 CPU 密集型服务来节省电池电量。它还可以阻止应用程序访问网络并推迟它们的工作、同步和标准警报。

系统会定期退出 Doze 短暂的时间,以让应用程序完成其延迟活动。在此维护时段内,系统会运行所有挂起的同步、作业和警报,并允许应用程序访问网络。

简而言之,在打盹模式下,系统暂停网络访问、忽略唤醒锁、停止从传感器获取数据、将 AlarmManager 作业推迟到下一个打盹维护窗口(调用频率逐渐降低)、WiFi 扫描、JobScheduler 作业和同步适配器不运行

对于每个应用,setAndAllowWhileIdle() 和 setExactAndAllowWhileIdle() 都不能每 9 (?) 分钟触发一次以上的警报。

而且前台服务似乎也参与了这场“打瞌睡剧”,至少在 MarshMellow (M) 中。

为了在这种情况下生存,至少需要重新审查大量应用程序。你能想象一个简单的 mp3 播放器在设备进入打盹模式时停止播放音乐吗?

打盹模式会自动启动,当设备从电源上拔下并放在桌子上大约 1 小时左右,或者甚至更早,当用户单击电源按钮关闭屏幕时,但我认为这可能取决于设备制造商也是。

我尝试了很多对策,其中一些真的很搞笑。

在我的测试结束时,我找到了一个可能的解决方案:

即使主机设备处于打盹模式下,让您的应用程序运行的一种可能(也许是唯一)方法基本上是让ForegroundService(甚至是假的,根本不做任何工作)在另一个进程中运行,并获得部分唤醒锁

您需要做的基本上如下(您可以创建一个简单的项目来测试它):

  • 1 - 在您的新项目中,创建一个扩展应用程序(myApp)的新类,或使用新项目的主要活动。
  • 2 - 在 myApp onCreate() 中启动一个服务 (myAntiDozeService)
  • 3 - 在 myAntiDozeService onStartCommand() 中,创建作为前台服务启动服务所需的通知,使用 startForeground(id, notification) 启动服务并获取部分 WakeLock。

记住!这将起作用,但这只是一个起点,因为您必须小心这种方法将产生的“副作用”:

  • 1 - 电池消耗:如果您不使用某些策略并使 WakeLock 始终处于活动状态,CPU 将永远为您的应用程序工作。

  • 2 - 即使在锁定屏幕中,也将始终显示一个通知,并且无法通过简单地滑动来删除此通知,它会一直存在,直到您停止前台服务。

好的,让我们一起做。

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>标签中添加您的应用名称:

<application
        ....
        android:name=".myApp"
        ....

最后将您的前台服务添加到另一个进程中:

   <service
        android:name=".myAntiDozeService"
        android:process=":MyAntiDozeProcessName">
    </service>

一些笔记。

  • 在前面的示例中,创建的通知在单击时会打开测试项目的 ActivityMain 活动。

    Intent notificationIntent = new Intent(mContext, ActivityMain.class);

    但你也可以使用另一种意图。

  • 要对其进行测试,您必须在 ActivityMain.java 中添加一些要执行的工作,例如一些重复警报(通常在设备进入打盹模式时停止),或者成熟的网络访问,或者播放定时音,或者....任何你想要的。
  • 请记住,主要活动执行的作业必须永远运行,因为要测试此 AntiDoze,您需要等待至少 1 小时以确保设备进入打盹模式。
  • 要进入打盹模式,设备必须保持安静并拔下电源,因此您无法在调试时对其进行测试。首先调试您的应用程序,检查一切是否正在运行,然后停止它,拔下电源,再次重新启动应用程序,然后将设备单独放在桌面上并保持安静。

  • adb文档建议的用于模拟 Doze 和 StandBy 模式的命令可以也不能给您正确的结果(我想这取决于设备制造商、驱动程序等)。请以真实的行为进行测试。

在我的第一个测试中,我使用 AlarmManager 和音调发生器每 10 分钟播放一次音调,以了解我的应用程序仍然处于活动状态。而且它仍然从大约 18 小时开始运行,每 10 分钟就用一次响亮的音调打破我的耳朵。:-)

快乐编码!

于 2017-01-07T15:37:22.643 回答
0

我的一位同行开发人员编写了一个意图服务,该服务进行 API 调用,然后休眠 2 分钟。唤醒后,它再次发送。

只有在为用户提供价值时才运行服务。坐两分钟,看着时钟滴答作响,并没有积极地为用户提供价值。

使用具有 WAKE 权限的警报管理器会解决这个问题吗?

这取决于您所说的“解决这个问题”是什么意思。您可以使用AlarmManager请求每两分钟获得一次控制权,以便您可以完成工作。当设备处于打盹模式时,您实际上不会每两分钟获得一次控制,而是每个维护窗口一次。

于 2016-09-19T11:12:10.947 回答