menu
Is this helpful?

# Android推送集成文档

# 自动回收方案

自动回收方案是SDK内部自动上报各平台(当前阶段是FCM和极光)推送token和推送消息点击事件(te_ops_push_click)。

接入流程:

  1. 升级数据采集SDK版本至3.0.1-beta.2
implementation 'cn.thinkingdata.android:ThinkingAnalyticsSDK:3.0.1-beata.2'
  1. 引入全埋点插件,版本2.1.0
buildscript {
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'cn.thinkingdata.android:android-gradle-plugin2:2.1.0'
    }
}

在项目 build.gradle 文件中配置插件相关参数

apply plugin: 'cn.thinkingdata.android'
android {

}
  1. 开启自动回收推送链路
val config = TDConfig.getInstance(
    this,
    TA_APP_ID,
    TA_SERVER_URL
)
//开启推送自动回收
config.enableAutoPush()
TDAnalytics.init(config)
  1. 账号切换

切换账号之后需要调用login接口,SDK内部会自动将推送token通过user_set接口上报至新的账号。

TDAnalytics.login("new_account_id")

# 一、FCM推送

自定义FirebaseMessagingService,该方案的原理是在编译期hook FirebaseMessagingService中的onNewToken方法,所以需要提供这个类。

class MyFirebaseMessagingService extends FirebaseMessagingService {
    @Override
    public void onNewToken(@NonNull String token) {
        
    } 
}

在manifest文件中注册

<service
    android:name="自定义service"
    android:exported="false">
    <intent-filter>
        <action android:name="com.google.firebase.MESSAGING_EVENT" />
    </intent-filter>
</service>

# 二、极光推送

自定义JPushMessageReceiver,该方案的原理是在编译期hook JPushMessageReceiver中的onRegister方法,所以需要提供这个类。

public class PushMessageReceiver extends JPushMessageReceiver {
    @Override
    public void onRegister(Context context, String registrationId) {
        
    }
}

在manifest文件中注册

<receiver
    android:name="自定义 Receiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter>
        <action android:name="cn.jpush.android.intent.RECEIVE_MESSAGE" />
        <category android:name="您应用的包名" />
    </intent-filter>
</receiver>

# 手动回收方案

# 一、FCM推送

# 1.1 上报 "推送 ID"

  • 在调用TA login或者切换账号之后上传FCM的Token。
//TASDK初始化
TDAnalytics.init(this, APPID, SERVER_URL);
TDAnalytics.login("user_id");
FirebaseMessaging.getInstance().getToken()
    .addOnCompleteListener(new OnCompleteListener<String>() {
        @Override
        public void onComplete(@NonNull Task<String> task) {
          if (!task.isSuccessful()) {
            Log.w(TAG, "Fetching FCM registration token failed", task.getException());
            return;
          }

          // Get new FCM registration token
          String token = task.getResult();
          JSONObject properties = new JSONObject();
          properties.put("fcm_token",token);
          TDAnalytics.userSet(properties);
        }
 });
  • 在FCM Token变更时,更新用户属性:
@Override
public void onNewToken(@NonNull String token) {
    JSONObject properties = new JSONObject();
    properties.put("fcm_token",token);
    TDAnalytics.userSet(properties);
}

# 1.2. 采集推送点击事件

在用户点击通知时上传推送点击事件,可以在onCreate或者onNewIntent获取推送参数。

public class PushOpenClickActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handlePushOpen();
    }
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        handlePushOpen();
    }

    private void handlePushOpen() {
        try {
            Intent intent = getIntent();
            if (intent == null) {
                return;
            }
            Bundle bundle = intent.getExtras();
            if (bundle == null) {
                return;
            }
            String teExtras = bundle.getString("te_extras");
            TEPushUtil.trackAppOpenNotification(teExtras);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

# 1.3. 处理推送消息

建议以下两种方案二选一

  • 普通参数:调用handleTEPushAction方法。
  • 透传参数:调用handleTEPassThroughAction方法。
public class PushOpenClickActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handlePushOpen();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        handlePushOpen();
    }

    private void handlePushOpen() {
        try {
            Intent intent = getIntent();
            if (intent == null) {
                return;
            }
            Bundle bundle = intent.getExtras();
            if (bundle == null) {
                return;
            }
            String teExtras = bundle.getString("te_extras");
            //如果处理普通参数 调用如下方法
            TEPushUtil.handleTEPushAction(teExtras);
            //如果处理透传参数 调用如下方法
            TEPushUtil.handleTEPassThroughAction(teExtras);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

# 二、极光推送

# 2.1 上报 "推送 ID"

  • 在调用TA login或者切换账号之后上传极光的Registration ID。
//登录或者切换账号之后调用
TDAnalytics.login("user_id");

JSONObject properties = new JSONObject();
properties.put("jiguang_id",JPushInterface.getRegistrationID(context));
//instance为TA实例
TDAnalytics.userSet(properties);
  • 在极光提供的onRegister接口中上传极光的Registration ID。
public class PushMessageReceiver extends JPushMessageReceiver {
    @Override
    public void onRegister(Context context, String registrationId) {
        JSONObject properties = new JSONObject();
        properties.put("jiguang_id",registrationId);
        //instance为TA实例
        TDAnalytics.userSet(properties);
    }
}

# 2.2. 采集推送点击事件

非厂商通道可以在 onNotifyMessageOpened 接口中发送推送点击事件。

public class PushMessageReceiver extends JPushMessageReceiver {
    
    @Override
    public void onNotifyMessageOpened(Context context, NotificationMessage message) {
        //在通知点击回调中发送通知点击事件
        String te_extras = TAPushUtil.getPushExtras(message.notificationExtra);
        TEPushUtil.trackAppOpenNotification(te_extras);
    }
    
}

使用厂商通道的情况下,需要在厂商通道 Activity 的 onCreateonNewIntent 中拿到 intent,解析出相应的参数再发送推送点击事件

public class PushOpenClickActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handlePushOpen();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        handlePushOpen();
    }

    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");
            String te_extras = TAPushUtil.getPushExtras(extras);
            TEPushUtil.trackAppOpenNotification(te_extras);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

# 2.3. 处理推送消息

建议以下两种方案二选一

  • 普通参数:调用handleTEPushAction方法。
  • 透传参数:调用handleTEPassThroughAction方法。

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

public class PushMessageReceiver extends JPushMessageReceiver {
    
    @Override
    public void onNotifyMessageOpened(Context context, NotificationMessage message) {
        //在通知点击回调中发送通知点击事件
        String te_extras = TAPushUtil.getPushExtras(message.notificationExtra);
        //如果处理普通参数 调用如下方法
        TEPushUtil.handleTEPushAction(teExtras);
        //如果处理透传参数 调用如下方法
        TEPushUtil.handleTEPassThroughAction(teExtras);
    }
    
}

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

public class PushOpenClickActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handlePushOpen();
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        handlePushOpen();
    }

    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");
            String te_extras = TAPushUtil.getPushExtras(extras);
            //如果处理普通参数 调用如下方法
            TEPushUtil.handleTEPushAction(teExtras);
            //如果处理透传参数 调用如下方法
            TEPushUtil.handleTEPassThroughAction(teExtras);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

}

# 附录

# 客户端收到推送参数示例

以下仅展示客户端收到的扩展字段参数

{
    "te_extras": {
        //点击推送的跳转方式
        "ops_loading_type": "OPEN_APP",
        //透传参数
        "passthrough_params": {
                "param1": "abc",
                "param2": 101,
                "param3": [{
                        "subText1": "xyz",
                        "subText2": 2
                }]
        },
        //TE运营通道回执属性
        "#ops_receipt_properties": {
                "ops_task_id": "0082",
                "ops_project_id": 1,
                "ops_task_instance_id": "0082_20230331",
                "ops_push_language": "default",
                "ops_task_exec_detail_id": "55"
        }
    }
}

# 如何验证客户推送回收链路接入成功?

  1. 接入推送之后,首次启动或者推送token变更时,是否会上报以下事件。
{
    "#type": "user_set",
    "#time": "2023-11-13 15:50:55.729",
    "#distinct_id": "distinct",
    "properties": {
        "jiguang_id": "190e35f7e15c8481caa"
    },
    "#uuid": "9f233c31-a664-46ff-94d6-f767a3098c3a"
}
  1. 点击推送通知启动应用,观察是否会上传te_ops_push_click事件,观察事件属性中是否包含ops_receipt_properties。
{
    "#type": "track",
    "#time": "2023-03-16 16:08:32.191",
    "#distinct_id": "90d80464-6832-43f1-80d9-bd93fc09c4fe",
    "#event_name": "te_ops_push_click",
    "properties": {
        "#lib_version": "3.0.1-beata.1",
        "#carrier": "中国移动",
        "#os": "Android",
        "#device_id": "6262ca7f71e6aca3",
        "#screen_height": 2400,
        "#bundle_id": "cn.thinkingdata.random",
        "#device_model": "M2012K11AC",
        "#screen_width": 1080,
        "#system_language": "zh",
        "#install_time": "2023-03-10 11:24:44.285",
        "#simulator": false,
        "#lib": "Android",
        "#manufacturer": "Xiaomi",
        "#os_version": "11",
        "#app_version": "1.0",
        "#fps": 60,
        "#network_type": "WIFI",
        "#ram": "2.7\/7.4",
        "#disk": "4.6\/106.3",
        "#device_type": "Phone",
        "ops_receipt_properties": {
            "ops_project_id": 1,
            "ops_request_id": "3b21d2a8-8d3d-44fa-b460-3bb311ed3bcd"
        },
        "#zone_offset": 8
    },
    "#uuid": "7a977e23-b78a-4433-baae-ead17ad2fde9"
}

# trackAppOpenNotification

public static void trackAppOpenNotification(String extras) {
    try {
        if (TextUtils.isEmpty(extras)) {
            return;
        }
        JSONObject jsonObject = new JSONObject(extras);
        JSONObject properties = new JSONObject();
        Object obj = json.opt("#ops_receipt_properties");
        JSONObject ops = null;
        if (obj instanceof String) {
            ops = new JSONObject(( String ) obj);
        } else if (obj instanceof JSONObject) {
            ops = ( JSONObject ) obj;
        }
        properties.put("#ops_receipt_properties", ops);
        TDAnalytics.track("te_ops_push_click", properties);
        //立马上报
        TDAnalytics.flush();
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# handleTEPushAction

public static void handleTEPushAction(String extras) {
    try {
        if (TextUtils.isEmpty(extras)) {
            return;
        }
        JSONObject jsonObject = new JSONObject(extras);
        String type = jsonObject.optString("ops_loading_type");
        if ("OPEN_APP".equals(type)) {
            // TODO 处理打开 App 消息,--> 请启动 App
        } else if ("OPEN_URL".equals(type)) {
            String url = jsonObject.optString("ops_url");
            if (!TextUtils.isEmpty(url)) {
                // TODO 处理打开 URL 消息,--> 请处理 URL
            }
        } else if ("CUSTOMIZED".equals(type)) {
            String custom = jsonObject.optString("ops_customized");
            if (!TextUtils.isEmpty(custom)) {
                // TODO 处理自定义消息,--> 请处理自定义消息
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# handleTEPassThroughAction

public static void handleTEPassThroughAction(String extras) {
    try {
        if (TextUtils.isEmpty(extras)) {
            return;
        }
        JSONObject jsonObject = new JSONObject(extras);
        String params = jsonObject.optString("passthrough_params");
        //params为透传参数,接下来是实现具体的业务逻辑
    } catch (Exception e) {
        e.printStackTrace();
    }
}

# TEPushUtil类

public class TEPushUtil {

    public static void trackAppOpenNotification(String extras) {
        try {
            if (TextUtils.isEmpty(extras)) {
                return;
            }
            JSONObject jsonObject = new JSONObject(extras);
            JSONObject properties = new JSONObject();
            Object obj = json.opt("#ops_receipt_properties");
            JSONObject ops = null;
            if (obj instanceof String) {
                ops = new JSONObject(( String ) obj);
            } else if (obj instanceof JSONObject) {
                ops = ( JSONObject ) obj;
            }
            properties.put("#ops_receipt_properties", ops);
            TDAnalytics.track("te_ops_push_click", properties);
            //立马上报
            TDAnalytics.flush();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void handleTEPushAction(String extras) {
        try {
            if (TextUtils.isEmpty(extras)) {
                return;
            }
            JSONObject jsonObject = new JSONObject(extras);
            String type = jsonObject.optString("ops_loading_type");
            if ("OPEN_APP".equals(type)) {
                // TODO 处理打开 App 消息,--> 请启动 App
            } else if ("OPEN_URL".equals(type)) {
                String url = jsonObject.optString("ops_url");
                if (!TextUtils.isEmpty(url)) {
                    // TODO 处理打开 URL 消息,--> 请处理 URL
                }
            } else if ("CUSTOMIZED".equals(type)) {
                String custom = jsonObject.optString("ops_customized");
                if (!TextUtils.isEmpty(custom)) {
                    // TODO 处理自定义消息,--> 请处理自定义消息
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    public static void handleTEPassThroughAction(String extras) {
        try {
            if (TextUtils.isEmpty(extras)) {
                return;
            }
            JSONObject jsonObject = new JSONObject(extras);
            String params = jsonObject.optString("passthrough_params");
            //params为透传参数,接下来是实现具体的业务逻辑
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public static String getPushExtras(Object notificationExtras) {
        String teExtras = "";
        try {
            if (notificationExtras != null) {
                if (notificationExtras instanceof String) {
                    teExtras = new JSONObject((String) notificationExtras).optString("te_extras");
                } else if (notificationExtras instanceof Map) {
                    teExtras = new JSONObject((Map) notificationExtras).optString("te_extras");
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return teExtras;
    }

}