Android监听手机解锁事件问题:静态广播接收器未触发
Hey there, let's work through this issue you're having with triggering actions when a user unlocks their Android device. I've looked at your two attempted solutions and I can see exactly why they're not working—Android's background restrictions in newer versions are the culprit. Let's break this down step by step, fix the existing issues, and get a working solution that works across all modern Android versions.
Why Your Current Attempts Aren't Working
1. Static Broadcast Receiver for USER_PRESENT
Starting with Android 8.0 (API 26), Google restricted most implicit broadcasts from being statically registered in the manifest—android.intent.action.USER_PRESENT falls right into this category. That's why your ScreenReceiver's onReceive method never gets called; the system simply ignores static registrations for this broadcast on newer OS versions.
2. Dynamic Registration in a Background Service
Your second approach using a background service works on older Android versions, but Android 8.0 introduced strict background execution limits. Once your app moves to the background (no visible activities), the system will quickly shut down your NoteService to save resources. When the service dies, the dynamically registered receiver goes with it, so it can't listen for unlock events anymore.
Working Solutions for All Android Versions
Option 1: Foreground Service + Dynamic Broadcast Registration
A foreground service stays alive because it shows a persistent notification (required on Android 8.0+), making it perfect for long-term event listening. Here's how to implement it:
- Update your
NoteServiceto run as a foreground service and register the receiver dynamically:
class NoteService : Service() { private lateinit var screenReceiver: ScreenReceiver override fun onCreate() { super.onCreate() // Register the USER_PRESENT receiver dynamically screenReceiver = ScreenReceiver() val intentFilter = IntentFilter(Intent.ACTION_USER_PRESENT) registerReceiver(screenReceiver, intentFilter) // Set up foreground service (required for Android 8.0+) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Create a notification channel first val channel = NotificationChannel( "unlock_listener_channel", "Unlock Listener Service", NotificationManager.IMPORTANCE_LOW ) val notificationManager = getSystemService(NotificationManager::class.java) notificationManager.createNotificationChannel(channel) } // Build the foreground notification val notification = NotificationCompat.Builder(this, "unlock_listener_channel") .setSmallIcon(R.drawable.ic_app_icon) .setContentTitle("Listening for unlock events") .setContentText("Service running in background") .setPriority(NotificationCompat.PRIORITY_LOW) .build() // Start the service in foreground mode startForeground(1001, notification) } override fun onDestroy() { super.onDestroy() // Always unregister the receiver to avoid memory leaks unregisterReceiver(screenReceiver) } override fun onBind(intent: Intent): IBinder? { return null // We don't need binding for this service } }
- Add required permissions and service declaration to
AndroidManifest.xml:
<!-- Foreground service permission (Android 8.0+) --> <uses-permission android:name="android.permission.FOREGROUND_SERVICE" /> <!-- Notification permission (Android 13+) --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <service android:name=".service.NoteService" android:foregroundServiceType="dataSync" /> <!-- Pick a type that matches your use case -->
- Update your
MyApplicationto start the service correctly for all Android versions:
class MyApplication : MultiDexApplication() { companion object { lateinit var instance: MyApplication } override fun onCreate() { super.onCreate() instance = this MultiDex.install(applicationContext) val serviceIntent = Intent(this, NoteService::class.java) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { // Use startForegroundService for Android 8.0+ startForegroundService(serviceIntent) } else { // Legacy startService for older versions startService(serviceIntent) } } }
Option 2: Activity-Based Dynamic Registration (For Non-Persistent Listening)
If you don't need to listen for unlock events when your app is completely closed, you can register the receiver only when your app is in the foreground (e.g., when an activity is visible):
- Fix your
ScreenReceiverto actually call the clear note method:
class ScreenReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (Intent.ACTION_USER_PRESENT == intent.action) { // Call your clear note method here checkClearSavedNote(context) } } private fun checkClearSavedNote(context: Context) { AppPref.getInstance(context).putString(AppPref.KEY_ID_CURRENT_NOTE, "") Log.e("Quang", "clear note") } }
- Register/unregister the receiver in your main activity's lifecycle methods:
class MainActivity : AppCompatActivity() { private lateinit var screenReceiver: ScreenReceiver override fun onResume() { super.onResume() // Register receiver when activity is visible screenReceiver = ScreenReceiver() val intentFilter = IntentFilter(Intent.ACTION_USER_PRESENT) registerReceiver(screenReceiver, intentFilter) } override fun onPause() { super.onPause() // Unregister when activity is hidden to avoid leaks unregisterReceiver(screenReceiver) } }
This way, the receiver only exists while your app is in the foreground, avoiding background restrictions entirely.
Quick Fix for Your Original Receiver
Don't forget—your original ScreenReceiver had a bug: the checkClearSavedNote method was private and never called from onReceive. I fixed that in the code above, but make sure you always hook up your business logic to the broadcast trigger!
内容的提问来源于stack exchange,提问作者quangkid

