推送 Push\通知\渠道推送 JobScheduler 本地推送 WorkManager 后台执行限制

本文主要是介绍推送 Push\通知\渠道推送 JobScheduler 本地推送 WorkManager 后台执行限制,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

通知

通知概览如下:
创建通知
后台执行限制
后台优化
后台处理指南 对于我们的使用场景,Android官方推荐使用WorkManager
使用workManager实现每日定时推送通知
使用 WorkManager 实现定期通知
android 定时推送提醒,使用workManager实现每日定时推送通知

Demo工程

https://gitee.com/my739168148/local-push.git

通知的兼容性

自 Android 1.0 开始,通知系统界面以及与通知相关的 API 就在不断发展。如需在使用最新通知 API 功能的同时仍然支持旧版设备,请使用支持库通知 API:NotificationCompat 及其子类,以及 NotificationManagerCompat。这样一来,您就无需编写条件代码来检查 API 级别,因为这些 API 会为您代劳。

NotificationCompat 随着平台的发展不断更新,旨在纳入最新的方法。需要注意的是,某个方法在 NotificationCompat 中可用并不能保证可以在旧版设备上提供相应功能。在某些情况下,调用新引入的 API 会导致旧版设备出现空操作。例如,NotificationCompat.addAction() 仅在搭载 Android 4.1(API 级别 16)及更高版本的设备上显示操作按钮。

Android 4.1,API 级别 16

推出了展开式通知模板(称为通知样式),可以提供较大的通知内容区域来显示信息。用户可以通过单指向上/向下滑动的手势来展开通知。
还支持以按钮的形式向通知添加其他操作。
允许用户在设置中按应用关闭通知。

Android 4.4,API 级别 19 和 20

向 API 中添加了通知监听器服务。
API 级别 20 中新增了 Android Wear(现已更名为 Wear OS)支持。

Android 5.0,API 级别 21

  • 推出了锁定屏幕和提醒式通知。
  • 用户现在可以将手机设为勿扰模式,并配置允许哪些通知在设备处于“仅限优先事项”模式时打扰他们。
  • 向 API集添加了通知是否在锁定屏幕上显示的方法 (setVisibility()),以及指定通知文本的“公开”版本的方法。 添加了setPriority() 方法,告知系统通知的“干扰性”(例如,将其设为“高”可使通知以提醒式通知的形式显示)。
  • 向 Android Wear(现已更名为 Wear OS)设备添加了通知堆栈支持。使用 setGroup() 将通知放入堆栈。请注意,平板电脑和手机尚不支持通知堆栈。通知堆栈以后会称为组或 Bundle。

Android 7.0,API 级别 24

  • 重新设置了通知模板的样式以强调主打图片和头像。
  • 添加了三个通知模板:一个用于短信应用,另外两个用于借助展开式选项和其他系统装饰来装饰自定义内容视图。
  • 向手持设备(手机和平板电脑)添加了对通知组的支持。使用与 Android 5.0(API 级别 21)中推出的 Android Wear(现已更名为 Wear OS)通知堆栈相同的 API。
  • 用户可以使用内嵌回复功能直接在通知内进行回复(他们输入的文本将转发到通知的父应用)。

Android 8.0,API 级别 26

  • 现在必须将各个通知放入特定渠道中。
  • 现在,用户可以按渠道关闭通知,而非关闭来自某个应用的所有通知。
  • 包含有效通知的应用将在主屏幕/启动器屏幕上相应应用图标的上方显示通知“标志”。
  • 现在,用户可以从抽屉式通知栏中暂停某个通知。您可以为通知设置自动超时时间。
  • 您还可以设置通知的背景颜色。
  • 部分与通知行为相关的 API 已从 Notification 移至 NotificationChannel。例如,在搭载 Android 8.0 及更高版本的设备中,使用 NotificationChannel.setImportance(),而非NotificationCompat.Builder.setPriority()。

Android 13.0,API 级别 33

  • 添加了一项运行时权限。若要让您的应用能够发送非豁免通知,用户必须向您的应用授予此权限。

本地推送

顾虑1---- 后台执行限制

Android 8.0(API 级别 26)对应用在后台运行时可以执行的操作施加了限制。

在后台中运行的 Service 会消耗设备资源,这可能会降低用户体验。 为了缓解这一问题,系统对这些 Service 施加了一些限制。

系统可以区分前台和后台应用。 (用于 Service 限制目的的后台定义与内存管理使用的定义不同;一个应用按照内存管理的定义可能处于后台,但按照能够启动 Service 的定义又处于前台。)如果满足以下任意条件,应用将被视为处于前台:

  • 具有可见 Activity(不管该 Activity 已启动还是已暂停)。
  • 具有前台 Service。
  • 另一个前台应用已关联到该应用(不管是通过绑定到其中一个 Service,还是通过使用其中一个内容提供程序)。 例如,如果另一个应用绑定到该应用的 Service,那么该应用处于前台:
    - IME
    - 壁纸 Service
    - 通知侦听器
    - 语音或文本 Service
    如果以上条件均不满足,应用将被视为处于后台。

处于前台时,应用可以自由创建和运行前台与后台 Service。 进入后台时,在一个持续数分钟的时间窗内,应用仍可以创建和使用 Service。 在该时间窗结束后,应用将被视为处于空闲状态。 此时,系统将停止应用的后台 Service,就像应用已经调用 Service 的 Service.stopSelf() 方法一样。

在这些情况下,后台应用将被置于一个临时白名单中并持续数分钟。 位于白名单中时,应用可以无限制地启动 Service,并且其后台 Service 也可以运行。 处理对用户可见的任务时,应用将被置于白名单中,例如:

  • 处理一条高优先级 Firebase 云消息传递 (FCM)消息。
  • 接收广播,例如短信/彩信消息。
  • 从通知执行 PendingIntent。
  • 在 VPN 应用将自己提升为前台进程前开启 VpnService。

JobScheduler

JobScheduler是Android中的一个调度器,用于在特定条件下执行后台任务。它可以用于实现应用在被杀死后仍能推送消息的功能。

要使用JobScheduler实现应用被杀死后仍能推送消息,你需要以下步骤:
1、创建一个继承自JobService的类,用于执行后台任务。这个类负责在后台执行任务并推送消息。

public class MyJobService extends JobService {@Overridepublic boolean onStartJob(JobParameters params) {// 在这里执行后台任务和消息推送逻辑return true;}@Overridepublic boolean onStopJob(JobParameters params) {return false;}
}

2、在你的应用中注册这个JobService。在AndroidManifest.xml文件中添加以下代码:

<serviceandroid:name=".MyJobService"android:permission="android.permission.BIND_JOB_SERVICE" />

3、在你的应用中调用JobScheduler来安排任务。你可以在合适的时机调用以下代码来安排任务:

JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
ComponentName componentName = new ComponentName(this, MyJobService.class);
JobInfo jobInfo = new JobInfo.Builder(JOB_ID, componentName).setRequiresCharging(true) // 设置任务的条件,例如需要充电时执行.setPersisted(true) // 设置任务在设备重启后是否继续执行.build();
jobScheduler.schedule(jobInfo);

实现方案——WorkManager

基于WorkManager实现本地推送。
WorkerManager实现本地推送时,需要自启动权限,因为推送通知消息是应用自身通知的。
workerManager不会触发Activity的onCreate,但是会触发Application的onCreate,Application的onCreate触发后,会触发Service或者receiver?
在这里插入图片描述

2023-07-19 20:17:58.108 975-975/? D/Zygote: Forked child process 13725
2023-07-19 20:17:58.110 1887-2484/? I/ActivityManager: Start proc 13725:cn.jj.localpush/u0a819 for next_app {cn.jj.localpush/cn.jj.localpush.MainActivity}
2023-07-19 20:17:58.111 1887-2484/? D/OplusUIFirst_FB: cn.jj.localpush proc start: 10819 13725 false cn.jj.localpush
2023-07-19 20:17:58.130 13725-13725/? I/cn.jj.localpush: Late-enabling -Xcheck:jni
2023-07-19 20:17:58.154 13725-13725/? E/cn.jj.localpush: Unknown bits set in runtime_flags: 0x40000000
2023-07-19 20:17:58.158 18098-18108/? I/adbd: jdwp connection from 13725
2023-07-19 20:17:58.163 13725-13725/? I/OneTrace: Mark active for pid=13725? true
2023-07-19 20:17:58.163 13725-13725/? D/cutils-dev: otrace_set_tracing_enabled? true
2023-07-19 20:17:58.167 13725-13770/? D/cutils-dev: properties changed in otrace_seq_number_changed!
2023-07-19 20:17:58.177 13725-13725/? V/studio.deploy: Startup agent attached to VM
2023-07-19 20:17:58.177 13725-13725/? V/studio.deploy: No existing instrumentation found. Loading instrumentation from instruments-88564b4d.jar
2023-07-19 20:17:58.180 13725-13725/? W/cn.jj.localpush: DexFile /data/data/cn.jj.localpush/code_cache/.studio/instruments-88564b4d.jar is in boot class path but is not in a known location
2023-07-19 20:17:58.183 13725-13725/? V/studio.deploy: Applying transforms with cached classes
2023-07-19 20:17:58.206 13725-13725/? W/cn.jj.localpush: Redefining intrinsic method java.lang.Thread java.lang.Thread.currentThread(). This may cause the unexpected use of the original definition of java.lang.Thread java.lang.Thread.currentThread()in methods that have already been compiled.
2023-07-19 20:17:58.206 13725-13725/? W/cn.jj.localpush: Redefining intrinsic method boolean java.lang.Thread.interrupted(). This may cause the unexpected use of the original definition of boolean java.lang.Thread.interrupted()in methods that have already been compiled.
2023-07-19 20:17:58.215 13725-13725/? D/CompatibilityChangeReporter: Compat change id reported: 171979766; UID 10819; state: ENABLED
2023-07-19 20:17:58.215 13725-13725/? I/oplus.android.OplusFrameworkFactoryImpl: get feature:IOplusAutoResolutionFeature
2023-07-19 20:17:58.215 13725-13725/? I/oplus.android.OplusFrameworkFactoryImpl: getOplusAutoResolutionFeature
2023-07-19 20:17:58.275 13725-13725/? I/OplusFeatureCache: Milliseconds spent on init(): 59
2023-07-19 20:17:58.280 13725-13725/cn.jj.localpush D/CompactWindowAppManager: initCompactApplicationInfo  MODE_COMPACT  
2023-07-19 20:17:58.346 13725-13725/cn.jj.localpush W/libc: Access denied finding property "ro.odm.prev.product.name"
2023-07-19 20:17:58.361 13725-13725/cn.jj.localpush D/LoadedApk: mApplicationInfo overrideDisplayId:null
2023-07-19 20:17:58.366 13725-13725/cn.jj.localpush W/ziparchive: Unable to open '/data/data/cn.jj.localpush/code_cache/.overlay/base.apk/classes3.dm': No such file or directory
2023-07-19 20:17:58.367 13725-13725/cn.jj.localpush W/ziparchive: Unable to open '/data/app/~~ND6I7AkQy9joo_T1SaZaIQ==/cn.jj.localpush-K36ru8wfOsk3TnC-vnUD1g==/base.dm': No such file or directory
2023-07-19 20:17:58.367 13725-13725/cn.jj.localpush W/ziparchive: Unable to open '/data/app/~~ND6I7AkQy9joo_T1SaZaIQ==/cn.jj.localpush-K36ru8wfOsk3TnC-vnUD1g==/base.dm': No such file or directory
2023-07-19 20:17:58.679 13725-13725/cn.jj.localpush I/Quality: ActivityThread: createClassLoader delay 317 /data/app/~~ND6I7AkQy9joo_T1SaZaIQ==/cn.jj.localpush-K36ru8wfOsk3TnC-vnUD1g==/base.apk 13725
2023-07-19 20:17:58.683 13725-13725/cn.jj.localpush I/Quality: ActivityThread: createOrUpdateClassLoaderLocked delay 322 cn.jj.localpush 13725
2023-07-19 20:17:58.746 13725-13725/cn.jj.localpush V/GraphicsEnvironment: ANGLE Developer option for 'cn.jj.localpush' set to: 'default'
2023-07-19 20:17:58.747 13725-13725/cn.jj.localpush V/GraphicsEnvironment: ANGLE GameManagerService for cn.jj.localpush: false
2023-07-19 20:17:58.748 13725-13725/cn.jj.localpush V/GraphicsEnvironment: App is not on the allowlist for updatable production driver.
2023-07-19 20:17:58.758 13725-13725/cn.jj.localpush D/NetworkSecurityConfig: No Network Security Config specified, using platform default
2023-07-19 20:17:58.760 13725-13725/cn.jj.localpush D/NetworkSecurityConfig: No Network Security Config specified, using platform default
2023-07-19 20:17:58.781 13725-13725/cn.jj.localpush D/WM-WrkMgrInitializer: Initializing WorkManager with default configuration.
2023-07-19 20:17:58.811 1887-4093/? W/PackageManager: package unknown uid 10819 pid 13725 call SetEnabledSetting(cn.jj.localpush component = androidx.work.impl.background.systemjob.SystemJobService, 1, 0)
2023-07-19 20:17:58.836 13725-13725/cn.jj.localpush I/JJWorld.MyApplication: MyApplication onCreate......
2023-07-19 20:17:58.838 13725-13725/cn.jj.localpush D/OplusActivityManager: get AMS extension: android.os.BinderProxy@99cd4ca
2023-07-19 20:17:58.841 1887-3837/? I/OplusHansManager: freeze uid: 10819 cn.jj.localpush pids: [13725] scene: LcdOn from: Preload_F
2023-07-19 20:19:06.707 1887-1887/? I/OplusHansManager: unfreeze uid: 10819 cn.jj.localpush pids: [13725] reason: Preload_UF scene: LcdOn
2023-07-19 20:19:06.716 13725-13788/cn.jj.localpush I/Quality: SlowSQLite: /d9a0facb11102c97f3b4af6fce499434/ cost= /67869
2023-07-19 20:19:06.740 13725-13761/cn.jj.localpush W/System: A resource failed to call close. 
2023-07-19 20:19:06.778 13725-13788/cn.jj.localpush D/CompatibilityChangeReporter: Compat change id reported: 160794467; UID 10819; state: ENABLED
2023-07-19 20:19:06.800 13725-13788/cn.jj.localpush D/CompatibilityChangeReporter: Compat change id reported: 194532703; UID 10819; state: ENABLED
2023-07-19 20:19:06.876 13725-13874/cn.jj.localpush I/JJWorld.MyWorkManager: doWork.....
2023-07-19 20:19:06.906 13725-13788/cn.jj.localpush I/WM-WorkerWrapper: Worker result SUCCESS for Work [ id=85711d49-0d34-45e7-bf15-b1f00cb1d5d7, tags={ cn.jj.localpush.MyWorkManager } ]
2023-07-19 20:19:06.909 1887-4107/? W/PackageManager: package unknown uid 10819 pid 13725 call SetEnabledSetting(cn.jj.localpush component = androidx.work.impl.background.systemalarm.RescheduleReceiver, 2, 0)
2023-07-19 20:19:16.724 1887-3803/? I/OplusHansManager: freeze uid: 10819 cn.jj.localpush pids: [13725] scene: LcdOn

需要引入资源

dependencies {implementation "androidx.work:work-runtime:2.7.0"
}

无额外权限

测试

实际测试,测试设备OPPO Find N,通知渠道优先级为 IMPORTANCE_DEFAULT,杀端时间1分钟:
1、应用在后台、杀端(无自启动权限)情况下,收不到推送,期间的推送消息在应用回前台后展示。
2、应用杀死后。OPPO FIND N设备开启 允许完全后台行为时。无法收到本地推送消息。
3、应用杀死后。开启自启动权限与允许完全后台行为时。可以收到本地推送消息。
4、应用杀死后。开启自启动权限时。可以收到本地推送消息。
5、应用在前台,可以收到推送消息。

注意:
1、发送多条数据的表现。最新的通知消息会覆盖上一条通知消息。
2、WorkManager会在设备空闲时执行任务,所以不保证任务能够准确按照设定的时间执行。任务可能会延后执行。

实际测试,测试设备OPPO Find N Android13,通知渠道优先级为 IMPORTANCE_HIGH,杀端时间1分钟:
1、应用在后台,收不到推送消息。
2、应用杀死后,没有开特殊权限的话,收不到推送。
3、应用杀死后。OPPO FIND N设备开启 自启动权限与允许完全后台行为时。可以收到本地推送消息。
4、应用杀死后。OPPO FIND N设备开启 自启动权限时。可以收到本地推送消息。
5、应用杀死后。OPPO FIND N设备开启 允许完全后台行为时。无法收到本地推送消息。
6、应用在前台,可以收到推送消息。

WorkManager是基于系统的JobScheduler和AlarmManager来调度任务的,而这些系统组件在应用处于后台时的行为是受限的。具体来说,如果应用处于后台,WorkManager可能会受到以下限制:

  • 1、执行时间延迟:当应用处于后台时,系统可能会推迟或延迟任务的执行,以降低对设备性能和电池寿命的影响。这意味着任务可能不会按照预期的时间立即执行。

  • 2、网络限制:在Android9及更高版本中,应用在后台时访问网络会受到限制。这意味着在后台时,应用可能无法建立网络连接或发送/接收网络请求。这也可能导致无法及时收到推送消息。

  • 3、自启动权限:某些设备厂商对后台任务的执行进行了更严格的限制,并要求用户授予自启动权限。这意味着除非用户明确允许应用在后台启动任务,否则任务可能无法正常执行。

WorkerManager实战

应用在后台,可以打印日志

   public void WorkerManagerTest() {
//        MyWorkManager myWorkManager = new MyWorkManager(activity,null);
//
//        Constraints constraints = null;
//        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
//            constraints = new Constraints.Builder().setTriggerContentMaxDelay(1 * 60,
//                    TimeUnit.SECONDS).build();
//        }@SuppressLint("IdleBatteryChargingConstraints") Constraints constraints = new Constraints.Builder().setRequiredNetworkType(NetworkType.UNMETERED)  // 网络状态.setRequiresBatteryNotLow(true)                 // 不在电量不足时执行.setRequiresCharging(true)                      // 在充电时执行.setRequiresStorageNotLow(true)                 // 不在存储容量不足时执行                 // 在待机状态下执行,需要 API 23.build();OneTimeWorkRequest workRequest = new OneTimeWorkRequest.Builder(MyWorkManager.class).setInitialDelay(20 * 1, TimeUnit.SECONDS).setConstraints(constraints).build();WorkManager.getInstance(activity).enqueue(workRequest);Log.i(TAG, "WorkerManagerTest working");}
2023-08-20 10:45:41.947 20841-20841/cn.jj.localpush I/JJWorld.MyApplication: MyApplication onCreate......
2023-08-20 10:45:42.039 20841-20841/cn.jj.localpush I/JJWorld.MainActivity: createNotificationChannel SDK version is ok....
2023-08-20 10:45:46.286 20841-20841/cn.jj.localpush I/JJWorld.MainActivity: WorkerManagerTest working
2023-08-20 10:46:06.375 20841-20950/cn.jj.localpush I/JJWorld.MyWorkManager: doWork

杀端

日志不会打印

杀端 开启自启动权限、完全后台行为

会打印日志

2023-08-20 11:11:32.873 29779-29779/cn.jj.localpush I/JJWorld.MyApplication: MyApplication onCreate......
2023-08-20 11:11:32.995 29779-29808/cn.jj.localpush I/JJWorld.MyWorkManager: doWork

魅族渠道

魅族渠道收到Push消息后的启动顺序如下,

  1. applicaition的oncreate
  2. MeizuPushReceiver中的onNotificationClicked
  3. 然后是主acitivity的onCreate.
    此外,应用在后台以及应用杀端情况下,魅族的主activity的onDestory会触发,每次应用拉起前端界面都是onCreate。

小米渠道

服务端发送消息类型

自定义点击动作消息

小米渠道拉起首页方式

小米收到的自定义参数可以在

public void onNotificationMessageClicked(Context context, MiPushMessage message)

中解析。解析之后拉起应用首页时,Context的类型为application,因此,需要拉起首页的intent消息中要添加下面的Flag标志位,表示如果当前没有存在activity的task任务栈的话,则创建task任务栈,如果存在task任务栈,则将activity压入task任务栈。

intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

不同Android版本差异

小米渠道服务端通过API发送自定义点击动作消息后,不同Android版本的小米手机表现不一样。对于android8.0的小米5s来说,点击推送消息之后不会启动activity,而对于android12.0的红米K50来说,点击推送消息之后,会启动一个activity。

Android 8.0

在这里插入图片描述

Android 12.0

点击推送消息后,android12.0手机会创建一个activity. 类名为

com.xiaomi.mipush.sdk.NotificationClickedActivity

在这里插入图片描述
因为小米渠道Android12版本在点击推送消息时会拉起activity,因此小米渠道没法判断无法根据应用在前台或后台,从而进行不同的处理,比如应用在前台时,仅传递自定义参数不拉起界面,应用在后台时,即传递参数又拉起界面。所以小米渠道仅判断杀端与不杀端的情况。
而华为和OPPO可以在MsgBus中判断应用在前台或者在后台,因此,可以根据应用在前台和后台的结果进行把不同逻辑的处理。

从后台拉起activity界面的方式

从后台拉起指定页面有两种常见的方式。

基于ActivityManager的moveTaskToFront方式

该方式的核心思想是获取对应于当前应用的任务栈,获取到任务栈之后使用将任务栈推到应用前台。

ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);

当context为activity时,任务栈可以通过getTaskId()直接获取,此时任务从后台到前台的语句为:

am.moveTaskToFront(getTaskId(), ActivityManager.MOVE_TASK_WITH_HOME);

当context为applicaition时,任务栈没法直接获取,需要利用getRunningTasks判断当前运行的任务栈。

List<ActivityManager.RunningTaskInfo> taskList = am.getRunningTasks(100);

如果其中任务栈对应的package名称与当前程序对应的packageName匹配的话,则将此task推到前台。

for (ActivityManager.RunningTaskInfo rti : taskList) {//找到当前应用的task,并启动task的栈顶activity,达到程序切换到前台if (rti.topActivity.getPackageName().equals(context.getPackageName())) {am.moveTaskToFront(rti.id, 0);hasLaunch = true;}
}

基于Intent的startActivity方式

此工具类的getLaunchIntentForPackage方法仅需packageName以及application类型的Context,就可以获取打开启动页activity的intent。

package cn.jj.push.aggpushsdk.utils;import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;import java.time.temporal.TemporalAccessor;
import java.util.List;public class LaunchUtils {private static final String TAG = "JJPushSDK.LaunchUtils";public static Intent getLaunchIntentForPackage(String packageName, Context context) {// First see if the package has an INFO activity; the existence of// such an activity is implied to be the desired front-door for the// overall package (such as if it has multiple launcher entries).PackageManager pm = context.getPackageManager();Intent intentToResolve = new Intent();intentToResolve.setAction(Intent.ACTION_MAIN);intentToResolve.addCategory(Intent.CATEGORY_INFO);intentToResolve.setPackage(packageName);List<ResolveInfo> ris = pm.queryIntentActivities(intentToResolve, 0);// Otherwise, try to find a main launcher activity.if (ris == null || ris.size() <= 0) {// reuse the intent instanceintentToResolve.removeCategory(Intent.CATEGORY_INFO);intentToResolve.addCategory(Intent.CATEGORY_LAUNCHER);intentToResolve.setPackage(packageName);ris = pm.queryIntentActivities(intentToResolve, 0);}if (ris == null || ris.size() <= 0) {return null;}Intent intent = new Intent(intentToResolve);intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);intent.setClassName(ris.get(0).activityInfo.packageName,ris.get(0).activityInfo.name);LogUtils2.i(TAG,"xiaomi start. packageName:" + ris.get(0).activityInfo.packageName);LogUtils2.i(TAG,"xiaomi start. activityName:" + ris.get(0).activityInfo.name);return intent;}
}

intent获取到之后,打开首页就可以了

 Intent launchIntentForPackage = LaunchUtils.getLaunchIntentForPackage(getPackageName(), this);startActivity(launchIntentForPackage);

华为与OPPO推送

华为与OPPO推送,可以选择打开指定页面的方式,然后从指定页面的intent中解析自定义参数信息。
为了与华为、oppo的意图过滤器匹配,可以在创建一个activity,在activity的oncreate中解析自定义参数信息,通过在oncreate的结束,调用finsh方法,将activity关闭。

  1. oncreate 通过intent 解析自定义参数
  2. 利用activity本身的context实现页面的跳转,跳转到启动页,通过将自定义参数存储。
  3. oncreate的结束,调用finish(),关闭当前的中间activity。

注意:此activity不设置ContentView,因此,此activity不会有界面。
通常情况下的activity都包括了setContentView语句。

setContentView(R.layout.activity_main);
@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);LogUtils2.i(TAG, "onCreate");// intent参数解析// 页面跳转finish();  	}

这篇关于推送 Push\通知\渠道推送 JobScheduler 本地推送 WorkManager 后台执行限制的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/164071

相关文章

poj 2135 有流量限制的最小费用最大流

题意: 农场里有n块地,其中约翰的家在1号地,二n号地有个很大的仓库。 农场有M条道路(双向),道路i连接着ai号地和bi号地,长度为ci。 约翰希望按照从家里出发,经过若干块地后到达仓库,然后再返回家中的顺序带朋友参观。 如果要求往返不能经过同一条路两次,求参观路线总长度的最小值。 解析: 如果只考虑去或者回的情况,问题只不过是无向图中两点之间的最短路问题。 但是现在要去要回

poj 3422 有流量限制的最小费用流 反用求最大 + 拆点

题意: 给一个n*n(50 * 50) 的数字迷宫,从左上点开始走,走到右下点。 每次只能往右移一格,或者往下移一格。 每个格子,第一次到达时可以获得格子对应的数字作为奖励,再次到达则没有奖励。 问走k次这个迷宫,最大能获得多少奖励。 解析: 拆点,拿样例来说明: 3 2 1 2 3 0 2 1 1 4 2 3*3的数字迷宫,走两次最大能获得多少奖励。 将每个点拆成两个

poj 2195 bfs+有流量限制的最小费用流

题意: 给一张n * m(100 * 100)的图,图中” . " 代表空地, “ M ” 代表人, “ H ” 代表家。 现在,要你安排每个人从他所在的地方移动到家里,每移动一格的消耗是1,求最小的消耗。 人可以移动到家的那一格但是不进去。 解析: 先用bfs搞出每个M与每个H的距离。 然后就是网络流的建图过程了,先抽象出源点s和汇点t。 令源点与每个人相连,容量为1,费用为

poj 3068 有流量限制的最小费用网络流

题意: m条有向边连接了n个仓库,每条边都有一定费用。 将两种危险品从0运到n-1,除了起点和终点外,危险品不能放在一起,也不能走相同的路径。 求最小的费用是多少。 解析: 抽象出一个源点s一个汇点t,源点与0相连,费用为0,容量为2。 汇点与n - 1相连,费用为0,容量为2。 每条边之间也相连,费用为每条边的费用,容量为1。 建图完毕之后,求一条流量为2的最小费用流就行了

maven 编译构建可以执行的jar包

💝💝💝欢迎莅临我的博客,很高兴能够在这里和您见面!希望您在这里可以感受到一份轻松愉快的氛围,不仅可以获得有趣的内容和知识,也可以畅所欲言、分享您的想法和见解。 推荐:「stormsha的主页」👈,「stormsha的知识库」👈持续学习,不断总结,共同进步,为了踏实,做好当下事儿~ 专栏导航 Python系列: Python面试题合集,剑指大厂Git系列: Git操作技巧GO

jenkins 插件执行shell命令时,提示“Command not found”处理方法

首先提示找不到“Command not found,可能我们第一反应是查看目标机器是否已支持该命令,不过如果相信能找到这里来的朋友估计遇到的跟我一样,其实目标机器是没有问题的通过一些远程工具执行shell命令是可以执行。奇怪的就是通过jenkinsSSH插件无法执行,经一番折腾各种搜索发现是jenkins没有加载/etc/profile导致。 【解决办法】: 需要在jenkins调用shell脚

Lua 脚本在 Redis 中执行时的原子性以及与redis的事务的区别

在 Redis 中,Lua 脚本具有原子性是因为 Redis 保证在执行脚本时,脚本中的所有操作都会被当作一个不可分割的整体。具体来说,Redis 使用单线程的执行模型来处理命令,因此当 Lua 脚本在 Redis 中执行时,不会有其他命令打断脚本的执行过程。脚本中的所有操作都将连续执行,直到脚本执行完成后,Redis 才会继续处理其他客户端的请求。 Lua 脚本在 Redis 中原子性的原因

Smarty模板执行原理

为了实现程序的业务逻辑和内容表现页面的分离从而提高开发速度,php 引入了模板引擎的概念,php 模板引擎里面最流行的可以说是smarty了,smarty因其功能强大而且速度快而被广大php web开发者所认可。本文将记录一下smarty模板引擎的工作执行原理,算是加深一下理解。 其实所有的模板引擎的工作原理是差不多的,无非就是在php程序里面用正则匹配将模板里面的标签替换为php代码从而将两者

(function() {})();只执行一次

测试例子: var xx = (function() {     (function() { alert(9) })(); alert(10)     return "yyyy";  })(); 调用: alert(xx); 在调用的时候,你会发现只弹出"yyyy"信息,并不见弹出"10"的信息!这也就是说,这个匿名函数只在立即调用的时候执行一次,这时它已经赋予了给xx变量,也就是只是

Java程序到CPU上执行 的步骤

相信很多的小伙伴在最初学习编程的时候会容易产生一个疑惑❓,那就是编写的Java代码究竟是怎么一步一步到CPU上去执行的呢?CPU又是如何执行的呢?今天跟随小编的脚步去化解开这个疑惑❓。 在学习这个过程之前,我们需要先讲解一些与本内容相关的知识点 指令 指令是指导CPU运行的命令,主要由操作码+被操作数组成。 其中操作码用来表示要做什么动作,被操作数是本条指令要操作的数据,可能是内存地址,也