《插件自助开发——统计类插件》

统计系统

开发者可以准确跟踪用户每次的应用使用,准确统计启动、活跃、留存数据。本文介绍了如何接入 SDK 的统计服务。

构造函数和初始化

统计系统的类一般命名为“Analytics+插件名”,构造函数参数为 Context,是主 ActivityContext。在构造函数中实现初始化 SDK 等。

public class AnalyticsQH360 implements InterfaceAnalytics
{
    protected static String LOG_TAG = "AnalyticsQH360";
    private static final String SDK_VERSION = "1.1.6";
    private static final String PLUGIN_VERSION = "2.1.8_"+SDK_VERSION;
    private Context mContext = null;

    public AnalyticsQH360(Context context)
    {
        mContext = context;
        configDeveloperInfo(PluginHelper.getParamsInfo());
    }

    private void configDeveloperInfo(final Hashtable<String, String> cpInfo)
    {
        final Hashtable<String, String> curCPInfo = cpInfo;
        PluginWrapper.runOnMainThread(new Runnable()
        {
            @Override
            public void run()
            {
                QHStatDo.init(mContext);
            }
        });
    }
}

以 360 统计为例,统计类 AnalyticsQH360 实现了 InterfaceAnalytics 接口。
AnySDK 框架可以通过 AnalyticsQH360(Context context) 方法实例化统计类。
开发者需要在 configDeveloperInfo(Hashtable<String, String> cpInfo) 方法中实现统计 SDK 的初始化操作。

InterfaceAnalytics

统计类插件需要实现接口 InterfaceAnalytics,接口中没有的方法,开发者可以自定义拓展。在 InterfaceAnalytics 接口中,有如下函数:

public void startSession();     //开始跟踪用户使用中的打开应用和页面跳转的数据
public void stopSession();      //结束跟踪用户使用中的打开应用和页面跳转的数据
public void setSessionContinueMillis(int millis);   //会话时间间隔
public void setCaptureUncaughtException(boolean isEnabled);     //设置SDK自动获取异常
public void logError(String errorId, String message);       //手动记录错误日志
public void logEvent(String eventId);       //自定义事件
public void logEvent(String eventId, Hashtable<String, String> paramMap);   //自定义事件
public void logTimedEventBegin(String eventId);     //事件开始
public void logTimedEventEnd(String eventId);       //事件结束
public void setDebugMode(boolean paramBoolean);     // 设置调试模式(已废弃)
public String getSDKVersion();      //获取SDK 版本号
public String getPluginVersion();   //获取插件版本号

Session 统计

startSession

public void startSession();
功能说明:在应用的每个 ActivityonResume 方法里调用 startSession 用于跟踪用户使用中的打开应用和页面跳转的数据。
举例说明:以 DataEye 为例,在 startSession 中调用 DataEye 的 onResume 方法。

@Override
public void startSession() {
    LogD("startSession() invoked!");
    DCAgent.onResume((Activity) mContext);
}

stopSession

public void stopSession();
功能说明: 在应用的每个 Activity 中的 onPause 方法里调用 stopSession,用于跟踪用户离开页面和退出应用的数据。
举例说明: 以 DataEye 为例,在 stopSession 中调用 DataEye 的 onPause 方法。

@Override
public void stopSession() {
    LogD("stopSession() invoked!");
    DCAgent.onPause((Activity) mContext);
}

setSessionContinueMillis

public void setSessionContinueMillis();
功能说明: 当用户两次使用之间过长时,将被认为是两个的独立的 Session(启动),例如用户回到 Home,或进入其他程序,经过一段时间后再返回之前的应用。可通过接口:setSessionContinueMillis(long interval) 来设置这个间隔(参数单位为毫秒)。
举例说明: 以 Flurry 为例,在 setSessionContinueMillis 中调用 Flurry 的 setContinueSessionMillis 方法。

@Override
public void setSessionContinueMillis(int millis) {
    LogD("setSessionContinueMillis invoked!");
    final int curMillis = millis;
    PluginWrapper.runOnMainThread(new Runnable() {
        @Override
        public void run() {
            FlurryAgent.setContinueSessionMillis(curMillis);
        }
    });
}

收集应用错误日志

setCaptureUncaughtException

public void setCaptureUncaughtException();
功能说明: SDK 自动获取异常信息, 向 setCaptureUncaughtException 方法中传入 true(开启自动捕获)或 false(关闭自动捕获), 来控制自动捕获功能的开关。
举例说明: 以 Flurry 为例,在 setCaptureUncaughtException 中调用 Flurry 的 setCaptureUncaughtExceptions 方法。

@Override
public void setCaptureUncaughtException(boolean isEnabled) {
    LogD("setCaptureUncaughtException invoked!");
    final boolean curEnable = isEnabled;
    PluginWrapper.runOnMainThread(new Runnable() {
        @Override
        public void run() {
            FlurryAgent.setCaptureUncaughtExceptions(curEnable);
        }
    });
}

logError

public void logError(String errorId, String message);
功能说明: 手动统计应用错误, errorId 为错误事件 ID, message 为错误事件内容。
举例说明: 以 Flurry 为例,在 logError 中调用 Flurry 的onError` 方法。

@Override
public void logError(String errorId, String message) {
    LogD("logError invoked!");
    final String curID = errorId;
    final String curMsg = message;
    PluginWrapper.runOnMainThread(new Runnable() {
        @Override
        public void run() {
            FlurryAgent.onError(curID, curMsg, "");
        }
    });
}

自定义事件统计

logEvent

public void logEvent(String eventId);
public void logEvent(String eventId, Hashtable paramMap);
功能说明:开启会话统计,用于跟踪用户离开页面和退出应用的数据。eventId 为自定义事件 ID,paramMap 为事件添加详尽的描述信息,可以更有效的对事件触发的条件和场景做分析。
举例说明:以 Flurry 为例,在 logEvent 中调用 Flurry 的 logEvent 方法。

@Override
public void logEvent(String eventId) {
    LogD("logEvent(eventId) invoked!");
    final String curId = eventId;
    PluginWrapper.runOnMainThread(new Runnable() {
        @Override
        public void run() {
            FlurryAgent.logEvent(curId);
        }
    });
}

@Override
public void logEvent(String eventId, Hashtable<String, String> paramMap) {
    LogD("logEvent(eventId, paramMap) invoked!");
    final String curId = eventId;
    final Hashtable<String, String> curParam = paramMap;
    PluginWrapper.runOnMainThread(new Runnable() {
        @Override
        public void run() {
            FlurryAgent.logEvent(curId, curParam);
        }
    });
}

logTimedEventBegin

public void logTimedEventBegin(String eventId);
功能说明:事件开始时统计,eventId 为定义事件 ID
举例说明:以 DataEye 为例,在 logTimedEventBegin 中调用 DateEye 的 onEventBegin 方法。

@Override
public void logTimedEventBegin(String eventId) {
    LogD("logTimedEventBegin(" + eventId.toString() + ") invoked!");
    DCEvent.onEventBegin(eventId);
}

logTimedEventEnd

public void logTimedEventEnd(String eventId);
功能说明:事件结束时统计,eventId 为定义事件 ID
举例说明:以 DataEye 为例,在 logTimedEventEnd 中调用 DateEye 的 onEventEnd 方法。

@Override
public void logTimedEventEnd(String eventId) {
    LogD("logTimedEventEnd(" + eventId.toString() + ") invoked!");
    DCEvent.onEventEnd(eventId);
}

设置调试模式

public void setDebugMode(boolean paramBoolean);
已废弃,实现方式参照模板。

获取SDK版本号

public String getSDKVersion();
SDK 版本是指第三方 SDK 的版本号,格式可能都不一样,如 6.0、1.0.3、2.1.0.4 等,这个版本号格式不需要任何改动。

获取插件版本号

public String getPluginVersion();
格式『版本序号_SDK版本号』,版本序号从『2.0.0』开始,每次更新+0.0.1

拓展接口

统计玩家账户信息

setAccount

public void setAccount(JSONObject info);
功能说明:定义一个玩家单元,更新玩家最新的属性信息。
JSONObject info 参数如下

参数 是否必传 参数说明
Account_Id Y 游戏中玩家 ID
Account_Name Y 游戏中玩家昵称
Account_Type Y 传入帐户的类型(ANONYMOUS匿名,REGISTED自有账号,SINA_WEIBO新浪微博,TENCENT_WEIBO腾讯微博,QQ ,ND91)
Account_Level Y 游戏中玩家等级
Account_Age Y 玩家年龄
Account_Operate Y 账户操作(LOGIN登陆,LOGOUT登出,REGISTER注册)
Account_Gender Y 游戏角色性别(UNKNOWN未知,FEMALE女性,MALE男性)
Server_Id Y 服务器 ID,若无填“1”

举例说明:以 DataEye 为例,setAccount 接入 DataEye 相关方法收集玩家信息,如玩家 ID,玩家昵称,账户类型等信息。

public void setAccount(JSONObject info){
    LogD("setAccount(" + info.toString() + ") invoked!");
    try {
        if (!info.isNull("Account_Operate")) {
            String operate = info.getString("Account_Operate");
            String id = info.getString("Account_Id");
            switch (Integer.parseInt(operate))
            {
            case AnalyticsWrapper.ACCOUNT_OPERATE_LOGIN:
                DCAccount.login(id);
                break;
            case AnalyticsWrapper.ACCOUNT_OPERATE_LOGOUT:
                DCAccount.logout();
                break;
            default:
                break;
            }
        }
        if (!info.isNull("Account_Age")) {
            String age = info.getString("Account_Age");
            DCAccount.setAge(Integer.parseInt(age));
        }
        if (!info.isNull("Server_Id")) {
            String serverId = info.getString("Server_Id");
            DCAccount.setGameServer(serverId);
        }
        if (!info.isNull("Account_Level")) {
            String level = info.getString("Account_Level");
            DCAccount.setLevel(Integer.parseInt(level));
        }
        if (!info.isNull("Account_Gender")) {
            String gender = info.getString("Account_Gender");
            switch (Integer.parseInt(gender))
            {
            case AnalyticsWrapper.ACCOUNT_GENDER_MALE:
                DCAccount.setGender(DCGender.DC_MALE);
                break;
            case AnalyticsWrapper.ACCOUNT_GENDER_FEMALE:
                DCAccount.setGender(DCGender.DC_FEMALE);
                break;
            case AnalyticsWrapper.ACCOUNT_GENDER_UNKNOWN:
                DCAccount.setGender(DCGender.DC_UNKNOWN);
                break;
            default:
                break;
            }
        }
        if (!info.isNull("Account_Type")) {
            String type = info.getString("Account_Type");
            switch (Integer.parseInt(type))
            {
            case AnalyticsWrapper.ACCOUNT_TYPE_ANONYMOUS:
                DCAccount.setAccountType(DCAccountType.DC_Anonymous);
                break;
            case AnalyticsWrapper.ACCOUNT_TYPE_ND91:
                DCAccount.setAccountType(DCAccountType.DC_ND91);
                break;
            case AnalyticsWrapper.ACCOUNT_TYPE_QQ:
                DCAccount.setAccountType(DCAccountType.DC_QQ);
                break;
            case AnalyticsWrapper.ACCOUNT_TYPE_QQ_WEIBO:
                DCAccount.setAccountType(DCAccountType.DC_QQWeibo);
                break;
            case AnalyticsWrapper.ACCOUNT_TYPE_REGISTED:
                DCAccount.setAccountType(DCAccountType.DC_Registered);
                break;
            case AnalyticsWrapper.ACCOUNT_TYPE_SINA_WEIBO:
                DCAccount.setAccountType(DCAccountType.DC_SinaWeibo);
                break;
            default:
                DCAccount.setAccountType(Integer.parseInt(type));
                break;
            }
        }

    } catch (Exception e) {
        LogE("setAccount error", e);
    }
}

跟踪玩家充值

跟踪玩家充值现金而获得虚拟币的行为,充入的现金将反映至游戏收入中。
充值过程分两个跟踪阶段:1、发出有效的充值请求;2、确认某次充值请求是否完成充值。

onChargeRequest

public void onChargeRequest(JSONObject info)
功能说明:在玩家发起充值请求时(例如玩家选择了某个充值包,进入支付流程那一刻)调用 onChargeRequest,并传入该笔交易的唯一订单 ID 和详细信息;
JSONObject info 参数如下

参数 是否必传 参数说明
Order_Id Y 订单 ID
Product_Name Y 商品名
Currency_Amount Y 现金数额(元)
Currency_Type Y 请使用国际标准组织 ISO 4217 中规范的 3 位字母代码标记货币类型。点击查看参考例:人民币 CNY;美元 USD;欧元 EUR
Payment_Type Y 支付的途径,最多 16 个字符。例如:“支付宝”“苹果官方”“XX 支付 SDK”
Virtual_Currency_Amount Y 虚拟币数值

举例说明:以 TalkingGame 为例,onChargeRequest 接入 TalkingGame 的 onChargeRequest 方法统计充值信息。

public void onChargeRequest(JSONObject info) {
    LogD("onChargeRequest("+info.toString() + ")invoked!");
    try {
        String orderId = info.getString("Order_Id");
        String iapId = info.getString("Product_Name");
        double currencyAmount = Double.valueOf(info.getString("Currency_Amount"));
        String currencyType = info.getString("Currency_Type");
        double virtualCurrencyAmount = Double.valueOf(info.getString("Virtual_Currency_Amount"));
        String paymentType = info.getString("Payment_Type");
        TDGAVirtualCurrency.onChargeRequest(orderId, iapId, currencyAmount,currencyType, virtualCurrencyAmount, paymentType);
    } catch (Exception e) {
        LogE("onChargeRequest error",e);
    }
}

onChargeSuccess

public void onChargeSuccess(String orderId)
功能说明:在确认玩家支付成功时调用 onChargeSuccess,并告知完成的是哪个订单 ID;
举例说明:以 TalkingGame 为例,onChargeSuccess接入 TalkingGame 的 onChargeSuccess 方法记录充值成功信息。

public void onChargeSuccess(String orderId) {
    LogD("onChargeSuccess("+orderId+")invoked!");
    TDGAVirtualCurrency.onChargeSuccess(orderId);
}

onChargeFail

public void onChargeFail(JSONObject info)
功能说明:在确认玩家支付失败时调用 onChargeFail,并告知完成的是哪个订单 ID 和失败原因。
JSONObject info 参数如下

参数 是否必传 参数说明
Order_Id Y 订单 ID
Fail_Reason Y 失败原因

举例说明:以 BI 为例,onChargeFail 接入 BI 的 onChargeFail 方法记录充值失败信息。

public void onChargeFail(JSONObject info){
    LogD("onChargeFail("+info.toString()+")invoked!");
    String orderId = info.getString("Order_Id");
    String failReason = info.getString("Fail_Reason");
    if (orderId == null || failReason == null) {
        LogE("something is null", null);
        return;
    }
    CCPayment.onChargeFailed(orderId, failReason);
}

onChargeOnlySuccess

public void onChargeOnlySuccess(JSONObject info)
功能说明:如果游戏中无法记录充值的发起,那么可以在某次充值成功后,调用 onChargeOnlySuccess 方法,来告知一次充值成功的相关信息。
JSONObject info 参数如下

参数 是否必传 参数说明
Order_Id Y 订单 ID
Product_Name Y 商品名
Currency_Amount Y 现金数额(元)
Currency_Type Y 请使用国际标准组织 ISO 4217 中规范的 3 位字母代码标记货币类型。点击查看参考例:人民币 CNY;美元 USD;欧元 EUR
Payment_Type Y 支付的途径,最多 16 个字符。例如:“支付宝”“苹果官方”“XX 支付 SDK”
Virtual_Currency_Amount Y 虚拟币数值

举例说明:以 DateEye 为例,onChargeOnlySuccess 接入 DateEye 的相关方法记录充值信息。

public void onChargeOnlySuccess(JSONObject info){
    LogD("onChargeOnlySuccess(" + info.toString() + ") invoked!");
    try {
        String orderId = info.getString("Order_Id");
        String currencyAmount = info.getString("Currency_Amount");
        String currencyType = info.getString("Currency_Type");
        String paymentType = info.getString("Payment_Type");
        String iapId = "";
        if (!info.isNull("Iap_Id")) {
            iapId = info.getString("Iap_Id");
        }
        if (info.isNull("Level_Id")) {
            DCVirtualCurrency.paymentSuccess(orderId, iapId, Double.valueOf(currencyAmount), currencyType, paymentType);
        } else {
            String levelId = info.getString("Level_Id");
            DCVirtualCurrency.paymentSuccessInLevel(orderId, iapId, Double.valueOf(currencyAmount), currencyType, paymentType, levelId);
        }
    } catch (Exception e) {
        LogE("onChargeOnlySuccess error", e);
    }
}

跟踪游戏内消费

跟踪游戏中全部使用到虚拟币的消费点,如购买虚拟道具、VIP 服务、复活等;跟踪某物品或服务的耗尽;

onPurchase

public void onPurchase(JSONObject info)
功能说明:在任意消费点发生时尽快调用 onPurchase
JSONObject info 参数如下

参数 是否必传 参数说明
Item_Id Y 物品 ID
Item_Type Y 物品类型
Item_Count Y 物品数量
Virtual_Currency Y 虚拟金额
Currency_Type Y 支付途径(可用来区分渠道消费)

举例说明:以 DateEye 为例,onPurchase 接入 DateEye 的相关统计道具购买信息。

public void onPurchase(JSONObject info) {
    LogD("onPurchase(" + info.toString() + ") invoked!");
    try {
        String itemId = info.getString("Item_Id");
        String itemType = info.getString("Item_Type");
        String itemCnt = info.getString("Item_Count");
        String virtualCurrency = info.getString("Virtual_Currency");
        String currencyType = info.getString("Currency_Type");
        String consumePoint = "";
        if (!info.isNull("Consume_Point")) {
            consumePoint = info.getString("Consume_Point");
        }
        if (info.isNull("Level_Id")) {
            DCItem.buy(itemId, itemType, Integer.valueOf(itemCnt), Long.parseLong(virtualCurrency), currencyType, consumePoint);
        } else {
            String levelId = info.getString("Level_Id");
            DCItem.buyInLevel(itemId, itemType, Integer.valueOf(itemCnt), Long.parseLong(virtualCurrency), currencyType, consumePoint, levelId);
        }
    } catch (Exception e) {
        LogE("onPurchase error", e);
    }
}

onUse

public void onUse(JSONObject info)
功能说明:在某个道具/服务被用掉(消失)时尽快调用 onUse
JSONObject info 参数如下

参数 是否必传 参数说明
Item_Id Y 物品 ID
Item_Type Y 物品类型
Item_Count Y 物品数量
Use_Reason Y 用途说明

举例说明:以 DateEye 为例,onUse 接入 DateEye 的相关统计道具使用信息。

public void onUse(JSONObject info) {
    LogD("onUse(" + info.toString() + ") invoked!");
    try {
        String itemId = info.getString("Item_Id");
        String itemType = info.getString("Item_Type");
        String itemCnt = info.getString("Item_Count");
        String reason = info.getString("Use_Reason");
        if (info.isNull("Level_Id")) {
        DCItem.consume(itemId, itemType, Integer.valueOf(itemCnt), reason);
        } else {
            String levelId = info.getString("Level_Id");
            DCItem.consumeInLevel(itemId, itemType, Integer.valueOf(itemCnt), reason, levelId);
        }
    } catch (Exception e) {
        LogE("onUse error", e);
    }
}

onReward

public void onReward(JSONObject info)
功能说明:在成功向玩家赠予虚拟币时调用 onReward 方法来传入相关数据
JSONObject info 参数如下

参数 是否必传 参数说明
Item_Id Y 物品 ID
Item_Type Y 物品类型
Item_Count Y 物品数量
Use_Reason Y 用途说明

举例说明:以 DateEye 为例,onReward 接入 DateEye 的相关统计道具赠予信息。

public void onReward(JSONObject info) {
    LogD("onReward(" + info.toString() + ") invoked!");
    try {
        String itemId = info.getString("Item_Id");
        String itemType = info.getString("Item_Type");
        String itemCnt = info.getString("Item_Count");
        String reason = info.getString("Use_Reason");
        if (info.isNull("Level_Id")) {
            DCItem.get(itemId, itemType, Integer.valueOf(itemCnt), reason);
        } else {
            String levelId = info.getString("Level_Id");
            DCItem.getInLevel(itemId, itemType, Integer.valueOf(itemCnt), reason, levelId);
        }
    } catch (Exception e) {
        LogE("onReward error", e);
    }
}

关卡

我们提供了一组方法来统计关卡相关的事件。startLevelfailLevelfinishLevel,其中 startLevelfailLevel/finishLevel 方法需要配对出现。

startLevel

public void startLevel(JSONObject info)
功能说明:在游戏开启新的关卡的时候调用 startLevel 方法
JSONObject info 参数如下

参数 是否必传 参数说明
Level_Id Y 关卡 ID
Seq_Num Y 关卡顺序

举例说明:以 DateEye 为例,startLevel 接入 DateEye 的关卡开始接口。

public void startLevel(JSONObject info){
    LogD("startLevel(" + info.toString() + ") invoked!");
    try {
        String levelId = info.getString("Level_Id");
        DCLevels.begin(levelId);
    } catch (Exception e) {
        LogE("startLevel error", e);
    }
}

finishLevel

public void finishLevel(String levelId)
功能说明:在成功过关的时候调用 finishLevel 方法
举例说明:以 DateEye 为例,finishLevel 接入 DateEye 的关卡完成接口。

public void finishLevel(String levelId){
    LogD("finishLevel(" + levelId.toString() + ") invoked!");
    try {
        DCLevels.complete(levelId);
    } catch (Exception e) {
        LogE("finishLevel error", e);
    }
}   

failLevel

public void failLevel(JSONObject info)
功能说明:在关卡失败的时候调用 failLevel 方法
JSONObject info 参数如下

参数 是否必传 参数说明
Level_Id Y 关卡 ID
Fail_Reason Y 失败原因

举例说明:以 DateEye 为例,failLevel 接入 DateEye 的关卡失败接口。

public void failLevel(JSONObject info){
    LogD("failLevel(" + info.toString() + ") invoked!");
    try {
        String levelId = info.getString("Level_Id");
        String reason = info.getString("Fail_Reason");
        DCLevels.fail(levelId, reason);
    } catch (Exception e) {
        LogE("failLevel error", e);
    }
}

任务

我们提供了一组方法来统计任务相关的事件。 startTaskfailTaskfinishTask,其中 startTaskfailTask/finishTask 方法需要配对出现。

startTask

public void startTask(JSONObject info)
功能说明:在游戏开启新的任务的时候调用 startTask 方法
JSONObject info 参数如下

参数 是否必传 参数说明
Task_Id Y 任务 ID
Task_Type Y 任务类型(GUIDE_LINE新手引导、MAIN_LINE主线、 BRANCH_LINE分支、 DAILY日常、 ACTIVITY活动、 OTHER其他)

举例说明:以 DateEye 为例,startTask 接入 DateEye 的任务开始接口。

public void startTask(JSONObject info){
    LogD("startTask(" + info.toString() + ") invoked!");
    try {
    String taskId = info.getString("Task_Id");
        String taskType = info.getString("Task_Type");
        DCTask.begin(taskId, Integer.parseInt(taskType) + 1);
    } catch (Exception e) {
        LogE("startTask error", e);
    }
}

finishTask

public void finishTask(String TaskId)
功能说明:在成功完成任务的时候调用 finishTask 方法
举例说明:以 DateEye 为例,finishTask 接入 DateEye 的任务完成接口。

public void finishTask(String taskId){
    LogD("finishTask(" + taskId.toString() + ") invoked!");
    try {
        DCTask.complete(taskId);
    } catch (Exception e) {
        LogE("finishTask error", e);
    }
}

failTask

public void failTask(JSONObject info)
功能说明:在任务失败的时候调用 failTask 方法
JSONObject info 参数如下

参数 是否必传 参数说明
Task_Id Y 任务 ID
Fail_Reason Y 失败原因

举例说明:以 DateEye 为例,failTask 接入 DateEye 的任务失败接口。

public void failTask(JSONObject info){
    LogD("failTask(" + info.toString() + ") invoked!");
    try {
        String taskId = info.getString("Task_Id");
        String reason = info.getString("Fail_Reason");
        DCTask.fail(taskId, reason);
    } catch (Exception e) {
        LogE("finishTask error", e);
    }
}

【评论区】