如果应用程序关闭,我如何显示通知?

How do i show a notification if app is closed?

我有一个应用程序,在早上 7 点需要检查白天是否有一些任务来显示他们的通知,工作流示例: 早上 7 点:检查今天是否有任何任务 -> 在下午 5 点找到一个 -> 在下午 5 点设置通知

我已经尝试实现 Worker、JobScheduler、AlarmReceiver、Broadcast Receiver、JobService,但如果应用程序至少不在后台,则不会触发任何内容。

关于实施什么来执行我需要的任何建议? 谢谢!

编辑:添加输出:adb shell dumpsys jobscheduler

    Implicit constraints:

BackgroundJobsController:
  Forced App Standby Feature enabled: true
  Force all apps standby: false
  Small Battery Device: false
  Force all apps standby for small battery device: false
  Plugged In: true
  Active uids: [u0a112 u0a113 u0a118 u0a119 u0a123 u0a124 u0a131 u0a140 u0a176 u0a190 u0a191 u0a192 u0a196 u0a201 u0a205 u0a212 u0a219 u0a228 u0a244 u0a253 u0a256 u0a287 u0a294 u0a433]
  Foreground uids: [u0a112 u0a119 u0a123 u0a131 u0a176 u0a190 u0a191 u0a196 u0a201 u0a205 u0a212 u0a244 u0a253 u0a256 u0a287 u0a294 u0a433]
  Except-idle + user whitelist appids: [1001, 2000, 10113, 10117, 10118, 10121, 10122, 10126, 10133, 10134, 10152, 10156, 10159, 10161, 10191, 10192, 10193, 10201, 10205, 10215, 10219, 10230, 10433, 10447]
  User whitelist appids: [10159, 10219, 10230, 10433, 10447]
  Temp whitelist appids: [10253]
  Exempted packages:
    User 0

      com.francesco.pickem
     

  Foreground UIDs: {1000=true, 1001=true, 1027=true, 1037=true, 1068=true, 10112=true, 10119=true, 10131=true, 10191=true, 10196=true, 10201=true, 10205=true, 10212=true, 10253=true, 10256=true, 10287=true, 10294=true, 99960=true}

  Cached UID->package map:
    10447: {com.francesco.pickem}

    <0>com.francesco.pickem
      ACTIVE: expirationTime=2051448531, windowSizeMs=600000, jobCountLimit=75, sessionCountLimit=75, executionTimeInWindow=0, bgJobCountInWindow=0, executionTimeInMaxPeriod=0, bgJobCountInMaxPeriod=0, sessionCountInWindow=0, inQuotaTime=0, jobCountExpirationTime=0, jobCountInRateLimitingWindow=0, sessionCountExpirationTime=0, sessionCountInRateLimitingWi
ndow=0
    

Historical stats at 2021-03-26-10-27-54 (-1h24m10s391ms) over +30m1s114ms:
  u0a447 / com.francesco.pickem:
    9x pending 61% 3x active-top
    11x canceled
  Max concurrency: 8 total, 2 foreground


Historical stats at 2021-03-26-09-56-28 (-1h55m36s45ms) over +31m25s654ms:

  u0a447 / com.francesco.pickem:
    24x pending 99% 8x active-top
    23x canceled
  Max concurrency: 8 total, 2 foreground



Historical stats at 2021-03-26-08-41-09 (-3h10m55s336ms) over +33m6s679ms:
  u0a446 / com.francesco.pickem:
    3x pending 2% 1x active-top
    3x canceled
  u0a447 / com.francesco.pickem:
    6x pending 14% 2x active-top
    5x canceled
  Max concurrency: 6 total, 2 foreground


Current stats at 2021-03-26-10-57-55 (-54m9s277ms) over +54m9s278ms:
  u0a447 / com.francesco.pickem:
    27x pending 22% 7x active-top
    27x canceled
  Max concurrency: 6 total, 3 foreground

  Job history:

       -11m26s720ms   START: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks
       -11m26s715ms   START: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks
       -11m21s695ms START-P: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks

        -5m42s273ms  STOP-P: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected
        -5m42s260ms  STOP-P: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected
        -5m42s208ms    STOP: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected

        -5m41s321ms   START: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks
        -5m41s319ms   START: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks
        -5m36s313ms START-P: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks

        -5m04s703ms  STOP-P: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks canceled
        -5m04s683ms   START: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks
        -5m04s683ms  STOP-P: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks canceled
        -5m04s671ms   START: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks
        -4m59s730ms    STOP: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks canceled
        -4m59s709ms START-P: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks
        -4m13s802ms  STOP-P: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks canceled
        -4m13s777ms   START: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks
        -4m13s776ms  STOP-P: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks canceled
        -4m13s766ms   START: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks
        -4m08s818ms    STOP: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks canceled
        -4m08s800ms START-P: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks
        -1m43s958ms    STOP: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected
        -1m43s842ms  STOP-P: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected
        -1m43s839ms  STOP-P: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected

        -1m41s389ms   START: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks
        -1m41s387ms   START: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks
        -1m36s367ms START-P: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks

          -32s680ms  STOP-P: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected
          -32s668ms  STOP-P: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected
          -32s628ms    STOP: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected

          -29s854ms   START: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks
          -29s851ms   START: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks
          -24s834ms START-P: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks

          -19s005ms  STOP-P: #u0a447/3 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected
          -18s975ms  STOP-P: #u0a447/2 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected
          -18s967ms    STOP: #u0a447/4 com.francesco.pickem/.NotificationsService.BackgroundTasks unexpectedly disconnected


Pending queue:

Active jobs:
  Slot #0: inactive since -6s437ms, stopped because: app called jobFinished
  Slot #1: inactive since -5s631ms, stopped because: app called jobFinished
  Slot #2: inactive since -8s14ms, stopped because: last work dequeued
  Slot #3: inactive since -19s778ms, stopped because: cancel() called by app, callingUid=10196 uid=10196 jobId=-439
  Slot #4: inactive since -19s751ms, stopped because: cancel() called by app, callingUid=10196 uid=10196 jobId=-459
  Slot #5: inactive since -5m41s932ms, stopped because: cancel() called by app, callingUid=10196 uid=10196 jobId=-439
  Slot #6: inactive since -1h22m42s81ms, stopped because: app called jobFinished
  Slot #7: inactive since -1h22m42s441ms, stopped because: app called jobFinished
  Slot #8: inactive
  Slot #9: inactive
  Slot #10: inactive
  Slot #11: inactive
  Slot #12: inactive
  Slot #13: inactive
  Slot #14: inactive
  Slot #15: inactive

mReadyToRock=true
mReportedActive=false

Concurrency:
  Screen state: current ON  effective ON
  Last screen ON : 2021-03-26 08:46:09.717 (-2h5m54s784ms)
  Last screen OFF: 2021-03-26 08:42:01.879 (-2h10m2s622ms)

  Current max jobs:

  Config={tot=8 bg min/max=2/6} Running[FG/BG (total)]: 0 / 0 (0) Pending: 0 / 0 (0) Actual max: 0 / 0 (0) Res BG: 0 Starting: 0 / 0 (0) Total: 0 / 0 (0)

  mLastMemoryTrimLevel: 0
  Stats:
    assignJobsToContexts: count=413694, total=704690,5ms, avg=1,703ms, max calls/s=3604 max dur/s=71812,1ms max time=71812,0ms
    refreshSystemState: count=80114, total=157823,1ms, avg=1,970ms, max calls/s=10 max dur/s=26915,4ms max time=26915,4ms

PersistStats: FirstLoad: 124/28/25 LastSave: 119/27/24


C:\Users\Francesco\AndroidStudioProjects\LeagueOfLegendsApp\PickEm2020>

编辑 2:

C:\Users\Francesco\AndroidStudioProjects\LeagueOfLegendsApp\PickEm2020>adb shell dumpsys jobscheduler | find  "JOB #u0a447"
  JOB #u0a447/3: d3eed36 com.francesco.pickem/.NotificationsService.BackgroundTasks
  JOB #u0a447/4: aadcfa5 com.francesco.pickem/.NotificationsService.BackgroundTasks

我建议您也使用 WorkerManager 并设置 PeriodicWorkRequest。 当然,该应用程序应该 运行 至少一次。 然后在应用程序打开时定期检查工作是否计划周全。

但是正如文档所说,PeriodicWorkRequest 应该足够了:

Robust Scheduling

WorkManager allows you to schedule work to run one- time or repeatedly using flexible scheduling windows. Work can be tagged and named as well, allowing you to schedule unique, replaceable work and monitor or cancel groups of work together. Scheduled work is stored in an internally managed SQLite database and WorkManager takes care of ensuring that this work persists and is rescheduled across device reboots. In addition, WorkManager adheres to power-saving features and best practices like Doze mode, so you don’t have to worry about it.

WorkerManager documentation

是的,它非常冗长,也许您可​​以使用 https://pastebin.com/

您看到的停止只是通知作业开始然后停止,因为它已完成。

转储中有几个有趣的部分可以分析 jobscheduler 问题。要知道这份工作是否计划周全,请至少分享名为:“已注册的 XX 工作”的部分。它看起来像这样:

Registered 65 jobs:
  JOB #1000/800: f44b386 android/com.android.server.pm.BackgroundDexOptService
    1000 tag=*job*/android/com.android.server.pm.BackgroundDexOptService
    Source: uid=1000 user=0 pkg=android
    JobInfo:
      Service: android/com.android.server.pm.BackgroundDexOptService
      PERIODIC: interval=+1d0h0m0s0ms flex=+1d0h0m0s0ms
      Requires: charging=true batteryNotLow=false deviceIdle=true
      Backoff: policy=1 initial=+30s0ms
      Has early constraint
      Has late constraint
    Required constraints: CHARGING TIMING_DELAY DEADLINE IDLE [0xc0000005]
    Satisfied constraints: CHARGING BATTERY_NOT_LOW TIMING_DELAY DEVICE_NOT_DOZING BACKGROUND_NOT_RESTRICTED [0x82400003]
    Unsatisfied constraints: DEADLINE IDLE [0x40000004]
    Uid: active
    Tracking: BATTERY IDLE TIME
    Standby bucket: ACTIVE
    Enqueue time: -20h45m53s452ms
    Run time: earliest=-27m55s737ms, latest=+23h32m4s263ms
    Last successful run: 2021-03-25 16:56:16
    Last run heartbeat: 885
    Ready: false (job=false user=true !pending=true !active=true !backingup=true comp=true)

您还可以使用 grep 过滤您的应用程序包名称或 uid。如果我关注你最后的评论,它可能是:

adb shell dumpsys jobscheduler | grep "JOB #u0a447" -A 22

我找到了解决这个问题的好方法,如果你接受前台服务,你可以实现它:

  1. 创建通知渠道:
private static final String CHANNEL_ID = "1";

    private void createNotificationChannel() {
        // Create the NotificationChannel, but only on API 26+ because
        // the NotificationChannel class is new and not in the support library
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            CharSequence name = "channel_name";
            String description = "channel_description";
            int importance = NotificationManager.IMPORTANCE_DEFAULT;
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID, name, importance);
            channel.setDescription(description);
            // Register the channel with the system; you can't change the importance
            // or other notification behaviors after this
            NotificationManager notificationManager = getSystemService(NotificationManager.class);
            notificationManager.createNotificationChannel(channel);
        }
    }

  1. 实现前台服务的启动(我有一个开关可以打开它on/off):
        switch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton compoundButton, boolean b) {

                if (b){
                    createNotificationChannel();
                    
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        
                        Intent serviceIntent = new Intent(SettingsActivity.this, ForegroundService.class);
                        ContextCompat.startForegroundService(SettingsActivity.this, serviceIntent);
                    }
                    serviceIntent);
                }else {
                    
                    Intent serviceIntent = new Intent(SettingsActivity.this, ForegroundService.class);
                    stopService(serviceIntent);
                }

            }
        });
  1. 如果您需要检查前台服务是否启动:
    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;
    }
  1. 前台服务class:
public class ForegroundService extends Service {

    DatabaseHelper databaseHelper;

    public static final String TAG ="ForegroundService";
    @Override
    public void onCreate() {
        super.onCreate();
        databaseHelper = new DatabaseHelper(getBaseContext());
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String input = intent.getStringExtra("inputExtra");
        Intent notificationIntent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this,
                0, notificationIntent, 0);
        Notification notification = new NotificationCompat.Builder(this, "1")
                .setContentTitle("Foreground Service")
                .setContentText(input)
                .setSmallIcon(R.drawable.icon)
                .setContentIntent(pendingIntent)
                .build();
        startForeground(1, notification);

        setAlarmManager();

        return START_NOT_STICKY;
    }


    @Override
    public void onDestroy() {
        super.onDestroy();
    }
    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }


    private void setAlarmManager() {
        Intent intent = new Intent(this, AlarmReceiver.class);
        intent.putExtra("TYPE", "TEST");
        PendingIntent sender = PendingIntent.getBroadcast(this, 2, intent, 0);
        AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
        long l = new Date().getTime();
        if (l < new Date().getTime()) {
            l += 86400000; // start at next 24 hour
        }
        
        am.setRepeating(AlarmManager.RTC_WAKEUP, l, 60000, sender); // 86400000
    }



}


  1. 不要忘记在清单上添加:
        <service
            android:name=".NotificationsService.ForegroundService">

        </service>

每日重复服务取自 NyanLH Solution to this other problem