Android PeriodicWorkRequest工作v1.0.1 [arch,不是androidx]每15m产生20次,而不是一次,为什么?

如何解决Android PeriodicWorkRequest工作v1.0.1 [arch,不是androidx]每15m产生20次,而不是一次,为什么?

在以下代码摘录中,我利用BroadcastReceiver在设备启动和/或程序包重新加载时启动Service。此NotificationService每隔15分钟通过Worker呼叫我的PeriodicWorkRequest。最初一切都会按预期运行,直到执行NotificationWorker为止。看来,在调用Worker时,它运行 20 次,而不是一次。我相信也是完全的二十倍。全部说完之后,它将等待〜15分钟(应有的时间),然后在再次调用Worker时表现出相同的行为。理想情况下,此Worker仅应每15m运行一次,尤其是因为它将要进行的某些计算相当昂贵。

我已经花了几天时间,一直在搜寻更多信息(由于使用了 Work v1.0.1 ,而不是较新的androidx v2.4.0,因此我在某种程度上受到了阻碍。 ,但我还没有准备好通过该更改来升级项目中所有可能发生的故障),并尽力调试此问题。不幸的是,由于我很少能使我的Log.?()消息甚至无法显示,因此调试过程相当缓慢且效率低下,更不用说给我任何提示这种行为的根源了。在LogBootServiceStartNotificationWorker中,这种行为(未显示NotificationService消息)是一个问题,我不知道为什么。

这是该问题的适用代码;请注意,如果您点击 dpaste 链接,则会发现有问题的代码的一般区域突出显示,以便于简化诊断( dpasted代码仅能再使用6天):

BootServiceStart-也是here on dpaste

    package com.example.sprite.half_lifetimer;
    ​
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.os.Build;
    import android.util.Log;
    ​
    public class BootServiceStart extends BroadcastReceiver {
        public void onReceive(Context context,Intent arg1) {
            Intent intent = new Intent(context,NotificationService.class);
    ​
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                context.startForegroundService(intent);
            } else {
                context.startService(intent);
            }
    ​
            if (GlobalMisc.Debugging) {
                Log.i("Halflife.BootServiceStart","Attempted to start NotificationService");
            }
        }
    }

NotificationService-也是here on dpaste

    package com.example.sprite.half_lifetimer;
    ​
    import android.app.Notification;
    import android.app.NotificationChannel;
    import android.app.NotificationManager;;
    import android.app.Service;
    import android.content.Intent;
    import android.os.Build;
    import android.os.IBinder;
    import android.support.annotation.Nullable;
    import android.support.v4.app.NotificationCompat;
    import android.util.Log;
    import androidx.work.PeriodicWorkRequest;
    import androidx.work.WorkManager;
    ​
    import java.time.LocalDateTime;
    import java.util.HashMap;
    import java.util.concurrent.TimeUnit;
    ​
    public class NotificationService extends Service {
        public static HashMap<Integer,Boolean> firedNotifications = new HashMap<>();
        public static LocalDateTime lastNotificationLoopLDT = null;
    ​
        @Nullable
        public IBinder onBind(Intent intent) {
            return null;
        }
    ​
        /**
         * Method handles creation of a NotificationChannel and database
         * initialization (for this particular subset of the code),then passing
         * control off to notificationLoop().
         */
        public void onCreate() {
            startForeground(31337,buildForegroundNotification());
    ​
            if (GlobalMisc.Debugging) {
                Log.i("Halflife.NotificationService.onCreate","Started NotificationService");
            }
    ​
            // 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) {
                NotificationChannel chan = new NotificationChannel(
                        "taper-n-clearing-talk","taper-n-clearing",NotificationManager.IMPORTANCE_NONE);
                chan.setDescription("Notifications for Taper dosages and Substance clearance");
    ​
                // 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(chan);
            }
    ​
            //get the database ready
            try {
                Permanence.Misc.init(/*NotificationService.this*/ getApplicationContext());
            } catch (Exception ex) {
                Log.e("Halflife.notificationLoop","Unable to init database: " +
                        ex.toString());
            }
    ​
            if (GlobalMisc.Debugging) {
                Log.i("Halflife.onCreate","all valid tapers: " +
                        Permanence.Tapers.loadAllValidTapers(getApplicationContext()).toString());
    ​
            }
    ​
            //notificationLoop();
            PeriodicWorkRequest notificationsRequest =
                    new PeriodicWorkRequest.Builder(NotificationWorker.class,15,TimeUnit.MINUTES)
                            .build();
            WorkManager.getInstance()
                    .enqueue(notificationsRequest);
        }
    ​
        private Notification buildForegroundNotification() {
            NotificationCompat.Builder b=new NotificationCompat.Builder(this);
    ​
            b.setOngoing(true)
                    .setContentTitle("HLT Foreground Service")
                    .setContentText("Giving Half-life Timer foreground priority")
                    .setChannelId("taper-n-clearing-talk")
                    .setSmallIcon(getApplicationContext().getResources().getIdentifier(
                            "plus_medical_blue","drawable",getApplicationContext().getPackageName()));
    ​
            return(b.build());
        }
    }

NotificationWorker-也是here on dpaste

    package com.example.sprite.half_lifetimer;
    ​
    import android.app.PendingIntent;
    import android.app.TaskStackBuilder;
    import android.content.Context;
    import android.content.Intent;
    import android.support.annotation.NonNull;
    import android.support.v4.app.NotificationCompat;
    import android.support.v4.app.NotificationManagerCompat;
    import android.util.Log;
    ​
    import androidx.work.Worker;
    import androidx.work.WorkerParameters;
    ​
    import java.time.Duration;
    import java.time.LocalDateTime;
    import java.time.LocalTime;
    ​
    public class NotificationWorker extends Worker {
        private boolean notificationDebugging = false;
    ​
        public NotificationWorker(@NonNull Context context,@NonNull WorkerParameters params) {
            super(context,params);
        }
    ​
        @Override
        public Result doWork() {
            LocalDateTime nextScheduledDosage;
            long adminDurationMinutes;
    ​
            if (!notificationDebugging) {
                if (GlobalMisc.NotificationsEnabled) {
                    //taper notification loop
                    for (Taper taper : Permanence.Tapers.loadAllValidTapers(getApplicationContext())) {
                        //this will handle if any tapers have been added since inception
                        if (!NotificationService.firedNotifications.containsKey(taper.getId())) {
                            NotificationService.firedNotifications.put(taper.getId(),false);
                        }
    ​
                        //if this is a constrained taper,but we're outside of the window,just
                        //go on to the next taper
                        if (taper.isConstrained() && !taper.inConstraintHours()) {
                            Log.i("Halflife.notificationLoop","skipping " + taper.toString() +
                                            " (outside of hourly constraints)");
    ​
                            continue;
                        }
    ​
                        if (!NotificationService.firedNotifications.get(taper.getId())) {
                            try {
                                nextScheduledDosage = taper.findNextScheduledDosageLDT();
                                if (!taper.isConstrained()) {
                                    Log.i("Halflife.notificationLoop","working with unconstrained taper");
    ​
                                    adminDurationMinutes = Duration.ofDays(1).dividedBy(
                                            taper.getAdminsPerDay()).toMinutes();
                                } else {
                                    Log.i("Halflife.notificationLoop","working with constrained taper");
    ​
                                    //not sure if this is necessary or not,but might as well
                                    //throw it in since the goddamned code is too complex for me
                                    //to follow right now down below
                                    LocalTime nextDosageTime =
                                            nextScheduledDosage.toLocalTime();
                                    if (nextDosageTime.isBefore(taper.getStartHour()) ||
                                            nextDosageTime.isAfter(taper.getEndHour())) {
                                        Log.i("notificationLoop","skipping " + taper.toString() +
                                                        " (outside of constraint hours)");
    ​
                                        continue;
                                    }
    ​
                                    //this part,of course,is necessary
                                    adminDurationMinutes =
                                            Duration.between(taper.getStartHour(),taper.getEndHour()).dividedBy(
                                                    taper.getAdminsPerDay())
                                                    .toMinutes();
                                }
    ​
                                if (GlobalMisc.Debugging) {
                                    Log.i("Halflife.notificationLoop","Checking taper: " +
                                            taper.getName());
                                    Log.i("Halflife.notificationLoop","nextScheduledDosage " +
                                            "contains: " + nextScheduledDosage.toString());
                                }
    ​
                                if (((NotificationService.lastNotificationLoopLDT != null) &&
                                        nextScheduledDosage.isAfter(
                                                NotificationService.lastNotificationLoopLDT) &&
                                        nextScheduledDosage.isBefore(
                                                LocalDateTime.now().plusMinutes(
                                                        (adminDurationMinutes / 5)))) ||
                                        (nextScheduledDosage.isAfter(
                                                LocalDateTime.now().minusMinutes(1)) &&
                                                nextScheduledDosage.isBefore(
                                                        LocalDateTime.now().plusMinutes(
                                                                (adminDurationMinutes / 5))))) {
                                    fireTaperNotification(taper);
    ​
                                    //set firedNotifications to reflect that we sent this
                                    //notification
                                    NotificationService.firedNotifications.replace(taper.getId(),true);
                                } else if (GlobalMisc.Debugging) {
                                    Log.i("Halflife.notificationLoop","not displaying notification as per " +
                                                    "datetime constraints");
                                }
                            } catch (Exception ex) {
                                Log.e("Halflife.notificationLoop","Issue finding next scheduled dosage: " +
                                                ex.toString());
    ​
                                return Result.failure();
                            }
                        }
                    }
                } else {
                    GlobalMisc.debugMsg("NotificationWorker:doWork","Would have just gone into substance taper notification loop");
                }
    ​
                if (GlobalMisc.NotificationsEnabled) {
                    //substance clearing notification loop
                    //LocalDateTime fiveMinAgo = LocalDateTime.now().minusMinutes(5);
                    for (Substance sub : Permanence.Subs.loadUnarchivedSubstances(
                            getApplicationContext())) {
                        if (GlobalMisc.Debugging) {
                            Log.i("Halflife.notificationLoop","Checking sub clearance: " + sub.getCommon_name());
                        }
    ​
                        //has this substance cleared within the last 5 minutes?
                        LocalDateTime clearedAt = sub.getFullEliminationLDT();
                        if (clearedAt != null) {
                            if (NotificationService.lastNotificationLoopLDT != null) {
                                if (clearedAt.isAfter(NotificationService.lastNotificationLoopLDT) &&
                                        clearedAt.isBefore(LocalDateTime.now())) {
                                    //fire the notification
                                    try {
                                        fireSubClearedNotification(sub);
                                    } catch (Exception ex) {
                                        Log.i("Halflife.doWork",ex.toString());
    ​
                                        return Result.failure();
                                    }
                                }
                            }
                        }
                    }
                } else {
                    GlobalMisc.debugMsg("NotificationWorker:doWork","Would have just gone into substance clearing notification loop");
                }
            } else {
                Log.i("Halflife.notificationLoop","In notification debugging " +
                        "mode");
    ​
                try {
                    fireTaperNotification(null);
                } catch (Exception ex) {
                    Log.i("Halflife.doWork",ex.toString());
    ​
                    return Result.failure();
                }
            }
    ​
            NotificationService.lastNotificationLoopLDT = LocalDateTime.now();
    ​
            return Result.success();
        }
    ​
        /**
         * Method handles the actual building of the notification regarding
         * the applicable taper,and shows it unless our handy HashMap
         * 'firedNotifications' shows that there is already a notification
         * present for this particular taper.
         *
         * @param taper the taper to display notification for
         */
        private void fireTaperNotification(Taper taper) throws Exception {
            Context ctxt = getApplicationContext();
            float currentDosageScheduled = taper.findCurrentScheduledDosageAmount();
    ​
            //here's the legitimate meat 'n potatoes for firing a notification
            try {
                //if we've already blown the dosage required for the next administration,just skip this
                //one
                if (currentDosageScheduled <= 0) {
                    Log.d("fireTaperNotification","More dosage taken than needs to be " +
                            "for the current taper step; skipping this taper administration.");
    ​
                    return;
                }
    ​
                Intent intent = new Intent(ctxt,AdminData.class);
                intent.putExtra("SUB_NDX",GlobalMisc.getSubListPositionBySid(taper.getSid()));
                intent.putExtra("NOTIFICATION_BASED",true);
                TaskStackBuilder stackBuilder = TaskStackBuilder.create(ctxt);
                stackBuilder.addParentStack(SubData.class);
                stackBuilder.addNextIntentWithParentStack(intent);
    ​
                Intent delIntent = new Intent(ctxt,NotificationDismissalReceiver.class);
                delIntent.putExtra("TAPER",true);
                delIntent.putExtra("SUB_ID",taper.getSid());
    ​
    ​
                PendingIntent pendingIntent =
                        stackBuilder.getPendingIntent(0,PendingIntent.FLAG_UPDATE_CURRENT);
                PendingIntent pendingDelIntent = PendingIntent.getBroadcast(ctxt,delIntent,PendingIntent.FLAG_UPDATE_CURRENT);
    ​
                LocalDateTime latestUsageLDT;
                LocalDateTime todaysOpeningConstraintLDT;
                boolean beforeOpeningConstraint = false;
                latestUsageLDT = Converters.toLocalDateTime(
                        Permanence.Admins.getLatestUsageTimestampBySid(taper.getSid()));
                if (taper.isConstrained()) {
                    todaysOpeningConstraintLDT =
                            LocalDateTime.now().withHour(taper.getStartHour().getHour())
                                    .withMinute(taper.getStartHour().getMinute())
                                    .withSecond(0);
    ​
                    if (latestUsageLDT.plus(taper.getTotalConstraintDuration()).isBefore(
                            todaysOpeningConstraintLDT)) {
                        beforeOpeningConstraint = true;
                    }
                }
    ​
                NotificationCompat.Builder builder = new NotificationCompat.Builder(
                        ctxt,"halflife")
                        .setContentTitle("Half-life Timer Taper " + taper.getName())
                        //note that the above line,right after "Due since: " +,will
                        //end up displaying the epoch start date for a taper on a
                        //substance that has no administrations whatsoever
                        .setSmallIcon(ctxt.getResources().getIdentifier("plus_medical_blue",ctxt.getPackageName()))
                        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                        .setContentIntent(pendingIntent)
                        .setDeleteIntent(pendingDelIntent)
                        .setAutoCancel(true);
    ​
                long rawTimestamp = Permanence.Admins.getLatestUsageTimestampBySid(taper.getSid());
    ​
                GlobalMisc.debugMsg("fireTaperNotification","Permanence.Admins.getLatestUsageTimestampBySid returns: " +
                                rawTimestamp);
    ​
                if (Converters.toLocalDateTime(rawTimestamp).isBefore(
                        LocalDateTime.of(1980,1,0))) {
                    builder.setContentText("Due: " +
                            String.format("%.2f",currentDosageScheduled) +
                            Permanence.Subs.getUnitsBySID(taper.getSid()) + "/" +
                            Permanence.Subs.loadSubstanceById(
                                    taper.getSid()).getCommon_name() + "\n" +
                            "Due now");
                } else if (beforeOpeningConstraint) {
                    builder.setContentText("Due:" +
                            currentDosageScheduled +
                            Permanence.Subs.getUnitsBySID(taper.getSid()) + " of " +
                            Permanence.Subs.loadSubstanceById(
                                    taper.getSid()).getCommon_name() + "\n" +
                            "Due since: " +
                            LocalDateTime.now().withHour(taper.getStartHour().getHour())
                               .withMinute(taper.getStartHour().getMinute())
                               .withSecond(0));
                } else {
                    builder.setContentText("Due:" +
                            currentDosageScheduled +
                            Permanence.Subs.getUnitsBySID(taper.getSid()) + " of " +
                            Permanence.Subs.loadSubstanceById(
                                    taper.getSid()).getCommon_name() + "\n" +
                            "Due since: " +
                            Converters.toLocalDateTime(
                                    Permanence.Admins.getLatestUsageTimestampBySid(
                                            taper.getSid())).plus(
                                    Duration.ofDays(1).dividedBy(
                                            taper.getAdminsPerDay())));
                }
    ​
                NotificationManagerCompat notificationManager =
                        NotificationManagerCompat.from(ctxt);
    ​
                notificationManager.notify(1,builder.build());
    ​
                if (GlobalMisc.Debugging || notificationDebugging) {
                    Log.i("Halflife.fireNotification","attempted to send taper notification");
                }
            } catch (Exception ex) {
                Log.e("Halflife.fireNotification","Something broke in taper notification: " + ex.toString());
    ​
                throw new Exception("taper notification broke");
            }
        }
    ​
        private void fireSubClearedNotification(Substance sub) throws Exception {
            Context ctxt = getApplicationContext();
    ​
            try {
                Intent intent = new Intent(ctxt,SubsRankedByLastUsage.class);
    ​
                PendingIntent pendingIntent = PendingIntent.getActivity(
                        ctxt,intent,PendingIntent.FLAG_UPDATE_CURRENT);
    ​
                NotificationCompat.Builder builder = new NotificationCompat.Builder(
                        ctxt,"halflife")
                        .setContentTitle("Half-life Timer Cleared: " + sub.getCommon_name())
                        .setContentText(sub.getCommon_name() + " cleared at " +
                                sub.getFullEliminationLDT().toString())
                        .setSmallIcon(ctxt.getResources().getIdentifier("plus_medical_blue",ctxt.getPackageName()))
                        .setPriority(NotificationCompat.PRIORITY_DEFAULT)
                        .setContentIntent(pendingIntent)
                        .setAutoCancel(true);
    ​
                NotificationManagerCompat notificationManager =
                        NotificationManagerCompat.from(ctxt);
    ​
                notificationManager.notify(1,"attempted to send sub clearednotification");
                }
            } catch (Exception ex) {
                Log.e("Halflife.fireNotification","Something broke in sub cleared notification: " + ex.toString());
    ​
                throw new Exception("sub cleared notification broke");
            }
        }
    }

我非常感谢所有可能对这种现象发生的原因有任何见解的人,如何避免这种现象的提示,在较旧的版本中可以找到API以及其他文档的人,已弃用的 JetPack work v1.0.1 库,或者我可以在Android Studio中更好地诊断此问题的方法,因为尝试用我知道的方法进行调试已被证明是徒劳的。

非常感谢您的时间,并在此问题上为您提供帮助!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-