Android O后台GCM推送通知不显示:startForeground()用法排查
解决GCM推送通知不显示的问题
我看了你的代码和日志,问题主要集中在通知通道创建时机、前台服务调用逻辑,以及Android 8.0+的后台服务适配问题上,下面一步步给你梳理修正方案:
核心问题拆解
- 通知通道创建顺序错误:你先构建了
Notification对象,再创建NotificationChannel。在Android 8.0(API 26)及以上版本,通知必须关联已存在的通道,否则系统会直接忽略这个通知。 - IntentService的适配问题:Android 8.0之后
IntentService已被标记为过时,它的onHandleIntent运行在后台线程,而startForeground更适合在主线程调用,容易触发系统的后台服务超时警告(比如你日志里的"Service took too long to process intent")。 - 冗余的通知调用:
startForeground已经会把通知显示为前台服务通知,再调用manager.notify属于多余操作,甚至可能导致通知显示冲突。 - 前台服务的合规性:调用
startForegroundService后,必须在5秒内调用startForeground,线程调度延迟就会触发系统警告,进而导致通知不显示。
修正后的代码实现
1. 替换IntentService为JobIntentService(适配Android 8.0+)
JobIntentService是Google推荐的IntentService替代方案,会根据系统版本自动选择合适的调度方式,避免后台限制问题:
public class GcmIntentService extends JobIntentService { private static final String TAG = GcmIntentService.class.getName(); public static final int JOB_ID = 1001; // 静态方法用于将任务加入队列 public static void enqueueWork(Context context, Intent intent) { enqueueWork(context, GcmIntentService.class, JOB_ID, intent); } public GcmIntentService() { super("GcmIntentService"); } @Override protected void onHandleWork(@NonNull Intent intent) { Log.d(TAG,"received intent"); String channelId = "MyChannelId"; // 第一步:优先创建通知通道(必须在构建通知之前) NotificationManager manager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel(channelId, "推送通知", NotificationManager.IMPORTANCE_HIGH); channel.setDescription("接收GCM推送的专属通知通道"); manager.createNotificationChannel(channel); } // 第二步:构建符合规范的通知 Notification notification = new NotificationCompat.Builder(getApplication(), channelId) .setSmallIcon(R.drawable.ic_launcher) // 注意:Android 8.0+要求图标为白色透明底,否则会显示异常 .setContentTitle("MyTitle") .setContentText("MyText") .setPriority(NotificationCompat.PRIORITY_HIGH) // 适配Android 7.1及以下的优先级设置 .build(); // 第三步:如果需要将服务设为前台,调用startForeground;否则直接用notify显示普通通知 // 普通推送场景建议直接用notify,避免前台服务占用资源 // startForeground(JOB_ID, notification); manager.notify(1, notification); } }
2. 修改BroadcastReceiver的调用逻辑
不再使用startForegroundService,改用JobIntentService的队列调度方法:
public class GcmBroadcastReceiver extends BroadcastReceiver { private static final String TAG = GcmBroadcastReceiver.class.getName(); @Override public void onReceive(Context context, Intent intent) { Log.d(TAG, "received GCM intent: "+intent.toString()); // 用JobIntentService的enqueueWork替代startForegroundService,适配后台限制 GcmIntentService.enqueueWork(context, intent); } }
3. 更新AndroidManifest.xml配置
给JobIntentService添加必要的绑定权限:
<receiver android:name=".cdmclient.GcmBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND" > <intent-filter> <action android:name="com.google.android.c2dm.intent.RECEIVE" /> <action android:name="com.google.android.c2dm.intent.REGISTRATION" /> <category android:name="${applicationId}.cdmclient" /> </intent-filter> </receiver> <service android:name=".cdmclient.GcmIntentService" android:exported="false" android:permission="android.permission.BIND_JOB_SERVICE"/> <!-- 新增JobService绑定权限 -->
额外优化建议
- 提前创建通知通道:建议在Application的
onCreate方法中提前创建所有通知通道,确保推送触发时通道已经存在,避免线程调度导致的延迟:
public class MyApp extends Application { @Override public void onCreate() { super.onCreate(); createNotificationChannels(); } private void createNotificationChannels() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { NotificationChannel channel = new NotificationChannel("MyChannelId", "推送通知", NotificationManager.IMPORTANCE_HIGH); channel.setDescription("接收GCM推送的专属通知通道"); NotificationManager manager = getSystemService(NotificationManager.class); manager.createNotificationChannel(channel); } } }
- 检查通知图标:Android 8.0+要求通知小图标必须是白色透明背景,否则会显示系统默认图标或不显示,务必确认你的
ic_launcher符合要求。
内容的提问来源于stack exchange,提问作者Locked
相关产品推荐
相关产品推荐

