You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Android 10中如何阻止用户关闭应用或在特定条件下重启应用?

解决Android 10中前台服务无法启动Activity以重启应用的问题

首先得明确:Android 10(API 29)引入了后台启动Activity的严格限制,直接在前台服务里调用startActivity()会被系统拦截,这是为了避免无意义打扰用户。不过我们可以通过符合系统规则的方式实现你的需求,下面是几个可行的方案:

方案1:前台服务 + 通知PendingIntent(无额外权限,符合系统规范)

这个方案的核心是利用通知的PendingIntent来启动Activity——系统允许通过用户交互(比如点击通知)或者服务绑定的通知唤醒应用,同时配合前台服务保持进程存活。

步骤实现:

  1. 配置前台服务与权限
    AndroidManifest.xml中声明前台服务和必要权限:

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <service android:name=".AppKeepAliveService" />
    
  2. 实现前台服务
    服务需要一直保持前台状态(必须显示通知),并在通知中绑定启动主Activity的PendingIntent:

    public class AppKeepAliveService extends Service {
        private static final int NOTIFICATION_ID = 1001;
        private static final String CHANNEL_ID = "KeepAliveChannel";
    
        @Override
        public void onCreate() {
            super.onCreate();
            createNotificationChannel();
            // 构建前台通知,绑定启动主Activity的PendingIntent
            Notification notification = new NotificationCompat.Builder(this, CHANNEL_ID)
                    .setSmallIcon(R.drawable.ic_app_icon)
                    .setContentTitle("我的应用正在运行")
                    .setContentText("点击回到应用")
                    .setPriority(NotificationCompat.PRIORITY_LOW)
                    .setContentIntent(getMainActivityIntent())
                    .setAutoCancel(false)
                    .build();
            startForeground(NOTIFICATION_ID, notification);
            // 启动定时检查应用状态的任务
            startAppStatusChecker();
        }
    
        private PendingIntent getMainActivityIntent() {
            Intent intent = new Intent(this, MainActivity.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
            return PendingIntent.getActivity(
                    this, 0, intent,
                    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
            );
        }
    
        private void createNotificationChannel() {
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                NotificationChannel channel = new NotificationChannel(
                        CHANNEL_ID, "应用保活", NotificationManager.IMPORTANCE_LOW
                );
                NotificationManager manager = getSystemService(NotificationManager.class);
                manager.createNotificationChannel(channel);
            }
        }
    
        // 定时检查应用是否在前台,不在则更新通知提示用户
        private void startAppStatusChecker() {
            Handler handler = new Handler(Looper.getMainLooper());
            handler.postDelayed(() -> {
                ActivityManager am = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
                List<ActivityManager.RunningTaskInfo> tasks = am.getRunningTasks(1);
                if (!tasks.isEmpty()) {
                    ComponentName topComponent = tasks.get(0).topActivity;
                    if (!topComponent.getPackageName().equals(getPackageName())) {
                        // 应用不在前台,更新通知提醒用户回到应用
                        Notification updatedNotification = new NotificationCompat.Builder(this, CHANNEL_ID)
                                .setSmallIcon(R.drawable.ic_app_icon)
                                .setContentTitle("应用已后台运行")
                                .setContentText("点击立即回到应用")
                                .setPriority(NotificationCompat.PRIORITY_LOW)
                                .setContentIntent(getMainActivityIntent())
                                .setAutoCancel(false)
                                .build();
                        NotificationManagerCompat.from(this).notify(NOTIFICATION_ID, updatedNotification);
                    }
                }
                handler.postDelayed(this::startAppStatusChecker, 3000); // 每3秒检查一次
            }, 3000);
        }
    
        @Nullable
        @Override
        public IBinder onBind(Intent intent) {
            return null;
        }
    }
    
  3. 配置主Activity启动模式
    AndroidManifest.xml中给主Activity设置singleTask启动模式,避免重复创建实例:

    <activity
        android:name=".MainActivity"
        android:launchMode="singleTask">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
    

优缺点:完全符合系统规则,不会被系统限制,但无法自动重启应用——需要用户点击通知回到应用,适合不想申请额外权限的场景。


方案2:辅助功能服务(AccessibilityService)实现自动重启

如果需要自动重启应用(用户关闭后立即打开),可以使用AccessibilityService——这是系统提供的辅助功能,允许监听系统窗口事件,并且在满足条件时启动Activity(需要用户授权辅助功能权限)。

步骤实现:

  1. 声明辅助功能服务
    AndroidManifest.xml中添加服务声明:

    <service
        android:name=".AppRestartAccessibilityService"
        android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
        <intent-filter>
            <action android:name="android.accessibilityservice.AccessibilityService" />
        </intent-filter>
        <meta-data
            android:name="android.accessibilityservice"
            android:resource="@xml/accessibility_config" />
    </service>
    
  2. 创建辅助功能配置文件
    res/xml目录下创建accessibility_config.xml

    <?xml version="1.0" encoding="utf-8"?>
    <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
        android:accessibilityEventTypes="typeWindowStateChanged"
        android:accessibilityFeedbackType="feedbackGeneric"
        android:accessibilityFlags="flagDefault"
        android:canRetrieveWindowContent="true"
        android:description="@string/accessibility_desc"
        android:packageNames="com.your.package.name" />
    

    strings.xml中添加辅助功能描述:

    <string name="accessibility_desc">此服务用于在应用被关闭时自动重启,提升使用体验</string>
    
  3. 实现AccessibilityService逻辑
    监听窗口关闭事件,当检测到自己的应用被关闭时,启动主Activity:

    public class AppRestartAccessibilityService extends AccessibilityService {
        @Override
        public void onAccessibilityEvent(AccessibilityEvent event) {
            // 监听窗口状态变化事件
            if (event.getEventType() != AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
                return;
            }
            // 检查事件是否来自当前应用
            if (!getPackageName().equals(event.getPackageName())) {
                return;
            }
            // 判断应用窗口是否被关闭(getSource()为null表示窗口已销毁)
            if (event.getSource() == null) {
                Intent intent = new Intent(this, MainActivity.class);
                intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                startActivity(intent);
            }
        }
    
        @Override
        public void onInterrupt() {
            // 服务被中断时的处理
        }
    
        @Override
        protected void onServiceConnected() {
            super.onServiceConnected();
            // 配置服务监听的事件和包名
            AccessibilityServiceInfo info = new AccessibilityServiceInfo();
            info.eventTypes = AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
            info.packageNames = new String[]{getPackageName()};
            info.feedbackType = AccessibilityServiceInfo.FEEDBACK_GENERIC;
            setServiceInfo(info);
        }
    }
    
  4. 引导用户开启辅助功能
    首次启动时,需要引导用户到系统设置开启辅助功能权限:

    private void requestAccessibilityPermission() {
        Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        startActivity(intent);
    }
    

优缺点:可以实现自动重启,但需要用户手动开启辅助功能权限,部分厂商的系统可能会限制辅助功能的后台运行,需要做好兼容。


重要注意事项

  1. 系统限制:Android 11及以上对后台启动Activity有更严格的限制,即使使用AccessibilityService,也需要确保你的应用符合系统的“用户交互”规则,避免被判定为恶意行为。
  2. 用户体验:尽量不要强制应用保持运行,除非是导航、音乐播放等必要场景——过度保活会消耗电量,容易引起用户反感甚至卸载应用。
  3. 进程存活:前台服务是保持进程存活的可靠方式,但也要注意避免服务被系统杀死(比如内存不足时),可以结合onTaskRemoved()回调做最后尝试。

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

火山引擎 最新活动