基础集成

重要

本文中含有需要您注意的重要提示信息,忽略该信息可能对您的业务造成影响,请务必仔细阅读。

1 路径设置

需要检查目前是否已经使用了友盟+SDK,如果已经使用,请及时更改SDK文件路径:

  • 已经集成了友盟+SDK,现在需要集成QT SDK:在QT和友盟+的所有代码最前面增加(至少早于收数域名)QTConfigure.resetStorePath

  • 已经集成了QT SDK,现在需要集成友盟+SDK:在QT和友盟+的所有代码最前面增加(至少早于收数域名)UMConfigure.resetStorePath

警告

如果不按照上述的逻辑调用,则会使友盟+SDK与QT SDK共同使用一个存储路径,导致日志混乱。具体逻辑为:先调用的哪个SDK初始化方法,就重新设置另外一个SDK的文件路径,比如先初始化的友盟+SDK,就调用 QTConfigure.resetStorePath;,如果是先初始化的QT SDK,就需要调用UMConfigure.resetStorePath;

2 域名设置

在预初始化之前,开发者需要在调用SDK任意其它接口之前最先调用QtConfigure.setCustomDomain()接口设置私有化环境收数域名。

/**
 * 设置上传统计日志的主域名和备用域名。此函数必须在SDK预初始化/初始化函数调用之前调用。SDK会优先将统计数据上报到主域名,失败的情况下会再尝试将数据上报到备用域名。
 * 主域名primaryDomain或不能传入null或者空串,如果传入null或者空串,SDK预初始化函数会抛出SdkDomainUndefined运行时异常。  
 * @param standbyDomain 备用域名可以传入null或者空串,此时SDK认为备用域名和主域名完全相同。SDK上传数据失败后第二次也会向主域名上报数据。
 * 传入的域名参数应该包含"https://" 前缀。  
 */
public static void setCustomDomain(String primaryDomain, String standbyDomain)

参数

含义

primaryDomain

上传日志的主域名收数地址。

standbyDomain

上传日志备用域名收数地址。

示例:

public class QTApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();

        QtConfigure.setCustomDomain("xxxxxx", null); // 请传入您自己的收数域名
        
        // 打开调试log
        QtConfigure.setLogEnabled(true);

        QtConfigure.preInit(this, "xxxxxx", "Channel");// 请传入您自己的appkey
        QtTrackAgent.disableActivityPageCollection();

// ...

3 合规初始化

3.1 初始化接口

由于工信部的合规要求App在用户同意隐私政策前不可以获取任何个人信息,所以 Quick Tracking 修改初始化为以下操作:

  1. 您需要确保App有《隐私政策》,并且在用户首次启动App时就弹出《隐私政策》取得用户同意。

  2. 您务必告知用户您选择Quick Tracking SDK服务,请在《隐私政策》中增加如下参考条款:“我们的产品集成Quick Tracking SDK,Quick Tracking SDK需要收集您的OAID/GAID/MAC/IMEI/Android ID/IDFA/IDFV/OPENUDID/GUID/SIM卡 IMSI 信息/硬件序列号/MCC(移动国家编码)、MNC(移动网号)以提供统计分析服务。

  3. 在用户同意隐私政策后才可以正式初始化Quick Tracking SDK,具体技术操作如下:

  • SDK预初始化接口

在Applicaiton.onCreate函数中调用预初始化函数QtConfigure.preInit(),预初始化函数不会采集设备信息,也不会向Quick Tracking后台上报数据。

// SDK预初始化函数
// preInit预初始化函数耗时极少,不会影响App首次冷启动用户体验
public static void preInit(Context context,String appkey,String channel)
  • 同意隐私协议后的正式初始化

一旦App获取到《隐私政策》的用户授权,后续的App冷启动,开发者应该保证在Applicaiton.onCreate函数中调用预初始化函数QtConfigure.preInit()。正式初始化函数QtConfigure.init可以按需调用(可以在预初始化函数之后紧接着调用,也可以放到后台线程中延迟调用,但还是必须调用,不能遗漏)

public static void init(Context context,String appkey,String channel,int deviceType,String pushSecret);

参数

含义

备注

appkey

QT为当前应用平台颁发的唯一标识。

  1. 入参的appkey一定要与QT后台保持一致;

  2. 该appkey会跟随每一条事件日志进行上报,用于QT的应用平台数据区分标识

channel

app投放的应用市场

QT分析平台的“系统属性”中“升级渠道”的数据来源

deviceType

QtConfigure.DEVICE_TYPE_PHONE

默认填写QtConfigure.DEVICE_TYPE_PHONE即可

pushSecret

废弃字段,填写空即可

废弃字段,填写空即可

示例:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        //必须在Applicaiton.onCreate函数中调用该预初始化函数
        QtConfigure.preInit(this,"您的appkey","应用商店名称");
        
        //同意隐私政策,才可以正式初始化Quick Tracking SDK
        if(isflag==1)
        {
          QtConfigure.init(this,"您的appkey","应用商店名称",QtConfigure.DEVICE_TYPE_PHONE, "");
        }
    }
}

3.2 Appkey获取

在初始化SDK时,需要填写参数Appkey。Appkey是在Quick Tracking中代表应用的唯一ID,在创建应用时生成,其获取或查看方法详见文档:应用管理

4 日志打印

可通过QtConfigure.setLogEnabled(boolean)接口控制【Quick Tracking】LOG的输出。

注意:

  • App正式上线前请关闭SDK运行调试日志。避免无关Log输出。

可以通过调用如下方法控制SDK运行调试日志是否输出,默认情况下SDK运行调试日志关闭。需要用户手动打开。

/**
* 设置组件化的Log开关
* 参数:boolean 默认为false,如需查看LOG设置为true
*/
QtConfigure.setLogEnabled(true);

注意:

  • 如果查看初始化过程中的LOG,一定要在调用初始化方法前将LOG开关打开。

  • 日志分为四种等级,方便用户查看:

    • Error(打印SDK集成或运行时错误信息)。

    • Warn(打印SDK警告信息)。

    • Info(打印SDK提示信息)。

    • Debug(打印SDK调试信息)。

5 特殊场景

5.1 App内强制kill或exit之类的方法杀死进程

如果开发者调用kill或者exit之类的方法杀死进程,请务必在此之前调用QtTrackAgent.onKillProcess方法,用来保存统计数据。

public static void onKillProcess(Context context);

参数

含义

context

当前宿主进程的ApplicationContext上下文。

6 数据自定义加密

6.1 自定义编解码接口函数:

SDK端不直接参与开发者自定义的加解密算法具体实现,SDK和收数端提供接口CryptoProvider,由客户自行实现此接口中的如下两个方法。

public interface CryptoProvider {
    byte[] encode(Map<String, String> header, byte[] input);
    byte[] decode(Map<String, String> header, byte[] input);
}

接口方法

参数说明

public byte[] encode(Map<String, String> header, byte[] input);

参数: header: 需要传递给解密端的额外信息(如:使用的加密算法,加密模式等等任何信息)。 以多个字符串K-V键值对的形式传入。SDK会将此Map中所有K-V键值对参数内容做base64编码后作为http header字段dc-args传递给收数服务端。 收数服务端会将此Map参数原封不动传给对应解密方法。 input: 待加密原始数据。 返回值:加密后数据。

public byte[] decode(Map<String, String> header, byte[] input);

参数: header: 由加密方传入的加密额外信息,解密实现方需要通过这些信息决定解密算法,模式等细节。 input: 待解密原始数据。 返回值:解密后数据。

6.2 自定义编解码接口注册函数

QtConfigure.registerCryptoProvider(Context appContext,  CryptoProvider customProvider);

接口方法

参数说明

registerCryptoProvider

参数: appContext: 应用applicationContext上下文对象。 customProvider: 用户自行实现的CryptoProvider接口对象。

6.3 示例代码

import com.quick.qt.commonsdk.sm.CryptoProvider;
import com.quick.qt.commonsdk.QtConfigure;

public class MyApplication extends Application {
    @Override
    public void onCreate() {
        super.onCreate();
        
        QtConfigure.setCustomDomain("您的收数服务域名", null);

        QtConfigure.setLogEnabled(true); // 打开SDK调试日志

        QtConfigure.preInit(this, "您的appkey", "您的渠道");

	  // 开发者自行实现的加密、解密接口
        CryptoProvider customProvider = new CryptoProvider() {
            final String key = "just_for_test";
            	public byte[] customEncode(final byte[] data, final byte[] key) {
            	    // 开发者自行实现
            	    return null;
            	}
            	public byte[] customDecode(final byte[] data, final byte[] key) {
            	    // 需开发者自行实现
            	    return null;
            	}
            @Override
            public byte[] encode(Map<String, String> header, final byte[] input) {
                byte[] result = null;
                try {
                    if (header != null) {
                        // 示例
                        header.put("arg1", "value1");
                        header.put("arg2", "value2");
                    }
                    if (input == null) {
                        // 如果传入的待加密内容为null,需要打印相关错误日志,方便联调
                        return result;
                    }
                    result = customEncode(input, key.getBytes()); // customEncode需要用户自行实现
                } catch (Exception e) {
                    Log.e("customProvider:encode: ", "exception: " + e.getMessage());
                }
                return result;
            }

            @Override
            public byte[] decode(Map<String, String> header, final byte[] input) {
                byte[] result = null;
                try {
                    if (header != null && header.size() > 0) {
                        Log.i("customProvider", "K-V from encode call.");
                        for (String key : header.keySet()) {
                            Log.i("customProvider", "decode: " + key + " = " + header.get(key));
                        }
                    }
                    result = customDecode(input, key.getBytes());
                } catch (Exception e) {
                    Log.e("customProvider:decode: ", "exception: " + e.getMessage());
                }
                return result;
            }
        };


	  // 注册自定义CryptoProvider接口对象
        final Context appContext = this.getApplicationContext();
        QtConfigure.registerCryptoProvider(appContext,  customProvider);

        // 如果终端用户已经同意隐私授权,再调用SDK正式初始化函数。
        // 注意:如果是宿主应用安装后的首次冷启动,QtConfigure.init正式初始化函数必须延迟到终端用户点击隐私授权对话框的“同意”按钮之后才能被调用。否则会导致隐私合规问题。
	    if (privacyAuthorizationComplete(appContext)) {
	  	   QtConfigure.init(appContext, "您的appkey", "您的渠道", QtConfigure.DEVICE_TYPE_PHONE, null);
	    }
        
        // ...
       
    }
}