21、通知的点击统计:通过PendingIntent传递统计参数、BroadcastReceiver统计点击、Firebase Analytics集成

通知发出去,用户点没点?点了哪个?这是每个做推送的工程师都绕不开的问题。说实话,我见过太多团队把通知发得满天飞,结果一问点击率,全凭感觉猜。这不行。

今天我们就来聊聊通知点击统计这件事。我会把三种主流方案都讲透:通过PendingIntent传参BroadcastReceiver拦截统计、以及接入Firebase Analytics。这三种方案各有适用场景,我建议你根据项目实际情况灵活组合。

核心观点:通知点击统计不是可选项,而是推送系统的标配。没有统计,你根本不知道推送效果如何,更别提优化了。
通知点击统计方案架构 通知点击统计 PendingIntent传参 BroadcastReceiver Firebase Analytics Intent extra 传 campaign_id Activity 中解析并上报 动态注册 BroadcastReceiver onReceive 中统计点击 FirebaseAnalytics.logEvent 自动归因与报表

方案一:通过PendingIntent传递统计参数

这是最基础、也是最直接的方式。说白了,就是在构建通知的时候,把统计需要的参数塞进Intent里,用户点击通知后,在目标Activity里取出来上报。

我个人习惯用campaign_id来标识每次推送活动。你想想看,如果没有这个ID,你根本不知道用户点的是哪一次推送。

// 构建通知时传入统计参数
Intent intent = new Intent(context, NotificationClickActivity.class);
intent.putExtra("campaign_id", "campaign_2024_001");
intent.putExtra("push_type", "promotion");
intent.putExtra("timestamp", System.currentTimeMillis());

PendingIntent pendingIntent = PendingIntent.getActivity(
    context,
    requestCode,
    intent,
    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);

NotificationCompat.Builder builder = new NotificationCompat.Builder(context, CHANNEL_ID)
    .setSmallIcon(R.drawable.ic_notification)
    .setContentTitle("限时优惠")
    .setContentText("今日全场8折,点击查看")
    .setContentIntent(pendingIntent)
    .setAutoCancel(true);
小提示:Android 12 以上要求显式声明 FLAG_IMMUTABLE 或 FLAG_MUTABLE。我建议大部分场景用 FLAG_IMMUTABLE,除非你需要修改Intent里的数据。

然后在目标Activity里解析参数并上报:

public class NotificationClickActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        // 解析统计参数
        Intent intent = getIntent();
        if (intent != null) {
            String campaignId = intent.getStringExtra("campaign_id");
            String pushType = intent.getStringExtra("push_type");
            long timestamp = intent.getLongExtra("timestamp", 0L);
            
            // 上报点击事件
            reportNotificationClick(campaignId, pushType, timestamp);
        }
        
        // 跳转到真正的目标页面
        navigateToTargetPage();
        finish();
    }
    
    private void reportNotificationClick(String campaignId, String pushType, long timestamp) {
        // 这里可以调用自己的统计SDK,或者埋点到本地日志
        Log.d("NotificationStats", "点击统计: campaign=" + campaignId 
            + ", type=" + pushType + ", time=" + timestamp);
        
        // 实际项目中通常会调用统计SDK
        // AnalyticsTracker.trackEvent("notification_click", params);
    }
}
注意:如果用户从最近任务列表重启Activity,getIntent()返回的还是启动时的Intent。这时候你需要判断一下,避免重复上报。我一般会在SharedPreferences里记录已上报的campaign_id+timestamp组合。

方案二:BroadcastReceiver统计点击

这个方案我特别喜欢,因为它不依赖Activity。什么意思呢?就是即使用户点击通知时App被杀死了,BroadcastReceiver也能被唤醒,完成统计后再决定要不要启动Activity。

我曾经在一个日活千万级的项目里用过这个方案。当时遇到一个坑:通知点击后Activity启动太慢,统计上报延迟严重。换成BroadcastReceiver后,统计几乎实时完成。

// 1. 定义 BroadcastReceiver
public class NotificationClickReceiver extends BroadcastReceiver {
    private static final String ACTION_NOTIFICATION_CLICK = 
        "com.example.action.NOTIFICATION_CLICK";
    
    @Override
    public void onReceive(Context context, Intent intent) {
        if (ACTION_NOTIFICATION_CLICK.equals(intent.getAction())) {
            String campaignId = intent.getStringExtra("campaign_id");
            String pushType = intent.getStringExtra("push_type");
            
            // 立即统计点击
            Log.d("NotificationStats", "BroadcastReceiver 统计: " + campaignId);
            
            // 上报到统计服务
            reportClick(campaignId, pushType);
            
            // 然后启动真正的目标Activity
            Intent targetIntent = new Intent(context, TargetActivity.class);
            targetIntent.putExtras(intent.getExtras());
            targetIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(targetIntent);
        }
    }
    
    private void reportClick(String campaignId, String pushType) {
        // 这里可以调用OkHttp直接上报,或者用WorkManager延迟上报
        // 我建议用WorkManager,保证上报成功
    }
}

// 2. 动态注册(推荐在Application中注册)
public class MyApp extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        
        NotificationClickReceiver receiver = new NotificationClickReceiver();
        IntentFilter filter = new IntentFilter(NotificationClickReceiver.ACTION_NOTIFICATION_CLICK);
        registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED);
    }
}

// 3. 构建通知时使用 Broadcast PendingIntent
Intent clickIntent = new Intent(NotificationClickReceiver.ACTION_NOTIFICATION_CLICK);
clickIntent.setPackage(context.getPackageName());
clickIntent.putExtra("campaign_id", "campaign_2024_002");
clickIntent.putExtra("push_type", "reminder");

PendingIntent pendingIntent = PendingIntent.getBroadcast(
    context,
    requestCode,
    clickIntent,
    PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE
);
关键区别:PendingIntent.getActivity 启动Activity,而 PendingIntent.getBroadcast 发送广播。广播接收器在onReceive里可以立即执行统计逻辑,不受Activity生命周期影响。

方案三:Firebase Analytics集成

如果你的项目已经接入了Firebase,那直接用Firebase Analytics做通知点击统计是最省事的。它自带自动归因功能,能帮你把点击和转化关联起来。

我记得第一次用Firebase做推送统计时,发现它连用户从点击到下单的完整路径都能追踪。说实话,这比自己埋点省了太多事。

// 1. 在build.gradle中添加依赖
// implementation 'com.google.firebase:firebase-analytics:21.5.0'
// implementation 'com.google.firebase:firebase-messaging:23.4.0'

// 2. 在通知点击时记录事件
public class NotificationClickActivity extends AppCompatActivity {
    private FirebaseAnalytics firebaseAnalytics;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        
        firebaseAnalytics = FirebaseAnalytics.getInstance(this);
        
        Intent intent = getIntent();
        if (intent != null) {
            String campaignId = intent.getStringExtra("campaign_id");
            
            // Firebase 自动归因参数
            Bundle params = new Bundle();
            params.putString(FirebaseAnalytics.Param.CAMPAIGN, campaignId);
            params.putString(FirebaseAnalytics.Param.MEDIUM, "push_notification");
            params.putString(FirebaseAnalytics.Param.SOURCE, "firebase");
            
            // 记录通知打开事件
            firebaseAnalytics.logEvent(FirebaseAnalytics.Event.NOTIFICATION_OPEN, params);
            
            // 也可以记录自定义事件
            // firebaseAnalytics.logEvent("custom_notification_click", params);
        }
    }
}

// 3. 在Firebase控制台查看报表
// 路径: Analytics -> 事件 -> notification_open
// 可以看到点击次数、点击率、以及后续的转化数据
技巧:Firebase Analytics 的 notification_open 事件是系统预定义的,会自动关联推送归因。但如果你需要更细粒度的统计,建议用自定义事件,比如 "promotion_notification_click" 带上商品ID。

三种方案对比

方案 优点 缺点 适用场景
PendingIntent传参 实现简单,无需额外组件 依赖Activity生命周期,App被杀可能丢失 小型项目、快速验证
BroadcastReceiver 独立于Activity,统计及时 需要动态注册,Android 8+限制隐式广播 中大型项目、对统计实时性要求高
Firebase Analytics 自动归因,报表完善,无需自建后端 依赖Google服务,国内需要特殊处理 海外项目、已接入Firebase的项目

我的实战建议

嗯,这里我想分享一些踩坑后的经验:

  • 不要只用一种方案。我建议用BroadcastReceiver做核心统计,同时把参数传给Activity做二次校验。这样即使广播没收到,Activity还能补报。
  • 注意去重。用户可能多次点击同一条通知。我习惯在统计时加一个click_id(UUID),服务端根据这个ID去重。
  • 国内环境要备选方案。Firebase在国内不稳定。我一般在海外版用Firebase,国内版用自建统计+友盟/极光。
  • 测试要覆盖极端情况。我曾经遇到过通知点击后App被系统杀死,统计还没上报就丢了。后来用WorkManager加了一个兜底上报任务,才彻底解决。
一句话总结:通知点击统计的核心就两件事——把参数传到位把事件报上去。至于用哪种方式,取决于你的项目规模和网络环境。但不管用哪种,一定要做

公众号:蓝海资料掘金营,微信deep3321