锁屏状态下FCM特定通知无法启动Activity且内容为空问题求助
Let's break down the problems you're facing and fix them step by step. Your code works fine when the app is foreground/background/killed, but fails on lock screen due to missing window permissions, thread-safe data handling, and incomplete intent flags.
Problem Analysis
The main issues causing failure on lock screen:
- Insufficient window flags: Your
OverlayActivitylacks flags to turn on the screen, dismiss the keyguard, and stay visible over lock screen - Thread-unsafe data passing: Using
GlobalVariableto share data between background FCM service and UI thread can lead to race conditions (empty Toast content) - Incomplete intent flags: The flags used to launch
OverlayActivitydon't prioritize bringing it to the foreground on lock screen - Null pointer risks: No null checks for FCM message body or parsed data, leading to unexpected failures
- Unnecessary Service initialization logic: The
onCreate()method inMyFirebaseMessagingServiceincorrectly launchesOverlayActivitywhen the service starts
Step-by-Step Fixes
1. Enhance OverlayActivity Window Permissions
Update OverlayActivity.onCreate() to add all critical flags for lock screen visibility:
public class OverlayActivity extends Activity { private static final String TAG = OverlayActivity.class.getSimpleName(); @Override protected void onCreate(Bundle savedInstanceState) { Log.d(TAG, "[onCreate]"); super.onCreate(savedInstanceState); Window window = getWindow(); // Add all required lock-screen flags window.addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); // Existing fragment transaction code... Fragment fragment = getFragmentManager().findFragmentByTag(FragmentType.OVERLAY.getTag()); if (fragment == null) { fragment = OverlayFragment.newInstance(); } FragmentTransaction ft = getFragmentManager().beginTransaction(); ft.replace(android.R.id.content, fragment, FragmentType.OVERLAY.getTag()); ft.commit(); } // Existing FragmentType enum... }
2. Fix FCM Message Handling & Data Passing
Rewrite MyFirebaseMessagingService.onMessageReceived() to add null checks, use intent extras instead of global variables, and update intent flags:
public class MyFirebaseMessagingService extends FirebaseMessagingService { private static final String TAG = "MyFirebaseMessaging"; @Override public void onMessageReceived(RemoteMessage remoteMessage) { Log.d(TAG, "From: " + remoteMessage.getFrom()); Log.d(TAG, "FCM Data: " + remoteMessage.getData()); // Guard against empty message body String body = remoteMessage.getData().get("body"); if (body == null || body.isEmpty()) { Log.e(TAG, "FCM message body is empty!"); return; } // Extract ticket number from body StringBuilder myNumbers = new StringBuilder(); for (int i = 0; i < body.length(); i++) { if (Character.isDigit(body.charAt(i))) { myNumbers.append(body.charAt(i)); } } String ticketNo = myNumbers.toString(); // Split message and validate format String[] splited = body.split("\\s+"); if (splited.length < 2) { Log.e(TAG, "Invalid message format!"); return; } String firstWord1 = splited[0]; String firstWord = splited[1]; Log.d(TAG, "FW Word : " + firstWord); Log.d(TAG, "0 Word : " + firstWord1); // Launch OverlayActivity directly with intent extras (no global variables) if ("Please".equals(firstWord1)) { Intent intent = new Intent(this, OverlayActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); // Pass data via intent extras intent.putExtra("showPopupStr", body); intent.putExtra("showPopupFirstword", firstWord1); intent.putExtra("ticketNo", ticketNo); intent.putExtra("msg", firstWord); startActivity(intent); } else { // Existing notification logic (update PendingIntent flag for Android 12+) String clickAction = remoteMessage.getNotification().getClickAction(); Intent intent = new Intent(clickAction); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); int pendingFlag = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S ? PendingIntent.FLAG_IMMUTABLE : PendingIntent.FLAG_ONE_SHOT; PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, pendingFlag); Uri sound = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); String channelId = "Default"; NotificationCompat.Builder builder = new NotificationCompat.Builder(this, channelId) .setSmallIcon(R.mipmap.ic_launcher) .setSound(sound) .setAutoCancel(true) .setContentIntent(pendingIntent); NotificationManager manager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(channelId, "Default channel", NotificationManager.IMPORTANCE_DEFAULT); manager.createNotificationChannel(channel); } manager.notify(0, builder.build()); } } // DELETE the onCreate() method that launches OverlayActivity - it's unnecessary and incorrect }
3. Update OverlayFragment to Use Intent Extras
Modify OverlayFragment to get data directly from the intent (avoids global variable race conditions) and fix MediaPlayer resource handling:
public class OverlayFragment extends Fragment { private static final String TAG = OverlayFragment.class.getSimpleName(); public static OverlayFragment newInstance() { return new OverlayFragment(); } @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fragment_overlay, container, false); Activity activity = getActivity(); if (activity == null) return view; // Get data from intent extras String showPopupFirstword = activity.getIntent().getStringExtra("showPopupFirstword"); String showPopupStr = activity.getIntent().getStringExtra("showPopupStr"); String msg = activity.getIntent().getStringExtra("msg"); // Show Toasts with non-null checks if (showPopupFirstword != null) { Toast.makeText(activity, showPopupFirstword, Toast.LENGTH_LONG).show(); } if (msg != null) { Toast.makeText(activity, "msg: " + msg, Toast.LENGTH_LONG).show(); } // Handle visitor request dialog MediaPlayer mp = null; if ("Please".equals(showPopupFirstword)) { // Initialize MediaPlayer safely try { mp = MediaPlayer.create(activity, R.raw.ring); mp.start(); } catch (Exception e) { Log.e(TAG, "Failed to play notification sound", e); } android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(activity); builder.setTitle("Visitor Request"); builder.setMessage(showPopupStr != null ? showPopupStr : "No message content"); builder.setCancelable(false); builder.setPositiveButton("Accept", (dialog, which) -> { stopAndReleaseMediaPlayer(mp); GlobalVeriable.notiMsg = "In"; // sendOwnerResponce(); }); builder.setNegativeButton("Reject", (dialog, which) -> { stopAndReleaseMediaPlayer(mp); GlobalVeriable.notiMsg = "Out"; // sendOwnerResponce(); }); final android.app.AlertDialog dialog = builder.create(); dialog.show(); dialog.getButton(DialogInterface.BUTTON_POSITIVE).setTextColor(Color.parseColor("#48A21D")); dialog.getButton(DialogInterface.BUTTON_NEGATIVE).setTextColor(Color.RED); new CountDownTimer(15000, 1000) { @Override public void onTick(long millisUntilFinished) {} @Override public void onFinish() { dialog.dismiss(); stopAndReleaseMediaPlayer(mp); activity.finish(); } }.start(); } else { activity.finish(); } // Close button logic Button close = view.findViewById(R.id.close); close.setOnClickListener(v -> activity.finish()); return view; } // Helper method to safely stop and release MediaPlayer private void stopAndReleaseMediaPlayer(MediaPlayer mp) { if (mp != null) { if (mp.isPlaying()) mp.stop(); mp.release(); } } }
4. Add Required Permissions
Ensure these permissions are in your AndroidManifest.xml:
<uses-permission android:name="android.permission.WAKE_LOCK" /> <!-- For Android 13+, add notification permission --> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
内容的提问来源于stack exchange,提问作者archana02

