Android AlarmManager.setExactAndAllowWhileIdle()和WakefulBroadcastReceiver不适用于一些新的制造和低价设备

时间:2016-12-02 12:12:42

标签: android alarmmanager android-doze-and-standby

我认为这个问题在stackoverflow被多次提出,但仍然有很多人在努力解决这个问题。

在我的Android应用程序中,我必须每隔半小时唤醒设备以获取当前位置并将其发送到服务器。为此,我使用了AlarmManager方法setExactAndAllowWhileIdle()WakefulBroadcastReceiver。它几乎适用于所有标准/流行设备,如三星,LG(Nexus),索尼,松下,联想,摩托罗拉,微型等等....但其他一些设备主要是中国设备不支持或不允许设备唤醒来自打盹模式setExactAndAllowWhileIdle()。我在leeco letV (Android OS 6.1)设备中对其进行了测试,不允许警报管理器在特定时间间隔内唤醒。

我在下面提到的代码部分:

UserTrackingReceiverIntentService.java

public class UserTrackingReceiverIntentService extends IntentService {

    public static final String TAG = "UserTrackingReceiverIntentService";
    Context context;
    public UserTrackingReceiverIntentService() {
        super("UserTrackingReceiverIntentService");
    }

    @TargetApi(Build.VERSION_CODES.M)
    @Override
    protected void onHandleIntent(Intent intent) {
        this.context = this;

        if (!Util.isMyServiceRunning(LocationService.class, context)) {
            context.startService(new Intent(context, LocationService.class));
        }

        Calendar calendar = Calendar.getInstance();
        //********************************** SETTING NEXT ALARM *********************************************
            Intent intentWakeFullBroacastReceiver = new Intent(context, SimpleWakefulReceiver.class);
            PendingIntent sender = PendingIntent.getBroadcast(context, 1001, intentWakeFullBroacastReceiver, 0);
            AlarmManager alarmManager = (AlarmManager) context.getSystemService(context.ALARM_SERVICE);

        if (calendar.get(Calendar.MINUTE) >= 0 && calendar.get(Calendar.MINUTE) < 30) {
            calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY));
            calendar.set(Calendar.MINUTE, 30);
            calendar.set(Calendar.SECOND, 0);
        } else if (calendar.get(Calendar.MINUTE) >= 30) {
            if (calendar.get(Calendar.HOUR_OF_DAY) == 23) {
                calendar.set(Calendar.HOUR_OF_DAY, 0);
                calendar.add(Calendar.DAY_OF_MONTH, 1);
            } else {
                calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY) + 1);
            }
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
        } else {
            calendar.set(Calendar.HOUR_OF_DAY, calendar.get(Calendar.HOUR_OF_DAY));
            calendar.set(Calendar.MINUTE, 0);
            calendar.set(Calendar.SECOND, 0);
        }

        //MARSHMALLOW OR ABOVE
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
                    calendar.getTimeInMillis(), sender);
        }
        //LOLLIPOP 21 OR ABOVE
        else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            AlarmManager.AlarmClockInfo alarmClockInfo = new AlarmManager.AlarmClockInfo(calendar.getTimeInMillis(), sender);
            alarmManager.setAlarmClock(alarmClockInfo, sender);
        }
        //KITKAT 19 OR ABOVE
        else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(AlarmManager.RTC_WAKEUP,
                    calendar.getTimeInMillis(), sender);
        }
        //FOR BELOW KITKAT ALL DEVICES
        else {
            alarmManager.set(AlarmManager.RTC_WAKEUP,
                    calendar.getTimeInMillis(), sender);
        }


        Util.registerHeartbeatReceiver(context);
        SimpleWakefulReceiver.completeWakefulIntent(intent);
    }
}

每次上述服务在拨打电话后30分钟后设置下一次警报。

public class SimpleWakefulReceiver extends WakefulBroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // This is the Intent to deliver to our service.
        Calendar calendar = Calendar.getInstance();
        Intent service = new Intent(context, UserTrackingReceiverIntentService.class);
        Date date = new Date();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        String DateTime = sdf.format(date);
        DateTime = Util.locatToUTC(DateTime);
        service.putExtra("date", String.valueOf(DateTime));

        String latitude = Util.ReadSharePrefrence(context, "track_lat");
        String longitude = Util.ReadSharePrefrence(context, "track_lng");

        service.putExtra("lat", latitude);
        service.putExtra("lon", longitude);

        Log.i("SimpleWakefulReceiver", "Starting service @ " + calendar.get(Calendar.HOUR_OF_DAY) + " : " + calendar.get(Calendar.MINUTE) + " : " + calendar.get(Calendar.SECOND));

        // Start the service, keeping the device awake while it is launching.
        startWakefulService(context, service);
    }
}

我已从leeco letV设备手动更改了某些设置,例如设置&gt;电池&gt;对齐唤醒&gt;禁用这两个选项。如果启用电池可以忽略来自应用程序的强制唤醒。

我的问题:

  1. 这类设备发生了什么?为什么他们不允许在特定时间间隔内发出警报?

  2. 在一些像Gionee s这样的设备加上警报发射晚了2-3分钟......为什么?

  3. android.net.conn.CONNECTIVITY_CHANGE广播接收器,以便在网络连接发生变化时进行监听.....在Gionee s plus等某些设备中无法正常工作时该怎么办?

  4. 各种制造商的电池优化设置有很多变化......如果是这样的解决方案,它是否会对我们应用的后台服务造成伤害。

4 个答案:

答案 0 :(得分:2)

嗨我在Xiomi phone.leeco测试时遇到了同样的问题我没有太多想法。 Xiomi 手机拥有自己的操作系统即MIUI。当我们清除RAM时,它会清除所有应用程序执行操作系统中注册的一些应用程序。像Whatsapp和facebook一些其他着名的应用程序。

我尝试过Service,Alarm Manager和Scheduler但没有取得任何成功。

他们提供了一些手动特定的方式来运行服务。我检查过Leeco它使用的是Android操作系统。

MIUI 7.0的解决方案=&gt;安全性=&gt;自动启动=&gt;选择要在background =&gt;中运行的应用重新启动 重启后,您的设备应该能够像其他Android设备一样在后台运行您的应用程序服务。

MIUI 4.0 settings

MIUI AutoStart Detailed Description

据我所知,您可以尝试使用服务,然后检查它是否有效。

非常希望你能得到一些帮助。

答案 1 :(得分:2)

这些中国手机可能已禁用应用程序唤醒以接收警报事件以延长电池寿命。我知道一些中国制造商如果早些时候关闭电池保护,就不会唤醒应用程序接收推送通知。因此,在再次手动打开应用程序之前,操作系统不会将其唤醒以进行任何事件处理。

但是可以更改此行为。检查电源管理设置并相应更改,以便定期或在需要时唤醒应用程序。我认为它可能是 受限模式 或类似的东西。

答案 2 :(得分:1)

我同意你的看法。 但我认为你应该尝试使用JobScheduler一次。 你会找到官方文件 https://developer.android.com/reference/android/app/job/JobScheduler.html 我发现最好的例子 https://github.com/googlesamples/android-JobScheduler 我没有测试它。据我所知,你必须尝试这个。

<强> 修改

如果要在6.0或更高版本中以打盹模式运行应用程序,则必须将应用程序列入白名单 看看

https://www.bignerdranch.com/blog/diving-into-doze-mode-for-developers/

https://developer.android.com/training/monitoring-device-state/doze-standby.html

Imtiyaz

答案 3 :(得分:1)

在我的Android应用程序中,我必须每15分钟唤醒设备以获取当前位置并将其发送到服务器。 Application flow diagram 我在Xiaomi Mi 5s(Android 6.0.1)上测试过它。当我关闭应用程序时,应用程序进入睡眠状态:{AlarmManager setExactAndAllowWhileIdle()无法正常工作¯\_(ツ)_/¯

是的,几乎所有标准/热门设备都能正常运行,如三星,LG(Nexus 5X),索尼,摩托罗拉,Pixel ......

我的解决方案

每种类型的组件元素的清单条目 - <activity><service><receiver><provider> - 支持android:process属性,可以指定该组件应该运行的过程。

您需要在android:process=":service"

中添加此字符串AndroidManifest.xml

像这样:

... 

<receiver
    android:name="your.full.packagename.MyWakefulReceiver"
    android:process=":service"
    android:enabled="true"/>

<service
    android:name="your.full.packagename.MyLocationService"
    android:process=":service"/>

<service
    android:name="your.full.packagename.MySendService"
    android:process=":service"
    android:exported="false" />

...

我在小米米5s上测试了这个解决方案。它的工作!!!

恕我直言,只要用户退出应用程序,小米固件就会杀死主要进程......