如何在Android 10中显示每日脱机通知?

如何在Android 10中显示每日脱机通知?,android,notifications,alarmmanager,android-10.0,Android,Notifications,Alarmmanager,Android 10.0,我想每天向用户发送脱机通知。尝试了很多方法,如:报警管理器、工作管理器,但没有成功。通知不会触发,或者只是在稍后触发 有什么办法完成这项工作吗 报警功能: public void StartAlarm() { Calendar calendar = Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.set(Calendar.HOUR_OF_DAY,2

我想每天向用户发送脱机通知。尝试了很多方法,如:报警管理器、工作管理器,但没有成功。通知不会触发,或者只是在稍后触发

有什么办法完成这项工作吗

报警功能:

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);

}
报警接收器:

公共类AlarmReceiver扩展了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);
}
私有数据库参考myRef;
所有民族的私人ArrayList;
私人有限公司;
私有上下文ctx;
@凌驾
公共void onReceive(上下文、意图){
Toast.makeText(上下文,“在报警接收器中”,Toast.LENGTH_LONG.show();
ctx=上下文;
initPeoples();
}
公共字符串getCurrentDate(){
日历=Calendar.getInstance();
SimpleDataFormat mdformat=新的SimpleDataFormat(“d MMMM”);
字符串strDate=mdformat.format(calendar.getTime());
返回标准日期;
}
私人有限公司(){
FirebaseDatabase=FirebaseDatabase.getInstance();
myRef=database.getReference(“用户”);
myRef.keepSynced(真);
myRef.addListenerForSingleValueEvent(新的ValueEventListener()){
@凌驾
public void onDataChange(@NonNull DataSnapshot DataSnapshot){
allPeoples=newarraylist();
对于(DataSnapshot快照:DataSnapshot.getChildren()){
if(snapshot.getKey().equals(“教师”)| | snapshot.getKey().equals(“员工”)){
for(DataSnapshot:snapshot.child(“peoples”).getChildren(){
字符串生日=(字符串)faculty.child(“DOB”).getValue();
if(生日等于(getCurrentDate()){
字符串成员=生日;
增加(成员);
}
}
}否则{
for(DataSnapshot学生:snapshot.child(“peoples”).getChildren(){
字符串生日=(字符串)student.child(“DOB”).getValue();
if(生日等于(getCurrentDate()){
字符串成员=生日;
增加(成员);
}
}
}
}
numberOfPeoples=所有人.size();
ShowNotification();
}
@凌驾
已取消的公共void(@NonNull DatabaseError DatabaseError){
}
});
}
公开通知()
{
字符串CHANNEL_ID=“CHANNEL_1453”;
字符串CHANNEL_NAME=“生日通知”;
NotificationManagerCompat manager=NotificationManagerCompat.from(ctx);
if(Build.VERSION.SDK\u INT>=Build.VERSION\u code.O){
NotificationChannel通道=新的NotificationChannel(通道ID、通道名称、,
通知经理(重要性高);
manager.createNotificationChannel(频道);
}
Notification Notification=新建NotificationCompat.Builder(ctx,频道ID)
.setSmallIcon(R.drawable.ic_stat_notify)
.setContentTitle(“生日提醒”)
.setColor(颜色.绿色)
.setContentText(“单击查看今天谁过生日!”)
.setPriority(通知兼容优先级高)
.setAutoCancel(真)
.setCategory(通知Compat.CATEGORY_消息)
.setContentIntent(PendingEvent.getActivity(ctx,0,新的Intent(ctx,生日.类),PendingEvent.FLAG_UPDATE_CURRENT))
.build();
经理通知(454,通知);
}

}

看起来你做的一切都是对的,正如你在评论中所说,它正在处理nexus s,我想你也在清单中声明了接收者

因此,在经历了一周同样的问题后,结果如下:

在一些带有自定义操作系统的设备中,如小米,当你将应用程序从最近的列表中刷走时,操作系统会将其视为强制停止,因此所有服务和其他内容都将被删除。基于此,目前没有办法解决这个问题。他们回答说,他们正在与原始设备制造商合作解决这个问题

但根据我自己的经验,如果您使用work manager设置通知,它将被延迟,但您最终会收到它(至少在大多数情况下)

但如果你想让时间准确,目前没有办法进行


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

我可以看到一个可能的问题,并且可以在没有workmanager的情况下执行此操作(假设在通知运行时设备连接正常)

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

您可以在服务中显示通知,也可以安排下一次报警,因为
setExactAndAllowHileId
本身不是重复报警。因此,在你的接收器中,类似于:

@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上运行得非常好,但不是在所有设备上。因此,我仍然认为这不是在特定时间发送通知的可靠解决方案。

您是否在所有设备上都存在此问题?根据我的经验,它必须在emulator上运行良好。对吗?是的所有设备我的模拟器我