AlarmManager и BroadcastReceiver в Android Oreo + - PullRequest
3 голосов
/ 14 июля 2020

Я делаю приложение, которое уведомляет пользователя в определенное время дня. Как будильник. Код работает нормально в версиях до Android Oreo.

Из того, что я читал, Android Oreo и более поздние версии убивают действия в фоновом режиме, поэтому у меня возникает ошибка ниже.

2020-07-13 20:31:06.766 1609-1737/? W/BroadcastQueue: Background execution not allowed: receiving Intent { act=android.intent.action.PACKAGE_REPLACED dat=package:studio.com.archeagemanager flg=0x4000010 (has extras) } to air.br.com.alelo.mobile.android/co.acoustic.mobile.push.sdk.wi.AlarmReceiver

Это как если бы BroadcastService просто не запускался, когда должен. Но когда я открываю приложение, оно запускается мгновенно.

AndroidManifest. xml

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="owner.custom.permission" />
<permission
    android:name="owner.custom.permission"
    android:protectionLevel="signatureOrSystem">
</permission>

<meta-data
    android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version" />

<application
    android:allowBackup="true"
    android:icon="@mipmap/app_icon_new"
    android:label="@string/app_name"
    android:supportsRtl="true"
    android:theme="@style/AppTheme">
    <activity
        android:name=".SplashScreenActivity"
        android:theme="@style/AppCompat.TelaCheia">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name"
        android:screenOrientation="portrait"
        android:theme="@style/AppTheme.NoActionBar">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>

    <receiver
        android:name=".AlarmReceiver"
        android:enabled="true"
        android:exported="false">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
        </intent-filter>
    </receiver>

</application>

TabFragEventA. java (Здесь я помещаю только функцию, которая инициирует тревогу)

public void startAlarm(int eventID) {
    String[] NOTIFICATION_TITLES = {"Ocleera Rift", "Ocleera Rift", "Mistmerrow Conflict", "Mistmerrow Conflict",
            "Mistmerrow Conflict", "Nation Construction Quests", "Diamond Shores", "Diamond Shores", "Battle of the Golden Plains",
            "Battle of the Golden Plains", "Karkasse Ridgelands", "Kraken", "The Mirage Isle Fish Fest", "Red Dragon",
            "Abyssal Attack", "Lusca Awakening", "Delphinad Ghostship", "Cattler Wrangler", "Legendary Chef", "+1"};

    String notificationTitle = getString(R.string.contentTitle);
    String notificationText = NOTIFICATION_TITLES[eventID] + getString(R.string.contentNotificationText);

    int alarmHour = localHour.get(eventID);
    int alarmMinute = localMinute.get(eventID);
    // Decrease 5 minutes
    if(alarmMinute == 0) {
        alarmHour = alarmHour - 1;
        alarmMinute = 55;
    } else {
        alarmMinute = alarmMinute - 5;
    }
    // Setting the alarm moment
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY, alarmHour);
    calendar.set(Calendar.MINUTE, alarmMinute);
    calendar.set(Calendar.SECOND, 0);
    calendar.set(Calendar.MILLISECOND, 0);
    // Analyze if the alarm moment has passed
    Calendar actualTime = Calendar.getInstance();
    if(actualTime.getTimeInMillis() >= calendar.getTimeInMillis()) {
        calendar.add(Calendar.DAY_OF_MONTH, 1);
    }

    Intent intent = new Intent(getActivity().getApplicationContext(), AlarmReceiver.class);
    intent.putExtra("contentTitle", notificationTitle);
    intent.putExtra("contentText", notificationText);
    /*if(eventID == 11 || eventID == 13) {
        intent.putExtra("specificDayWeek", true);
    } else {
        intent.putExtra("specificDayWeek", false);
    }*/
    if(eventID == 12) {
        intent.putExtra("castleSupply", true);
        intent.putIntegerArrayListExtra("castleSupplyDayWeek", (ArrayList<Integer>) CastleSupply);
    }
    else if(eventID == 13) {
        intent.putExtra("castleClaim", true);
        intent.putIntegerArrayListExtra("castleClaimDayWeek", (ArrayList<Integer>) CastleClaim);
    }
    else if(eventID == 14) {
        intent.putExtra("abyssalAttack", true);
        intent.putIntegerArrayListExtra("abyssalDaysWeek", (ArrayList<Integer>) AbyssalDayWeek);
    }
    else if(eventID == 15) {
        intent.putExtra("luscaAwakening", true);
        intent.putIntegerArrayListExtra("luscaAwakeningDayWeek", (ArrayList<Integer>) LuscaAwakening);
    } else {
        intent.putExtra("abyssalAttack", false);
    }

    intent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
    // PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity().getApplicationContext(), eventID, intent, PendingIntent.FLAG_UPDATE_CURRENT);
    PendingIntent pendingIntent = PendingIntent.getBroadcast(getActivity().getApplicationContext(), eventID, intent, 0);
    AlarmManager alarmManager = (AlarmManager) getActivity().getSystemService(Context.ALARM_SERVICE);
    alarmManager.setRepeating(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), AlarmManager.INTERVAL_DAY, pendingIntent);
}

AlarmReceiver. java

public class AlarmReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {

        String contentTitle = intent.getStringExtra("contentTitle");
        String contentText = intent.getStringExtra("contentText");

        String CHANNEL_ID = "my_channel_01";
        NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            CharSequence name = "Channel Name";
            int importance = NotificationManager.IMPORTANCE_HIGH;
            NotificationChannel mChannel = new NotificationChannel(CHANNEL_ID, name, importance);
            notificationManager.createNotificationChannel(mChannel);
        }

        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(context, CHANNEL_ID)
                .setSmallIcon(R.drawable.icon_notification)
                .setContentTitle(contentTitle)
                .setContentText(contentText)
                .setLargeIcon(BitmapFactory.decodeResource(context.getResources(), R.drawable.icon_notification))
                .setContentIntent(pendingIntent)
                .setAutoCancel(true)
                .setSound(RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))
                .setVibrate(new long[]{800, 500, 600, 300});

        notificationManager.notify(0, notificationBuilder.build());

        Log.d("ALARMRECEIVER", "INSIDE");

    }
}

Хотелось бы знать, что сделать, чтобы широковещательный приемник работал в фоновом режиме. Или альтернативный вариант - установить этот будильник на Android Oreo +, чтобы он уведомлял пользователя даже при закрытом приложении или в фоновом режиме.

Ответы [ 3 ]

4 голосов
/ 16 июля 2020

Android имеет дремлющий режим, в котором он переходит в спящий режим после некоторого бездействия, даже если вам удастся сделать это на некоторых телефонах, китайские ПЗУ наверняка будут беспокоить вас (в котором удаление из последних приложений работает как принудительная остановка приложения)

Для вашей проблемы есть решения, такие как диспетчер работ, Foregroundservices, планировщик заданий, он должен работать, но опять же не могу сказать для всех ПЗУ. Я думаю, что прямо сейчас нет подходящего решения для этой фоновой обработки.

Но вы можете отправить уведомление FCM с сервера с высоким приоритетом.

Вы можете видеть, что Facebook и WhatsApp может работать в фоновом режиме, потому что они внесены в белый список компаний. Вы можете внести свое приложение в белый список, включив автоматический запуск из настроек, но вам нужно сделать это вручную, что не относится к случаям, когда мы говорим о fb и whatsapp

Подробности на этом веб-сайте: https://dontkillmyapp.com/

С этой проблемой Больше всего страдают будильники, трекеры состояния, приложения для автоматизации, календари или просто все, что нужно сделать за вас в определенный момент, когда вы не используете свой телефон.

С Android 6 (Marshmallow) Google представил режим Doze для базовой Android, пытаясь унифицировать экономию заряда батареи на различных Android телефонах.

К сожалению, некоторые производители (например, Xiaomi, Huawei, OnePlus или даже Samsung ..), похоже, не поймали этот мяч, и у всех есть свои собственные устройства для экономии заряда батареи, обычно очень плохо написанные, экономящие батарею только на поверхностном уровне с побочными эффектами.

2 голосов
/ 16 июля 2020

Начиная с Android oreo и выше фоновые службы больше не поддерживаются. предложение из документации разработчика - использовать службу переднего плана. Чтобы служба переднего плана работала, вам нужно подключить ее к уведомлению. вы можете настроить уведомление, чтобы оно было скрыто или не отображалось в пользовательском интерфейсе в последнее время.

проверьте это решение

@RequiresApi(api = Build.VERSION_CODES.O)
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    context = this;


    String input = intent.getStringExtra("inputExtra");
    createNotificationChannel();


    Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
            .setSmallIcon(R.mipmap.ic_launcher)

            .setContentTitle("GPS Service")

            .setContentText("GPS Service")
//                .setLargeIcon(emailObject.getSenderAvatar())
            .setStyle(new NotificationCompat.BigTextStyle()
                    .bigText("Ready\nAndroi " + android.os.Build.VERSION.SDK_INT))
            .setPriority(Notification.PRIORITY_HIGH)
            .setOngoing(true)
            .build();

    startForeground(1, notification);
    getLocation();

    //do heavy work on a background thread


    locationModel = new LocationModel("", "", "", "", "");

    LongOperation longOperation = new LongOperation();
    longOperation.execute();


    //stopSelf();

    return START_STICKY;
}

и в основном действии onCreate

    if (!isMyServiceRunning(ForegroundService.class)) {

        BroadcastReceiver br = new GPSBroadcastReceiver();
        IntentFilter filter = new IntentFilter(CONNECTIVITY_ACTION);
        filter.addAction(Intent.ACTION_BOOT_COMPLETED);

        getApplicationContext().registerReceiver(br, filter);
}

проверка, запущена ли служба

    private boolean isMyServiceRunning(Class<?> serviceClass) {
    ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    for (ActivityManager.RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if (serviceClass.getName().equals(service.service.getClassName())) {
            return true;
        }
    }
    return false;
}

вот пример из одного из моих проектов. Служба переднего плана - это способ go!

enter image description here

some helpful sources.

пример службы переднего плана 1

пример службы переднего плана 2

повторяющийся пример диспетчера аварийных сигналов

1 голос
/ 22 июля 2020

Если вы пишете приложения для часов, используйте AlarmManager.setAlarmClock . Он может срабатывать, даже если система находится в режиме холостого хода с низким энергопотреблением (он же дремота). Когда вы устанавливаете AlarmClock, он виден пользователю. Кроме того, SystemUI может отображать разные значки или сигналы тревоги.

Если вы просто хотите, чтобы вас запускали в определенное время для выполнения какой-либо работы, нет хорошего способа выйти за пределы фонового ограничения.

...