如何实现Android应用在被杀死/后台时通知7天未活跃用户?
Hey there! Let's break down a better approach to solve your problem—since your previous attempts with AlarmManager and BroadcastReceiver hit roadblocks (like not working when the app is killed, or causing infinite loops), WorkManager is the way to go here. It's Jetpack's dedicated component for reliable background tasks, and it handles all the edge cases you're struggling with.
Why Your Previous Approaches Failed
AlarmManagercan get canceled when the app is force-stopped or the device reboots (unless you handle boot broadcasts, which adds unnecessary complexity).- Calling your
TimerRestarterBroadcastReceiverfromonDestroy()is risky: first,onDestroy()isn't guaranteed to run (the system can kill your app without invoking it), and second, restarting the service every time it destroys creates that infinite loop you're seeing.
Step-by-Step Implementation with WorkManager
1. Add WorkManager Dependency
First, make sure you have the WorkManager runtime dependency in your app-level build.gradle:
dependencies { def work_version = "2.8.1" implementation "androidx.work:work-runtime:$work_version" }
2. Create a Worker Class for Inactivity Checks
This class will handle checking if the user has been inactive for 7 days and sending the reminder notification:
import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.os.Build; import android.preference.PreferenceManager; import androidx.annotation.NonNull; import androidx.core.app.NotificationCompat; import androidx.work.Worker; import androidx.work.WorkerParameters; public class InactiveUserCheckerWorker extends Worker { private static final String CHANNEL_ID = "INACTIVE_USER_REMINDER"; private static final long SEVEN_DAYS_IN_MILLIS = 7 * 24 * 60 * 60 * 1000; public InactiveUserCheckerWorker(@NonNull Context context, @NonNull WorkerParameters params) { super(context, params); } @NonNull @Override public Result doWork() { // Fetch the last time the user opened the app long lastActiveTime = getLastActiveTime(); long currentTime = System.currentTimeMillis(); // Check if 7 full days have passed since the user's last activity if (currentTime - lastActiveTime >= SEVEN_DAYS_IN_MILLIS) { sendInactiveUserNotification(); } // Return success to let WorkManager know the task completed properly return Result.success(); } private long getLastActiveTime() { return PreferenceManager.getDefaultSharedPreferences(getApplicationContext()) .getLong("last_app_open_time", System.currentTimeMillis()); } private void sendInactiveUserNotification() { NotificationManager notificationManager = (NotificationManager) getApplicationContext().getSystemService(Context.NOTIFICATION_SERVICE); // Create a notification channel for Android O and above (required for notifications) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel( CHANNEL_ID, "Inactive User Reminder", NotificationManager.IMPORTANCE_DEFAULT ); notificationManager.createNotificationChannel(channel); } // Build and show the reminder notification Notification notification = new NotificationCompat.Builder(getApplicationContext(), CHANNEL_ID) .setSmallIcon(R.drawable.ic_app_icon) // Replace with your app's actual icon .setContentTitle("We Miss You!") .setContentText("It's been 7 days since you used our app—come back and explore what's new!") .setPriority(NotificationCompat.PRIORITY_DEFAULT) .setAutoCancel(true) .build(); notificationManager.notify(1001, notification); } }
3. Track User Activity & Schedule the Work
You need to update the user's last active time every time they open the app (e.g., in your main activity's onResume() or your Application class's onCreate()). Then schedule a periodic WorkManager task to check for inactivity every 7 days.
Example in Your Application Class:
import android.app.Application; import android.content.Context; import androidx.preference.PreferenceManager; import androidx.work.ExistingPeriodicWorkPolicy; import androidx.work.PeriodicWorkRequest; import androidx.work.WorkManager; import java.util.concurrent.TimeUnit; public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); // Update last active time when the app starts updateLastActiveTime(this); // Schedule a recurring task to check for inactivity every 7 days PeriodicWorkRequest inactivityCheckRequest = new PeriodicWorkRequest.Builder( InactiveUserCheckerWorker.class, 7, TimeUnit.DAYS ).build(); // Enqueue the task with a unique name to avoid duplicate tasks WorkManager.getInstance(this).enqueueUniquePeriodicWork( "InactiveUserCheck", ExistingPeriodicWorkPolicy.KEEP, // Keep existing task if it already exists inactivityCheckRequest ); } public static void updateLastActiveTime(Context context) { PreferenceManager.getDefaultSharedPreferences(context) .edit() .putLong("last_app_open_time", System.currentTimeMillis()) .apply(); } }
Update Activity Time in Your Main Activity:
Make sure to refresh the last active time every time the user brings the app to the foreground:
@Override protected void onResume() { super.onResume(); MyApp.updateLastActiveTime(this); }
Key Benefits of This Approach
- Reliability: WorkManager automatically handles device reboots, app force-stops, and doze mode. It uses the best available background task mechanism for the Android version (JobScheduler for API 23+, AlarmManager for older versions).
- No Infinite Loops: Unlike your previous BroadcastReceiver setup, WorkManager manages task lifecycle—no need to trigger restarts from
onDestroy(). - Simplicity: You don't have to handle complex broadcast registrations or AlarmManager edge cases. WorkManager abstracts all that away.
- Flexibility: You can add constraints (e.g., only run when the device is charging or has network access) to save battery:
Constraints constraints = new Constraints.Builder() .setRequiredNetworkType(NetworkType.NOT_REQUIRED) // Adjust based on your needs .setRequiresCharging(false) // Adjust based on your needs .build(); // Add constraints to your PeriodicWorkRequest PeriodicWorkRequest inactivityCheckRequest = new PeriodicWorkRequest.Builder(...) .setConstraints(constraints) .build();
内容的提问来源于stack exchange,提问作者segfault404

