web-dev-qa-db-ja.com

Android AlarmManager.setExactAndAllowWhileIdle()およびWakefulBroadcastReceiverが一部の新しい製造された低価格デバイスで機能しない

この質問はstackoverflowで何度も尋ねられると思いますが、それでも解決するのに苦労している多くの人々がいます。

私のAndroidアプリでは、現在の場所を取得してサーバーに送信するために30分ごとにデバイスをウェイクアップする必要があります。このため、AlarmManagersetExactAndAllowWhileIdle()を使用しましたmethodとWakefulBroadcastReceiver。samsung、LG(Nexus)、Sony、Panasonic、lenovo、Motorola、Micro maxなどのほぼすべての標準/人気のあるデバイスで正常に動作します。デバイスがサポートしていないか、setExactAndAllowWhileIdle()を使用してDozeモードからのデバイスウェイクアップを許可できません。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デバイスから手動でいくつかの設定を変更したので、[設定]> [バッテリー]> [ウェイクアップの調整]>両方のオプションを無効にしました。有効になっている場合、バッテリーはアプリからの強制ウェイクアップを無視できます。

私の質問:

  1. そのような種類のデバイスで何が起こりましたか?なぜ彼らは特定の時間間隔で警報を発することを許可しないのですか?

  2. Gioneeのような一部のデバイスでは、2〜3分遅れてアラームが発動します...なぜですか?

  3. Android.net.conn.CONNECTIVITY_CHANGEネットワーク接続が変更されたときにリッスンするブロードキャストレシーバー..... Gionee s plusなどの一部のデバイスで機能しない場合の対処方法

  4. さまざまなメーカーのバッテリー最適化設定には多くのバリエーションがあります。それが解決策である場合、アプリのバックグラウンドサービスに害を及ぼす可能性がありますか?.

25
himCream

こんにちはXiomi phone.leecoでテスト中に同じ問題が発生しました。あまり考えていません。 Xiomi電話にはMIUIである独自のOSがあります。RAMをクリアすると、すべてのアプリがクリアされ、一部のアプリが実行されますOSに登録します。WhatsappやFacebookのように、他の有名なアプリもいくつかあります。

Service、Alarm Manager、Schedulerを試しましたが、うまくいきません。

彼らはサービスを実行するために手動で特定の方法を提供します。私はそれが使用したLeecoをチェックしましたAndroid OS。

MIUI 7.0のソリューション=>セキュリティ=>自動起動=>バックグラウンドで実行するアプリを選択=>再起動再起動後、デバイスは実行できるはずです他のバックグラウンドでのアプリケーションサービスAndroidデバイスが行います。

MIUI 4.0設定

MIUI AutoStart詳細説明

私によると、サービスで試してみて、機能するかどうかを確認できます。

うまくいけば、あなたはこれから少し助けを得るでしょう。

3
Saveen

おそらくこれらの中国の携帯電話は、バッテリー寿命を節約するためにアラームイベントを受信するためにアプリウェイクを無効にしています。一部の中国のメーカーは、バッテリー節約のために以前に閉じられた場合、プッシュ通知を受信するためにアプリを起動しません。したがって、アプリが再び手動で開かれるまで、イベント処理のためにOSによって起動されません。

ただし、この動作は変更できます。電源管理設定を確認し、それに応じて変更して、定期的または必要なときにアプリを起動できるようにします。私はそれがRestricted Modeまたは同様の何かである可能性があると思います。

2
Vikram Rao

私のAndroidアプリでは、15分ごとにデバイスをウェイクアップして現在地を取得し、サーバーに送信する必要があります。 Application flow diagram Xiaomi Mi 5s(Android 6.0.1)でテストしました。アプリケーションを閉じると、アプリケーションがスリープ状態になります:( AlarmManager with setExactAndAllowWhileIdle() not working ¯\_(ツ)_/¯

はい、Samsung、LG(Nexus 5X)、Sony、Motorola、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" />

...

Xiaomi Mi 5sでこのソリューションをテストしました。それは仕事です!!!

私見ユーザーがアプリケーションを終了するたびにXiaomiファームウェアがメインプロセスを終了するようです...

2
Plo_Koon

仰るとおりです。しかし、私はJobSchedulerを一度試してみるべきだと思います。あなたは公式ドキュメントを見つけるでしょう https://developer.Android.com/reference/Android/app/job/JobScheduler.html 私が見つける最良の例 https://github.com/googlesamples/Android-JobScheduler 私はそれをテストしていません。私によれば、これを試す必要があります。

[〜#〜]編集[〜#〜]

6.0以降でDozeモードでアプリケーションを実行する場合は、アプリケーションをホワイトリストに登録してチェックアウトする必要があります

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

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

イムティヤズ

1
Imtiyaz Khalani