You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Java如何实现跨Activity的应用内持久通知?

Android Java 实现跨Activity的应用内通知解决方案

问题根源

当前应用内通知绑定在单个Activity的View层级中,切换Activity时原Activity进入onPause/onStop状态,通知View被销毁,导致通知提前消失。要实现跨Activity持续显示,核心是让通知的生命周期不依赖单个Activity。

方案一:全局悬浮窗(Overlay)实现

这是最贴合Instagram/Twitter体验的方案,通知属于系统窗口层级,不依附任何Activity,切换页面完全无感知。

实现步骤

  1. 申请权限
    在AndroidManifest.xml中声明权限:

    <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
    

    Android 6.0+需动态引导用户开启权限:

    if (!Settings.canDrawOverlays(this)) {
        Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                Uri.parse("package:" + getPackageName()));
        startActivityForResult(intent, 1001);
    }
    
  2. 全局通知管理单例
    创建单例类统一处理通知的显示、隐藏和计时逻辑:

    public class InAppNotificationManager {
        private static InAppNotificationManager instance;
        private WindowManager windowManager;
        private View notificationView;
        private Handler handler = new Handler(Looper.getMainLooper());
        private Runnable hideRunnable;
    
        private InAppNotificationManager(Context context) {
            windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        }
    
        public static synchronized InAppNotificationManager getInstance(Context context) {
            if (instance == null) {
                instance = new InAppNotificationManager(context.getApplicationContext());
            }
            return instance;
        }
    
        public void showNotification(String content, long duration) {
            // 移除已有通知
            hideNotification();
    
            // 加载自定义通知布局
            notificationView = LayoutInflater.from(windowManager.getContext())
                    .inflate(R.layout.in_app_notification, null);
            TextView contentTv = notificationView.findViewById(R.id.tv_content);
            contentTv.setText(content);
    
            // 配置窗口参数
            WindowManager.LayoutParams params = new WindowManager.LayoutParams();
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                params.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
            } else {
                params.type = WindowManager.LayoutParams.TYPE_PHONE;
            }
            params.format = PixelFormat.TRANSLUCENT;
            params.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                    | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                    | WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS;
            params.width = WindowManager.LayoutParams.MATCH_PARENT;
            params.height = WindowManager.LayoutParams.WRAP_CONTENT;
            params.gravity = Gravity.TOP;
    
            // 添加到系统窗口
            windowManager.addView(notificationView, params);
    
            // 计时自动隐藏
            hideRunnable = this::hideNotification;
            handler.postDelayed(hideRunnable, duration);
        }
    
        public void hideNotification() {
            if (notificationView != null && windowManager != null) {
                handler.removeCallbacks(hideRunnable);
                windowManager.removeView(notificationView);
                notificationView = null;
            }
        }
    }
    
  3. 调用方式
    在任意Activity或组件中直接调用:

    InAppNotificationManager.getInstance(this).showNotification("您有一条新消息", 3000);
    

方案二:BaseActivity统一管理+状态保存

若不想申请悬浮窗权限,可通过BaseActivity统一处理通知的重建,兼容性更好,仅存在切换页面时短暂的重绘过程。

实现步骤

  1. 通知状态管理单例
    保存通知的内容、剩余时长和激活状态:

    public class NotificationStateManager {
        private static NotificationStateManager instance;
        private String currentContent;
        private long startTime;
        private long totalDuration;
        private boolean isActive;
    
        private NotificationStateManager() {}
    
        public static synchronized NotificationStateManager getInstance() {
            if (instance == null) {
                instance = new NotificationStateManager();
            }
            return instance;
        }
    
        public void activateNotification(String content, long duration) {
            currentContent = content;
            totalDuration = duration;
            startTime = System.currentTimeMillis();
            isActive = true;
        }
    
        public void deactivateNotification() {
            isActive = false;
            currentContent = null;
        }
    
        public boolean hasActiveNotification() {
            return isActive && (System.currentTimeMillis() - startTime) < totalDuration;
        }
    
        public String getContent() {
            return currentContent;
        }
    
        public long getRemainingDuration() {
            if (!hasActiveNotification()) return 0;
            return totalDuration - (System.currentTimeMillis() - startTime);
        }
    }
    
  2. BaseActivity统一处理
    所有业务Activity继承该BaseActivity,在onResume时检查并重建通知:

    public abstract class BaseActivity extends AppCompatActivity {
        private View notificationView;
        private Handler handler = new Handler(Looper.getMainLooper());
        private Runnable hideRunnable;
    
        @Override
        protected void onResume() {
            super.onResume();
            checkAndRestoreNotification();
        }
    
        @Override
        protected void onPause() {
            super.onPause();
            removeNotificationView();
        }
    
        private void checkAndRestoreNotification() {
            NotificationStateManager stateManager = NotificationStateManager.getInstance();
            if (stateManager.hasActiveNotification()) {
                showNotification(stateManager.getContent(), stateManager.getRemainingDuration());
            }
        }
    
        private void showNotification(String content, long remainingDuration) {
            removeNotificationView();
    
            // 加载通知布局并添加到当前Activity根布局
            notificationView = LayoutInflater.from(this)
                    .inflate(R.layout.in_app_notification, findViewById(android.R.id.content), false);
            TextView contentTv = notificationView.findViewById(R.id.tv_content);
            contentTv.setText(content);
            findViewById(android.R.id.content).addView(notificationView);
    
            // 计时隐藏并更新状态
            hideRunnable = () -> {
                removeNotificationView();
                NotificationStateManager.getInstance().deactivateNotification();
            };
            handler.postDelayed(hideRunnable, remainingDuration);
        }
    
        private void removeNotificationView() {
            if (notificationView != null) {
                handler.removeCallbacks(hideRunnable);
                ((ViewGroup) notificationView.getParent()).removeView(notificationView);
                notificationView = null;
            }
        }
    }
    
  3. 调用方式
    在任意Activity中触发通知:

    NotificationStateManager.getInstance().activateNotification("新通知提醒", 3000);
    

方案对比

方案优点缺点
全局悬浮窗体验完美,无页面切换感知需要申请SYSTEM_ALERT_WINDOW权限,兼容性略差
BaseActivity无需特殊权限,兼容性好切换页面时通知会短暂消失后重建

内容的提问来源于stack exchange,提问作者KZMKBL

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 17:08:11