意图服务不在打盹模式下工作

时间:2016-09-19 11:05:42

标签: android android-6.0-marshmallow android-intentservice android-doze

我的一位同行开发人员编写了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警报管理器解决此问题吗?

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

2 个答案:

答案 0 :(得分:3)

正如documentation所说:

  

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

     

系统会定期退出Doze一段时间以便让应用程序使用   完成他们的延期活动。在此维护窗口期间,   系统运行所有待处理的同步,作业和警报,并允许应用程序   访问网络。

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

setAndAllowWhileIdle()和setExactAndAllowWhileIdle()都不能每个应用每9(?)分钟多次触发警报。

前景服务似乎也参与了这个&#34; Doze Drama&#34;,至少在MarshMellow(M)中。

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

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

我尝试了很多对策,其中一些非常有趣。

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

即使主机设备处于打盹模式,一种可能(也许是唯一)让您的应用运行的方式基本上是ForegroundService(即使是假的,也不做任何工作)全部)使用获得的another process {/ 3}}在partial WakeLock中投放。

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

  • 1 - 在新项目中,创建一个扩展Application(myApp)的新类,或使用 新项目的主要活动。
  • 2 - 在myApp onCreate()中启动服务(myAntiDozeService)
  • 3 - 在myAntiDozeService onStartCommand()中,创建通知 需要将服务作为前台服务启动,启动 使用startForeground(id,notification)进行服务并获取 部分唤醒锁。

记住!这会有效,但这只是一个起点,因为你必须小心使用&#34;副作用&#34;这种方法会产生:

  • 1 - 电池耗尽:如果您,CPU将永远适用于您的应用 不要使用某种策略并让WakeLock始终处于活动状态。

  • 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,例如一些repeating alarm(是 通常在设备处于打盹模式时停止,或者是成熟的 网络访问,或播放的定时音调,或....无论你想要什么。
  • 请记住,主要活动执行的工作必须运行 永远因为要测试这个AntiDoze你需要等待至少1 小时以确保设备进入打盹模式。
  • 要进入打盹模式,设备必须安静并拔下电源插头 在调试时你无法测试它。首先调试您的应用, 检查一切是否正在运行然后停止,拔掉,重新启动 再次使用应用程序,让设备独立,安静地放在桌面上。

  • 文档向simulate Doze and StandBy modes建议的adb命令可能也无法为您提供正确的结果 (我想,这取决于设备制造商,驱动程序,bla BLA)。请在REAL行为中进行测试。

在我的第一次测试中,我使用AlarmManager和音频发生器每隔10分钟播放一次音调,以便了解我的应用仍处于活动状态。 它仍然在大约18个小时的时间内运行,每隔10分钟就用一声响亮的声音打断我的耳朵。 : - )

快乐的编码!

答案 1 :(得分:0)

  

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

Only have a service running while it is actively delivering value to the user。坐着两分钟,观看时钟滴答,并没有积极地为用户提供价值。

  

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

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