如何解决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.?()
消息甚至无法显示,因此调试过程相当缓慢且效率低下,更不用说给我任何提示这种行为的根源了。在Log
,BootServiceStart
和NotificationWorker
中,这种行为(未显示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 举报,一经查实,本站将立刻删除。