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

Android API34实现APP被杀后推送通知及解决autoCancel失效问题

Android API 34 后台保活与通知自动消失解决方案

核心解决要点

  • 前台通知自动取消:前台服务的通知无法通过autoCancel=true自动取消,需在通知点击事件中触发服务切换回静默保活通知,同时移除推送通知。
  • API34后台保活优化:仅依赖START_STICKY和onTaskRemoved重启服务在Android 12+中受限,需结合JobScheduler实现可靠重启。
  • 权限适配:必须申请POST_NOTIFICATIONS权限(API33+),并在运行时主动请求。

完整代码实现

1. 权限配置(AndroidManifest.xml)

<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" /> <!-- 可选:开机自启 -->

<application ...>
    <service
        android:name=".PushKeepAliveService"
        android:foregroundServiceType="dataSync|remoteMessaging" <!-- API34需指定服务类型 -->
        android:exported="false" />

    <service
        android:name=".JobRestartService"
        android:permission="android.permission.BIND_JOB_SERVICE"
        android:exported="true" />

    <receiver
        android:name=".PushClickReceiver"
        android:exported="false" />

    <receiver
        android:name=".BootCompletedReceiver"
        android:exported="true">
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
        </intent-filter>
    </receiver>
</application>

2. 前台服务类(PushKeepAliveService.java)

import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.job.JobInfo;
import android.app.job.JobScheduler;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Build;
import android.os.IBinder;
import android.util.Log;

import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;

public class PushKeepAliveService extends android.app.Service {
    private static final String TAG = "PushKeepAliveService";
    private static final int FOREGROUND_ID = 1001;
    private static final String CHANNEL_ID = "push_keep_alive_channel";
    private static final int JOB_ID = 2001;

    public static final String ACTION_SHOW_PUSH = "ACTION_SHOW_PUSH";
    public static final String ACTION_RESET_FOREGROUND = "ACTION_RESET_FOREGROUND";

    @Override
    public void onCreate() {
        super.onCreate();
        createNotificationChannel();
        // 启动前台,显示静默保活通知
        startForeground(FOREGROUND_ID, createSilentNotification());
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent != null) {
            switch (intent.getAction()) {
                case ACTION_SHOW_PUSH:
                    // 处理服务器推送请求,显示通知
                    String requestContent = intent.getStringExtra("REQUEST_CONTENT");
                    showPushNotification(requestContent);
                    break;
                case ACTION_RESET_FOREGROUND:
                    // 重置为静默保活通知
                    stopForeground(STOP_FOREGROUND_REMOVE);
                    startForeground(FOREGROUND_ID, createSilentNotification());
                    break;
            }
        }
        return START_STICKY;
    }

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    @Override
    public void onTaskRemoved(Intent rootIntent) {
        super.onTaskRemoved(rootIntent);
        // 用JobScheduler重启服务,规避后台启动限制
        scheduleJobRestart();
        stopSelf();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        Log.d(TAG, "Service destroyed");
    }

    private void createNotificationChannel() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            NotificationChannel channel = new NotificationChannel(CHANNEL_ID,
                    "推送保活通道", NotificationManager.IMPORTANCE_LOW);
            channel.setSound(null, null);
            channel.enableVibration(false);
            NotificationManager manager = getSystemService(NotificationManager.class);
            if (manager != null) {
                manager.createNotificationChannel(channel);
            }
        }
    }

    // 创建静默保活通知(低优先级,无提示音)
    private Notification createSilentNotification() {
        Intent intent = new Intent(this, MainActivity.class);
        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

        return new NotificationCompat.Builder(this, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_notification) // 替换为你的应用图标
                .setContentTitle("应用运行中")
                .setContentText("后台保持推送接收")
                .setPriority(NotificationCompat.PRIORITY_LOW)
                .setContentIntent(pendingIntent)
                .setOngoing(true) // 防止用户手动清除
                .build();
    }

    // 显示新请求推送通知,点击后自动取消
    private void showPushNotification(String content) {
        Intent clickIntent = new Intent(this, PushClickReceiver.class);
        PendingIntent pendingClickIntent = PendingIntent.getBroadcast(this, 1, clickIntent,
                PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);

        Notification pushNotification = new NotificationCompat.Builder(this, CHANNEL_ID)
                .setSmallIcon(R.drawable.ic_notification)
                .setContentTitle("新请求到来")
                .setContentText(content)
                .setPriority(NotificationCompat.PRIORITY_HIGH)
                .setContentIntent(pendingClickIntent)
                .setAutoCancel(true) // 点击后自动取消通知
                .build();

        // 更新前台通知为推送通知
        startForeground(FOREGROUND_ID, pushNotification);
    }

    // 调度Job重启服务
    private void scheduleJobRestart() {
        JobScheduler jobScheduler = (JobScheduler) getSystemService(JOB_SCHEDULER_SERVICE);
        ComponentName componentName = new ComponentName(this, JobRestartService.class);
        JobInfo jobInfo = new JobInfo.Builder(JOB_ID, componentName)
                .setMinimumLatency(1000) // 1秒后执行重启
                .setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
                .setPersisted(true) // 设备重启后仍生效
                .build();
        jobScheduler.schedule(jobInfo);
    }
}

3. 通知点击接收器(PushClickReceiver.java)

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

public class PushClickReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        // 通知服务切换回静默保活状态
        Intent serviceIntent = new Intent(context, PushKeepAliveService.class);
        serviceIntent.setAction(PushKeepAliveService.ACTION_RESET_FOREGROUND);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            context.startForegroundService(serviceIntent);
        } else {
            context.startService(serviceIntent);
        }

        // 打开主页面
        Intent activityIntent = new Intent(context, MainActivity.class);
        activityIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        context.startActivity(activityIntent);
    }
}

4. JobScheduler重启服务(JobRestartService.java)

import android.app.job.JobParameters;
import android.app.job.JobService;
import android.content.Intent;
import android.os.Build;

public class JobRestartService extends JobService {
    @Override
    public boolean onStartJob(JobParameters params) {
        // 重启前台服务
        Intent intent = new Intent(this, PushKeepAliveService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(intent);
        } else {
            startService(intent);
        }
        jobFinished(params, false);
        return false;
    }

    @Override
    public boolean onStopJob(JobParameters params) {
        return false;
    }
}

5. MainActivity启动服务与权限请求

import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;

public class MainActivity extends AppCompatActivity {
    private static final int REQUEST_POST_NOTIFICATIONS = 100;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 请求通知权限(API33+)
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            if (ContextCompat.checkSelfPermission(this, Manifest.permission.POST_NOTIFICATIONS)
                    != PackageManager.PERMISSION_GRANTED) {
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.POST_NOTIFICATIONS},
                        REQUEST_POST_NOTIFICATIONS);
            } else {
                startKeepAliveService();
            }
        } else {
            startKeepAliveService();
        }
    }

    private void startKeepAliveService() {
        Intent intent = new Intent(this, PushKeepAliveService.class);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            startForegroundService(intent);
        } else {
            startService(intent);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_POST_NOTIFICATIONS) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startKeepAliveService();
            }
        }
    }
}

6. 开机自启接收器(可选)

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Build;

public class BootCompletedReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())
                || Intent.ACTION_LOCKED_BOOT_COMPLETED.equals(intent.getAction())) {
            Intent serviceIntent = new Intent(context, PushKeepAliveService.class);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                context.startForegroundService(serviceIntent);
            } else {
                context.startService(serviceIntent);
            }
        }
    }
}

关键说明

  • 通知逻辑:平时用静默低优先级通知保活服务,收到推送时替换为高优先级可点击通知,用户点击后自动切回静默通知,既满足保活要求,又解决autoCancel无效问题。
  • JobScheduler重启:Android 12+限制后台直接启动服务,JobScheduler可绕过该限制,确保应用从近期列表移除后服务能可靠重启。
  • 权限适配:API33+必须申请POST_NOTIFICATIONS权限,否则无法显示通知,导致前台服务启动失败。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.09 16:54:53