【问题标题】:How to show daily offline notifications in Android 10?如何在 Android 10 中显示每日离线通知?
【发布时间】:2020-08-07 14:12:24
【问题描述】:

我想每天向用户发送离线通知。尝试了很多事情,例如:警报管理器,工作管理器但没有运气。通知没有触发或者它们只是稍后触发。

有什么方法可以完成工作吗?

报警功能:

public void StartAlarm()
{
    Calendar calendar = Calendar.getInstance();
    calendar.setTimeInMillis(System.currentTimeMillis());
    calendar.set(Calendar.HOUR_OF_DAY,23);
    calendar.set(Calendar.MINUTE,59);
    calendar.set(Calendar.SECOND,0);


    AlarmManager alarmManager = (AlarmManager) getApplicationContext().getSystemService(Context.ALARM_SERVICE);


    Intent alarmIntent = new Intent(getApplicationContext(), AlarmReceiver.class);
    //alarmIntent.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);

    Log.d("LOG", String.valueOf(calendar.getTimeInMillis()));

    PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 156, alarmIntent, 0);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            alarmManager.setAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            alarmManager.setExact(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        } else {
            alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), pendingIntent);
        }

工作经理代码:

 public MyWorker(@NonNull Context context, @NonNull WorkerParameters workerParams) {
    super(context, workerParams);
}


@NonNull
@Override
public Result doWork() {


    StartAlarm();


    Log.d("In worker","yes");


    return Result.success();
}

工作经理司机:

public void StartPeriodicWorker()
{

    final PeriodicWorkRequest periodicWorkRequest = new
            PeriodicWorkRequest.Builder(MyWorker.class,24,TimeUnit.HOURS)
            .addTag("Birthday")
            .build();

    WorkManager.getInstance(getApplicationContext()).enqueueUniquePeriodicWork("Birthday Notifier", ExistingPeriodicWorkPolicy.REPLACE, periodicWorkRequest);

}

报警接收器:

  public class AlarmReceiver extends BroadcastReceiver {

private DatabaseReference myRef;
private ArrayList<String> allPeoples;

private int numberOfPeoples;

private Context ctx;

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

    Toast.makeText(context,"In alarm receiver",Toast.LENGTH_LONG).show();

    ctx = context;
    initPeoples();

}

public String getCurrentDate() {
    Calendar calendar = Calendar.getInstance();
    SimpleDateFormat mdformat = new SimpleDateFormat("d MMMM");
    String strDate = mdformat.format(calendar.getTime());
    return strDate;
}


private  void initPeoples() {

    FirebaseDatabase database = FirebaseDatabase.getInstance();
    myRef = database.getReference("Users");
    myRef.keepSynced(true);

    myRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot dataSnapshot) {

            allPeoples =  new ArrayList<>();
            for(DataSnapshot snapshot : dataSnapshot.getChildren()){
                if(snapshot.getKey().equals("Teacher") || snapshot.getKey().equals("Staff")){
                    for(DataSnapshot faculty : snapshot.child("peoples").getChildren()){
                        String birthday = (String) faculty.child("DOB").getValue();

                        if(birthday.equals(getCurrentDate())) {
                            String member = birthday;
                            allPeoples.add(member);
                        }
                    }
                }else{
                    for(DataSnapshot student : snapshot.child("peoples").getChildren()){
                        String birthday = (String) student.child("DOB").getValue();

                        if(birthday.equals(getCurrentDate())) {
                            String member = birthday;
                            allPeoples.add(member);
                        }
                    }
                }
            }

            numberOfPeoples = allPeoples.size();

            ShowNotification();

        }

        @Override
        public void onCancelled(@NonNull DatabaseError databaseError) {

        }
    });

}

public void ShowNotification()
{
    String CHANNEL_ID = "Channel_1453";
    String CHANNEL_NAME = "Birthday Notification";

    NotificationManagerCompat manager = NotificationManagerCompat.from(ctx);

    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
        NotificationChannel channel = new NotificationChannel(CHANNEL_ID, CHANNEL_NAME,
                NotificationManager.IMPORTANCE_HIGH);
        manager.createNotificationChannel(channel);
    }

    Notification notification = new NotificationCompat.Builder(ctx, CHANNEL_ID)
            .setSmallIcon(R.drawable.ic_stat_notify)
            .setContentTitle("Birthday Reminder")
            .setColor(Color.GREEN)
            .setContentText("Click to see who has birthday today!")
            .setPriority(NotificationCompat.PRIORITY_HIGH)
            .setAutoCancel(true)
            .setCategory(NotificationCompat.CATEGORY_MESSAGE)
            .setContentIntent(PendingIntent.getActivity(ctx, 0, new Intent(ctx, Birthday.class), PendingIntent.FLAG_UPDATE_CURRENT))
            .build();

    manager.notify(454, notification);
}

}

【问题讨论】:

  • 您在所有设备上都有这个问题吗?根据我的经验,它必须在模拟器上运行良好。对吗?
  • 是的,所有设备。我的模拟器是 Google Pixel 3A xl。
  • 是的,请尝试另一个模拟器,例如 nexus s。然后请报告它是否工作正常。我会向你解释什么可能是你的解决方案。如果它在 nexus 上也不起作用,那么请将您的代码添加到问题中。
  • 好吧..有一件事我忘了说,我用过 Nexus S API 19,效果很好。我在这里添加代码。
  • 刚刚编辑了这个问题。我也尝试过 setRepeating 警报(当时没有使用工作管理器)但他们没有启动

标签: android notifications alarmmanager android-10.0


【解决方案1】:

我可以看到一个可能的问题,并且可以在没有工作管理器的情况下执行此操作(假设在通知运行时设备具有健康的连接)。

我建议不要在接收器本身中建立网络,而是启动一个服务(如果高于 Android 8.0,则为前台服务),然后在那里完成你的工作。这是因为 android 的接收器时间限制远低于服务/前台服务。所以对我来说,您的接收器在网络请求完成之前被终止,因此没有显示任何通知,这听起来似乎是合理的。

您可以在服务中显示通知,还可以安排下一个警报,因为setExactAndAllowWhileIdle 本身并不是重复警报。所以在你的接收器中,类似:

@Override
public void onReceive(Context context, Intent intent) {
        Intent service = new Intent(context, BirthdayNotifyService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(service);
        } else {
            context.startService(service);
        }
}

然后是一个可能的服务类:

public class BirthdayNotifyService extends IntentService {

    public BirthdayNotifyService() {
        super("BirthdayNotifyService");
    }

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

    @Override
    protected void onHandleIntent(@Nullable Intent intent) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            //Build a simple notification to show if a foreground service is neccesary
            Notification noti = notiBuilder.build();
            startForeground(1, noti);
        }

        //Do your network request work here. After completed, show your birthday notification and stop the foreground service.
    }
}

要停止服务,请在显示生日通知后立即使用以下命令:

stopForeground(true);
stopSelf();

更新:我在我的手机(小米红米 Note 8 plus)上使用了这个方法,它工作了几次,然后就停止工作了。它在股票 android 上运行良好,但并非在所有设备上都运行良好。因此,我仍然会说这不是在特定时间发送通知的可靠解决方案。

【讨论】:

  • 感谢您的回答阿明。但在 Android R 上,intentservice 已被贬低。链接:developer.android.com/reference/android/app/IntentService
  • 那里他们说使用工作经理或工作计划。我已经尝试过使用服务,你可以看我的旧帖子:stackoverflow.com/questions/63262762/… 但是它的电池有问题
  • 我的示例设计为仅在警报触发时启动服务。您是否有机会在活动中启动警报并从接收器调用服务?
  • 我没有尝试过 JobIntentService,所以我不能说那个是否适用于你的情况。但这里是 Android R 和启动前台服务的文档 - 允许您在后台发出网络请求。 developer.android.com/preview/privacy/foreground-services#types,以及前台服务类型列表developer.android.com/reference/android/…
  • 我看到了您对持续运行服务的推理 - 但我建议从警报接收器启动单个前台服务,因为后台服务在许多 android OEM 上继续运行很痛苦。另外,如果这是您的目标,我认为您应该将 showNotification 移动到条件内部,以首先检查 numberOfPeoples 是否 > 0
【解决方案2】:

看起来你做的一切都是正确的,正如你在 cmets 中所说的它在 nexus S 上工作,我假设你也在清单中声明了接收器。

所以在遇到同样的问题一周后,结果如下:

在某些具有自定义操作系统(例如小米)的设备中,当您将应用程序从最近列表中滑动时,操作系统会将其视为强制停止,因此所有服务和其他内容都会被终止。基于Issue Tracker,目前无法解决此问题。他们回应说他们正在与 OEM 合作解决这个问题。

但根据我自己的经验,如果您使用工作管理器设置通知,它会延迟,但您最终会收到它(至少大部分时间)。

但如果您希望时间准确,目前没有办法进行。

目前唯一的解决方案是手动给予一些许可。有关此手动设置的更多信息,请访问dontkillmyapp

【讨论】:

  • 我也尝试了一切(几乎)一个星期,但没有运气。我想知道游戏是如何做到的(关于“你还没有进入游戏等等等等...”的离线通知。感谢您提供的信息,如果您有时间可以看看我的其他问题吗?这篇文章和那篇文章相互关联。这是链接:stackoverflow.com/questions/63262762/…
  • 起初我使用服务方法,有时它有点工作......然后回到工作经理和警报
  • 我明白,游戏可以使用工作管理器来完成它,如果你尝试通过工作管理器发送通知(然后设置初始延迟),那么它最终会发送通知,但会有一些延迟(比如 8或延迟 10 小时)
  • 我检查了你的其他问题,这对我来说完全一样,我也尝试了所有这些方法,但没有运气。如果您打开应用程序,它会发送通知。但是如果你等待的时间更长,即使应用程序没有打开它也会发送通知。电池优化会这样做,集群一些工作请求并一起完成,这就是延迟背后的原因
  • 您能回复我的评论吗?您是否设法使其以可靠的方式工作?我使用了前台服务。它工作了几次,但是当应用程序关闭时警报停止工作。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多