java - 如何在应用程序关闭时每小时触发一次警报

标签 java android

我有一个生日应用程序,需要每天检查一次本地 SharedPreferences 数据,以查看某人的生日是否是这一天。

到目前为止,我正在尝试做一些看似简单的事情:即使当我单击后退按钮、向上滑动以关闭应用程序、打开另一个应用程序、关闭手机(而不是关闭)等时,也会每分钟发送一条测试通知.很多其他应用程序都可以执行此操作。

如果我能让这个每分钟提醒工作一次,我就可以调整它以显示来自 SharedPreferences 的数据,这不是什么大问题。但是有一个问题:

ALARMMANAGER 根本没有按照文档中所说的方式工作。

我使用 setInexactRepeating 作为我的警报管理器,并触发一个名为“ReminderAlarm”的类,然后启动一个创建通知 channel 并构建测试通知的服务。问题是当我退出应用程序时,根本不会显示任何通知。然而,一旦我打开应用程序,他们就会。 因此,出于某种原因,除非应用程序打开,否则 AlarmManager 不会触发。(我也使用 Log.d 尝试过此操作,它也仅在应用程序打开时打印到日志)

这就是设置重复闹钟的原因:

 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_enter);
        ToggleButton toggle = (ToggleButton) findViewById(R.id.toggleButton);
        toggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if(isChecked) {
                    setAlarm();
                } else {
                    cancelAlarm();
                }
            }
        });
    }

    private void setAlarm() {
        alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);
        Intent intent = new Intent(this, ReminderAlarm.class);
        pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
        alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, SystemClock.elapsedRealtime(), 100, pendingIntent);
    }

    private void cancelAlarm() {
        //alarmManager.cancel(pendingIntent);
    }

这是警报接收器:(它在 list 中定义为接收器)

public class ReminderAlarm extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        Intent i = new Intent(context, ReminderBuilderService.class);
        context.startService(i);
    }
}

这是提醒构建器服务:(它在 list 中定义为服务)

public class ReminderBuilderService extends Service {
    public final String CHANNEL_ID = "MainChannel";

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        createNotificationChannel();
        buildNotification();
        return super.onStartCommand(intent, flags, startId);
    }

    private void createNotificationChannel() {
        String name = "Birthday Notifications";
        String description = "Reminds you when your friends birthday approaches";
        int importance = NotificationManager.IMPORTANCE_HIGH;
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
        channel.setDescription(description);
        NotificationManager notificationManager = getSystemService(NotificationManager.class);
        notificationManager.createNotificationChannel(channel);
    }
    private void buildNotification() {
        Context context = getApplicationContext();
        Intent openTap = new Intent(context, EnterActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, openTap, 0);

        NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID);
        builder.setSmallIcon(R.drawable.pix_cake);
        builder.setContentTitle("TestNotification");
        builder.setStyle(new NotificationCompat.BigTextStyle()
                .bigText("TestText"));
        builder.setPriority(NotificationCompat.PRIORITY_HIGH);
        builder.setContentIntent(pendingIntent);
        builder.setAutoCancel(true);

        NotificationManagerCompat notificationManager = NotificationManagerCompat.from(context);
        notificationManager.notify(0, builder.build());
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }
}

通知应该每分钟显示一次(我知道 setInexactRepeating 不允许精确的重复计时),但它只显示当应用程序打开时

最后,这是 Android Manifest xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.myfirstapp">

    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.SET_ALARM" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <application
        android:allowBackup="true"
        android:icon="@mipmap/main_icon"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/main_icon_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <receiver android:name=".ReminderAlarm"/>
        <activity android:name=".ListOfDaysActivity" />
        <activity android:name=".MainActivity" />
        <activity android:name=".EnterActivity"
            android:launchMode="singleInstance">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
        <receiver android:name=".ReminderAlarm"/>
        <service android:name=".ReminderBuilderService"/>
    </application>
</manifest>

我有什么遗漏的吗?

最佳答案

您可以使用JobSchedulerAlarmManager来实现您想要的结果。但您应该使用的是 WorkManager 两者的抽象,具体取决于 Android 版本。

WorkManager 具有高度可配置性,允许您创建 PeriodicWorkRequestOneTimeWorkRequest 这些保证会成功。当您安排工作以及在计时器中指定时,PeriodicWorkRequest 将触发。即使应用程序关闭或后台运行,它也会在后台执行。如果您不希望任务立即执行,您可以使用 PWR(PeriodicWorkRequest) 和 FlexInterval 。请参阅下面的文档了解更多信息。

WorkManager Docs

WorkManager Architecture

WorkmManager CodeLab

例如,我创建了两个PeriodicWorkRequests,它们刷新服务并通过更新 token 来保持用户始终登录。当用户进行身份验证时,将创建 PeriodicWorkRequest。就我而言,我不需要立即触发它,因为他们刚刚收到并缓存了此信息,因此我使用了 FlexInterval。当应用程序进入后台或关闭时,工作人员会继续每 12 小时刷新一次服务,每 6 小时刷新一次 token 。它就像一个魅力。

这是一个例子:

构建工作:

 override fun beginWork() {

        val periodicWorkRequest = PeriodicWorkRequest.Builder(
                MyWorker::class.java,
                REPEAT_INTERVAL, TimeUnit.MINUTES, // How often work should repeat
                // Flex not required.
                FLEX_INTERVAL, TimeUnit.MINUTES) // Limits execution into a time window
                .setConstraints(
                     Constraints.Builder().setRequiredNetworkType(
                                       NetworkType.CONNECTED).build())
                .addTag(MY_WORKER_TAG)
                .build()

        WorkManager.getInstance().enqueueUniquePeriodicWork(
                MY_UNIQUE_WORK,
                ExistingPeriodicWorkPolicy.KEEP,
                periodicLoginRequest)

worker :

class MyWorker(context: Context, params: WorkerParameters) : Worker(context, params) {

    override fun doWork(): Result {
            // DO WORK HERE

            Result.success()
        } else {
            // HANDLE FAILURE HERE
            Result.failure()
        }

上面是一个简单的实现,但它应该可以让您了解总体思路。

关于java - 如何在应用程序关闭时每小时触发一次警报,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57211638/

相关文章:

java - 无法从 php exec 方法执行 java 类文件?

java - Activiti 用户界面 Spring App 集成

eclipse - 我可以与其他人同时在同一个项目上使用 Java 进行编程吗?

java - java项目编译后eclipse源文件丢失

android - Facebook - 像浏览器一样在 webview 中显示 facebook 用户 Feed

Android微调器关闭

java - 为什么第一次 nanoTime() 调用和后续调用之间的时间差异如此之大?

android - 服务和广播接收器之间的区别

android - 如何使用 Eclipse 在我的 android 模拟器上安装 apk 文件?

android - 无法生成签名的APK,下载资源出错