java - Android Oreo+ 中的 AlarmManager 和 BroadcastReceiver

标签 java android android-studio broadcastreceiver alarmmanager

我正在制作一个在一天中的特定时间通知用户的应用程序。就像闹钟一样。该代码在 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

就好像广播服务根本没有在应该触发的时候触发一样。但是当我打开应用程序时,它会立即启动。

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 + 上设置此闹钟的替代方法,这样即使应用程序关闭或在后台,它也会通知用户。

最佳答案

Android 有一个打瞌睡模式,它会在一些不活动后进入休眠状态,即使你设法在某些手机上做到这一点,中文 ROM 肯定会给你带来麻烦(其中从最近的应用程序中删除就像强制停止应用程序一样)

对于您的问题,有一些解决方案,例如 Work manager 、 Foregroundservices 、jobscheduler 它应该可以工作,但又不能说适用于所有 ROM。我认为目前对于这种后台处理没有适当的解决方案。

但您可以做的一件事是从具有高优先级的服务器发送 FCM 通知。

您可以看到 Facebook 和 whatsapp 可以在后台运行,因为它们已被公司列入白名单。您可以通过启用从设置自动启动来将您的应用程序列入白名单。但是您需要手动进行,当我们谈论 fb 和 whatsapp 时,这不是一种情况

查看此网站了解更多详情:https://dontkillmyapp.com/

受此问题影响最大的是闹钟、健康追踪器、自动化应用程序、日历或任何需要在您不使用手机的特定时刻为您完成某些工作的东西。

在 Android 6 (Marshmallow) 中,Google 为基本 Android 引入了打盹模式,试图统一各种 Android 手机的电池节省。

不幸的是,一些制造商(例如小米、华为、一加甚至三星..)似乎没有捕获这个球,他们都有自己的电池保护程序,通常写得很糟糕,只是表面上节省电池并有副作用。

关于java - Android Oreo+ 中的 AlarmManager 和 BroadcastReceiver,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62886081/

相关文章:

在三星 Galaxy S3 上运行应用程序的 Android Studio 在上传文件时挂起

java - 如何为多个 if-else 语句编写函数

ADLS Gen2 中文件的 Java 文件对象

c# - 使用javascript在网页上自动登录和解析

java - 如何存储Java中方法返回的数组

android - 当库模块而不是主应用程序使用动态模块时,是否有在 Play 商店中使用动态模块的示例?

android - api高于21的应用程序未安装错误

android - 将 ListView 数据发送到另一个 ListView 和 Activity

android - Twitter Bootstrap 导航栏下拉菜单不起作用

java - 为什么我无法在 Android 的 edittext 中打印转义序列?