1. 视频版讲解

Android 推送集成

2. 极光推送 

2.1. 上报 "推送 ID"

在极光提供的 onRegister 接口中调用 SensorsDataAPI.sharedInstance().profilePushId("jiguang_id",s)。

若推送 ID 存在实效性,建议调用 SensorsDataAPI.sharedInstance().profileSet("push_id",s)。

// 推送 ID 的处理
public class MyJPushMessageReceiver extends JPushMessageReceiver {
    @Override
    public void onRegister(Context context, String s) {
        super.onRegister(context, s);
        //上报极光 "推送 ID"
        SensorsDataAPI.sharedInstance().profilePushId("jiguang_id", s);
    }
}
JAVA

在调用神策 SDK login 接口之后调用 SensorsDataAPI.sharedInstance().profilePushId("jiguang_id",JPushInterface.getRegistrationID(this))

SensorsDataAPI.sharedInstance().login(userId);
// 在调用神策 SDK login 接口后,也需要调用 profilePushId 接口上报极光 "推送 ID"
SensorsDataAPI.sharedInstance().profilePushId("jiguang_id",JPushInterface.getRegistrationID(this))
JAVA

2.2. 采集推送点击事件

神策 Android SDK 可以自动采集来自极光、个推、友盟推送的推送点击事件,请参考此文档开启采集。

2.3. 处理推送消息

非厂商通道可以在 onNotifyMessageOpened 接口中处理推送消息

public class MyJPushMessageReceiver extends JPushMessageReceiver {
    @Override
    public void onNotifyMessageOpened(Context context, NotificationMessage notificationMessage) {
        super.onNotifyMessageOpened(context, notificationMessage);
        if (notificationMessage == null) return;
        // 处理神策 Sensors Focus 推送的 "打开 App"、"打开 URL 消息"、"自定义消息" 的动作
        handleSensorsFocusPushMessage(notificationMessage.notificationExtras);
    }
}
JAVA

使用厂商通道的情况下,需要在厂商通道 ActivityonCreateonNewIntent 中拿到 intent,解析出相应的参数再处理推送消息

/**
 * 如果使用了极光 VIP 的厂商通道(uri_activity/uri_action),需要在厂商消息打开的 Activity 中处理厂商通道消息。
 * 处理厂商通道消息的点击事件的 Activity。
 */
public class OpenClickActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 处理厂商通道消息的点击
        handlePushOpen();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        // 处理厂商通道消息的点击
        handlePushOpen();
    }

    /**
     * 处理厂商通道消息的点击。
     * <p>
     * 华为通道消息数据:通过 getIntent().getData().toString(); 获取。
     * 小米、vivo、OPPO、FCM 通道消息数据 :通过 getIntent().getExtras().getString("JMessageExtra") 获取。
     * 魅族通道消息数据:通过 onNotifyMessageOpened 获取。
     */
    private void handlePushOpen() {
        try {
            Intent intent = getIntent();
            if (intent == null) {
                return;
            }
            String pushData = null;
            // 华为通道消息数据
            if (getIntent().getData() != null) {
                pushData = getIntent().getData().toString();
            }
            // 小米、vivo、OPPO、FCM 通道消息数据(魅族会回调 onNotifyMessageOpened )
            if (TextUtils.isEmpty(pushData) && getIntent().getExtras() != null) {
                pushData = getIntent().getExtras().getString("JMessageExtra");
            }
            if (TextUtils.isEmpty(pushData)) {
                return;
            }
            JSONObject jsonObject = new JSONObject(pushData);
            // 推送消息附加字段
            String extras = jsonObject.optString("n_extras");
            // 处理神策智能运营推送的 "打开 App"、"打开 URL"、"自定义消息" 动作
            handleSensorsFocusPushMessage(extras);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
JAVA

3. 友盟推送

3.1. 上报 "推送 ID"

IUmengRegisterCallbackonSuccess 接口中调用 SensorsDataAPI.sharedInstance().profilePushId("umeng_id", token)

若推送 ID 存在实效性,建议调用 SensorsDataAPI.sharedInstance().profileSet("push_id",s)。

代码示例

// 推送 ID 的处理
PushAgent.getInstance(context).register(new IUmengRegisterCallback() {
    @Override
    public void onSuccess(String token) {
        //上报友盟 "推送 ID"
        SensorsDataAPI.sharedInstance().profilePushId("umeng_id", token);
    }

    @Override
    public void onFailure(String s, String s1) {}
});
JAVA

在调用神策 SDK login 接口之后调用 SensorsDataAPI.sharedInstance().profilePushId("umeng_id",PushAgent.getInstance(this).getRegistrationId())。

SensorsDataAPI.sharedInstance().login(userId);
//在调用神策 SDK login 接口后,也需要调用 profilePushId 接口上报友盟 "推送 ID"
SensorsDataAPI.sharedInstance().profilePushId("umeng_id",PushAgent.getInstance(this).getRegistrationId());
JAVA

3.2. 记录 「推送点击」事件并处理平台推送的消息

需要在 launchAppopenUrlopenActivitydealWithCustomAction 这 4 个接口中调用 trackAppOpenNotification(uMessage.extra, uMessage.title, uMessage.text)handleSensorsFocusPushMessage(notificationExtras)。

PushAgent.getInstance(context).setNotificationClickHandler(new UmengNotificationClickHandler() {

    @Override
    public void launchApp(Context context, UMessage uMessage) {
        super.launchApp(context, uMessage);
        // 触发 App 打开推送消息 事件
        trackAppOpenNotification(uMessage.extra, uMessage.title, uMessage.text);
        // 处理神策智能运营推送消息的动作(神策智能运营的推送消息必须在 launchApp 接口中处理)
        handleSensorsFocusPushMessage(uMessage.extra);
    }

    @Override
    public void openUrl(Context context, UMessage uMessage) {
        super.openUrl(context, uMessage);
        // 触发 App 打开推送消息 事件
        trackAppOpenNotification(uMessage.extra, uMessage.title, uMessage.text);
    }

    @Override
    public void dealWithCustomAction(Context context, UMessage uMessage) {
        super.dealWithCustomAction(context, uMessage);
        // 触发 App 打开推送消息 事件
        trackAppOpenNotification(uMessage.extra, uMessage.title, uMessage.text);
    }

    @Override
    public void openActivity(Context context, UMessage uMessage) {
        super.openActivity(context, uMessage);
        // 触发 App 打开推送消息 事件
        trackAppOpenNotification(uMessage.extra, uMessage.title, uMessage.text);
    }
});
JAVA

3.3. 厂商通道使用说明

需要在处理厂商通道的 ActivityonMessage 接口中处理消息,调用 trackAppOpenNotification(extraStr, title, content)handleSensorsFocusPushMessage(notificationExtras)。

/**
 * 友盟厂商通道消息的 Activity 。
 * <p>
 * 该 Activity 需继承自 UmengNotifyClickActivity,同时实现父类的 onMessage 方法,
 * 对该方法的 intent 参数进一步解析即可,该方法异步调用,不阻塞主线程。
 * 并设置 launchMode="singleTask" 和 exported="true"
 */
public class HandlePushActivity extends UmengNotifyClickActivity {

    /**
     * 处理友盟厂商通道消息
     */
    @Override
    public void onMessage(Intent intent) {
        super.onMessage(intent);
        if (intent != null) {
            // 如果你们使用了友盟的厂商通道消息,需要在 onMessage 处理厂商通道消息!!!
            String messageBody = intent.getStringExtra(AgooConstants.MESSAGE_BODY);
            Log.e("TODO", "厂商通道消息:" + messageBody);
            if (!TextUtils.isEmpty(messageBody)) {
                try {
                    JSONObject push = new JSONObject(messageBody);
                    JSONObject extra = push.optJSONObject("extra");
                    String extraStr = null;
                    if (extra != null) {
                        extraStr = extra.toString();
                        // 处理神策智能运营推送消息的动作
                        handleSensorsFocusPushMessage(extraStr);
                    }
                    // 推送标题
                    String title = push.optJSONObject("body").optString("title");
                    // 推送内容
                    String content = push.optJSONObject("body").optString("text");
                    // 触发 App 打开推送消息 事件
                    trackAppOpenNotification(extraStr, title, content);
                    Log.e("TODO",
                            String.format("title: %s。content:%s。extraStr: %s。", title, content, extraStr));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
JAVA

4. 个推推送

4.1. 上报 "推送 ID"

GTIntentServiceonReceiveClientId 接口中调用 SensorsDataAPI.sharedInstance().profilePushId("getui_id",clientId)。

若推送 ID 存在实效性,建议调用 SensorsDataAPI.sharedInstance().profileSet("push_id",s)。

// 推送 ID 的处理
public class GeTuiService extends GTIntentService {
    @Override
    public void onReceiveClientId(Context context, String clientId) {
        //上报个推 "推送 ID"
        SensorsDataAPI.sharedInstance(context).profilePushId("getui_id", clientId);
    }
}
JAVA

在调用神策 SDK login 接口之后调用 SensorsDataAPI.sharedInstance().profilePushId("getui_id", PushManager.getInstance().getClientid(context))。

SensorsDataAPI.sharedInstance().login(userId);
//在调用神策 SDK login 接口后,也需要调用 profilePushId 接口上报个推 "推送 ID"
SensorsDataAPI.sharedInstance().profilePushId("getui_id", PushManager.getInstance().getClientid(context));
JAVA

4.2. 记录 「推送点击」事件并处理平台推送的消息

需要在 onNotificationMessageClicked 记录通知消息被点击的通知标题和通知内容,在 onReceiveMessageData 接口中记录事件并处理平台推送的消息。

public class GeTuiService extends GTIntentService {
	private boolean isNotificationClick = false;
    private String title;
    private String content;

    /**
     * 点击通知消息。
	 * 上报「推送点击」事件需要在 onReceiveMessageData 接口获取 Sensors Focus 的消息内容,因此此处先将通知的标题和内容保存到成员变量中
     */
    @Override
    public void onNotificationMessageClicked(Context context, GTNotificationMessage gtNotificationMessage) {
        title = gtNotificationMessage.getTitle();
        content = gtNotificationMessage.getContent();
        isNotificationClick = true;
    }

	/**
     * 处理透传消息到达 或 通知消息的点击
     */
    @Override
    public void onReceiveMessageData(Context context, GTTransmitMessage gtTransmitMessage) {
        if(context ==null || gtTransmitMessage ==null)return;

        /*
         * 透传消息的处理,此处仅仅演示了 sf_data 推送的相关字段,注意,如果你有原有的逻辑也有相关处理
         * 的逻辑,需要做一定的兼容处理。
         */
        byte[] payload = gtTransmitMessage.getPayload();
        String sfData = new String(payload);

		/*
         * onReceiveMessageData 在 透传消息的到达 与 通知消息的点击 时都会被回调,但只有 通知消息的点击 时需要上报「推送到达」事件。
         * 因此通过 isNotificationClick 变量来判断本次 onReceiveMessageData 被调用是由于 透传消息到达 还是 通知消息的点击
         */
		if (isNotificationClick) {
            isNotificationClick = false;
            trackAppOpenNotification(sfData, title, content);
        	handleSensorsFocusConfig(sfData);
        }else{
        	/*
			 * 透传消息到达需要自定义处理。例如从 sfData 获取出标题和内容来手动触发一个 notification
			 * 透传消息有点击的话,也需要调用 trackAppOpenNotification
         	 */
		}
    }
}
JAVA

4.3. 厂商通道使用说明

首先需要在在推送设置的的 intent 模板配置 intent,以下为配置示例,开发者可根据自己的项目进行配置。

示例代码

intent://www.test.com/path?custom=aaa#Intent;scheme=yang;launchFlags=0x10000000;component=com.sensorsdata.android.push/.HandlePushActivity;S.sf_key={sf_data:{sf_data},title:{sf_customized.title},content:{sf_customized.content}};end
XML


需要在处理厂商通道的 ActivityonCreate 中处理 intent,解析出相应的参数,调用 trackAppOpenNotification(extras, title, content)

public class HandlePushActivity extends AppCompatActivity {

	private static final String TAG = "HandlePushActivity";
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_test);
        handlePushIntent();
    }

    /**
     * 处理推送消息的 Intent
     */
    private void handlePushIntent() {
        Intent intent = getIntent();
        if (intent != null) {
            // 拿到自定义透传字段的值
            String sf_key = intent.getStringExtra("sf_key");
            Log.i(TAG, sf_key);
  			if (TextUtils.isEmpty(sf_key)) {
                return;
            }
			try {
				JSONObject jsonObject = new JSONObject(sf_key);
                String title = jsonObject.optString("title");
                String content = jsonObject.optString("content");
				String extras = jsonObject.optString("sf_data");
                trackAppOpenNotification(extras, title, content);
            } catch (JSONException e) {
                e.printStackTrace();
            }
        }
    }

}
JAVA

5. 测试推送

  1. 首先发送推送 ID 到神策后,选择上报推送 ID 的 key(这里以 jiguang_id 为例),点击测试推送。
  2. 填入测试机的推送 ID、推送标题、内容,如下图所示。
  3. 确认推送,可查看手机是否收到推送,以及查看埋点上报字段是否正确。

6. 附录

6.1. trackAppOpenNotification 方法详情

因各个推送不同,开发者可根据实际的字段及业务需求进行字段的解析。

 /**
 * 埋点 App 打开推送消息
 * <p>
 * 事件名:$AppPushClick
 *
 * @param extras 推送消息的 extras(参数类型只能传 String 或 Map<String,String>)
 * @param notificationTitle 推送消息的标题
 * @param notificationContent 推送消息的内容
 */
public static void trackAppOpenNotification(String extras, String notificationTitle, String notificationContent) {
    try {
        JSONObject jsonObject = null;
        if (!TextUtils.isEmpty(extras)) {
            jsonObject = new JSONObject(extras);
        }
        JSONObject properties = new JSONObject();
        // 获取消息标题,并保存在事件属性 msg_title 中
        properties.put("$sf_msg_title", notificationTitle);
        // 获取消息 ID,并保存在事件属性 msg_id 中
        properties.put("$sf_msg_content", notificationContent);
        if (jsonObject != null) {
            properties.put("$sf_msg_id", jsonObject.opt("sf_msg_id"));
            properties.put("$sf_plan_id", jsonObject.opt("sf_plan_id"));
            if (!"null".equals(jsonObject.opt("sf_audience_id"))) {
                properties.put("$sf_audience_id", jsonObject.opt("sf_audience_id"));
            }
            properties.put("$sf_link_url", jsonObject.opt("sf_link_url"));
            properties.put("$sf_plan_strategy_id", jsonObject.opt("sf_plan_strategy_id"));
            properties.put("$sf_plan_type", jsonObject.opt("sf_plan_type"));
            properties.put("$sf_strategy_unit_id", jsonObject.opt("sf_strategy_unit_id"));
            properties.put("$sf_enter_plan_time", jsonObject.opt("sf_enter_plan_time"));
            properties.put("$sf_channel_id", jsonObject.opt("sf_channel_id"));
            properties.put("$sf_channel_category", jsonObject.opt("sf_channel_category"));
            properties.put("$sf_channel_service_name", jsonObject.opt("sf_channel_service_name"));
        }
        // 使用神策分析追踪 "App 消息推送成功" 事件
        SensorsDataAPI.sharedInstance().track("$AppPushClick", properties);
    } catch (Exception e) {
        e.printStackTrace();
    }
}
JAVA

6.2. handleSensorsFocusPushMessage 方法详情

/**
 * 处理神策智能运营推送消息。
 * TODO 此方法只是解析了神策智能运营的推送消息,具体的业务跳转逻辑需要开发者加上!!!
 *
 * @param notificationExtras 推送消息的 extra
 */
public static void handleSensorsFocusPushMessage(Object notificationExtras) {
    try {
        String sfData = null;
        if (notificationExtras != null) {
            if (notificationExtras instanceof String) {
                sfData = new JSONObject((String) notificationExtras).optString("sf_data");
            } else if (notificationExtras instanceof Map) {
                sfData = new JSONObject((Map) notificationExtras).optString("sf_data");
            }
        }
        if (!TextUtils.isEmpty(sfData)) {
            JSONObject sfJson = new JSONObject(sfData);
            if ("OPEN_APP".equals(sfJson.optString("sf_landing_type"))) {
                // TODO 处理打开 App 消息,--> 请启动 App
                Log.e("TODO", "-- 请启动 App --");
            } else if ("LINK".equals(sfJson.optString("sf_landing_type"))) {
                String url = sfJson.optString("sf_link_url");
                if (!TextUtils.isEmpty(url)) {
                    // TODO 处理打开 URL 消息,--> 请处理 URL
                    Log.e("TODO", "-- 请处理打开 URL --: " + url);
                }
            } else if ("CUSTOMIZED".equals(sfJson.optString("sf_landing_type"))) {
                JSONObject custom = sfJson.optJSONObject("customized");
                if (custom != null) {
                    // TODO 处理自定义消息,--> 请处理自定义消息
                    Log.e("TODO", "-- 请处理自定义消息--: " + custom);
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
JAVA

6.3. handleSensorsFocusConfig 方法详情

/**
 * 处理神策智能运营推送消息。
 * TODO 此方法只是解析了神策智能运营的推送消息,具体的业务跳转逻辑需要开发者加上!!!
 *
 * @param sfData 配置
 */
public static void handleSensorsFocusConfig(String sfData) {
    try {
        if (!TextUtils.isEmpty(sfData)) {
            JSONObject sfJson = new JSONObject(sfData);
            if ("OPEN_APP".equals(sfJson.optString("sf_landing_type"))) {
                // TODO 处理打开 App 消息,--> 请启动 App
                Log.e("TODO", "-- 请启动 App --");
            } else if ("LINK".equals(sfJson.optString("sf_landing_type"))) {
                String url = sfJson.optString("sf_link_url");
                if (!TextUtils.isEmpty(url)) {
                    // TODO 处理打开 URL 消息,--> 请处理 URL
                    Log.e("TODO", "-- 请处理打开 URL --: " + url);
                }
            } else if ("CUSTOMIZED".equals(sfJson.optString("sf_landing_type"))) {
                JSONObject custom = sfJson.optJSONObject("customized");
                if (custom != null) {
                    // TODO 处理自定义消息,--> 请处理自定义消息
                    Log.e("TODO", "-- 请处理自定义消息--: " + custom);
                }
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}
JAVA

6.4. 附录方法的封装类

如果不想把附录中的示例方法一个个复制到项目中,可以使用封装好的工具类 SensorsFocusHelper 来调用示例方法。使用步骤如下:

  1. 下载 SensorsFocusHelper 类,解压后复制到项目中
  2. 给 SensorsFocusHelper 类添加 package 声明
  3. 调用 SensorsFocusHelper 类中的同名方法

附录中的 handleSensorsFocusPushMessage 方法与 handleSensorsFocusConfig 方法需要开发者在 TODO 注释的位置添加上具体的业务跳转逻辑,SensorsFocusHelper 类将具体的业务跳转逻辑封装成接口 SensorsFocusHandler,具体的使用方式请参考以下示例:

SensorsFocusHelper.handleSensorsFocusPushMessage(notificationExtras, new SensorsFocusHelper.SensorsFocusHandler() {
            @Override
            public void handleOpenApp() {
                // TODO 处理打开 App 消息,--> 请启动 App
            }

            @Override
            public void handleLink(String url) {
                // TODO 处理打开 URL 消息,--> 请处理 URL
            }

            @Override
            public void handleCustomized(JSONObject customProperties) {
                // TODO 处理自定义消息,--> 请处理自定义消息
            }
        });
JAVA