keytops-game-framework 1.0.26 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/app/module/analytics/AnalyticsAble.d.ts +26 -1
- package/dist/index.js +1899 -1838
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -881,1155 +881,1492 @@ Storage.created = false;
|
|
|
881
881
|
*/
|
|
882
882
|
const storage = new Storage();
|
|
883
883
|
|
|
884
|
-
var InternalProperty;
|
|
885
|
-
(function (InternalProperty) {
|
|
886
|
-
/**设备系统版本 */
|
|
887
|
-
InternalProperty["DEVICE_SYSTEM"] = "_ds_";
|
|
888
|
-
/**设备操作系统类型 */
|
|
889
|
-
InternalProperty["DEVICE_PLATFORM"] = "_dp_";
|
|
890
|
-
/**设备手机品牌 */
|
|
891
|
-
InternalProperty["DEVICE_BRAND"] = "_db_";
|
|
892
|
-
/**设备手机型号 */
|
|
893
|
-
InternalProperty["DEVICE_MODEL"] = "_dm_";
|
|
894
|
-
/**宿主名称 */
|
|
895
|
-
InternalProperty["HOST_NAME"] = "_hn_";
|
|
896
|
-
/**宿主版本 */
|
|
897
|
-
InternalProperty["HOST_VERSION"] = "_hv_";
|
|
898
|
-
/**启动时间 */
|
|
899
|
-
InternalProperty["LAUNCH_TIME"] = "_lt_";
|
|
900
|
-
/**启动次数 */
|
|
901
|
-
InternalProperty["LAUNCH_TIMES"] = "_lts_";
|
|
902
|
-
/**安装时间 */
|
|
903
|
-
InternalProperty["INSTALL_TIME"] = "_it_";
|
|
904
|
-
/**app运载(安装)时长 */
|
|
905
|
-
InternalProperty["DURATION_OF_INSTALL"] = "_doi_";
|
|
906
|
-
/**app运行(启动)时长 */
|
|
907
|
-
InternalProperty["DURATION_OF_LAUNCH"] = "_dol_";
|
|
908
|
-
/**累积登录天数 */
|
|
909
|
-
InternalProperty["LOGIN_DAYS"] = "_lds_";
|
|
910
|
-
/**安装来源type */
|
|
911
|
-
InternalProperty["INSTALL_SCENE"] = "_is_";
|
|
912
|
-
/**安装来源source */
|
|
913
|
-
InternalProperty["INSTALL_CHANNEL"] = "_ic_";
|
|
914
|
-
/**启动来源场景 */
|
|
915
|
-
InternalProperty["LAUNCH_SCENE"] = "_ls_";
|
|
916
|
-
/**启动来源渠道 */
|
|
917
|
-
InternalProperty["LAUNCH_CHANNEL"] = "_lc_";
|
|
918
|
-
/**事件计时时长 */
|
|
919
|
-
InternalProperty["DURATION"] = "_dt_";
|
|
920
|
-
/**游戏版本 */
|
|
921
|
-
InternalProperty["APP_VERSION"] = "_av_";
|
|
922
|
-
})(InternalProperty || (InternalProperty = {}));
|
|
923
|
-
class EmptyAnalyticsSender {
|
|
924
|
-
/**
|
|
925
|
-
* @param [exclude] 其他排除的上报日志, 默认不排除
|
|
926
|
-
*/
|
|
927
|
-
constructor(exclude) {
|
|
928
|
-
this._exclude = exclude || [];
|
|
929
|
-
}
|
|
930
|
-
/**
|
|
931
|
-
* 设置排除
|
|
932
|
-
* @param excludeList 排除事件名称或者名称列表。
|
|
933
|
-
*/
|
|
934
|
-
exclude(...excludeList) {
|
|
935
|
-
this._exclude = excludeList.concat();
|
|
936
|
-
}
|
|
937
|
-
sendAble(eventName) {
|
|
938
|
-
if (this._exclude && this._exclude.indexOf(eventName) != -1) {
|
|
939
|
-
return false;
|
|
940
|
-
}
|
|
941
|
-
return true;
|
|
942
|
-
}
|
|
943
|
-
send(eventName, data) {
|
|
944
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
945
|
-
});
|
|
946
|
-
}
|
|
947
|
-
}
|
|
948
884
|
/**
|
|
949
|
-
*
|
|
885
|
+
* 通用计时器实现。
|
|
886
|
+
* 使用 setInterval 驱动 Timer 的内部更新循环。
|
|
950
887
|
*/
|
|
951
|
-
class
|
|
888
|
+
class TimerImpl {
|
|
952
889
|
constructor() {
|
|
953
|
-
this.
|
|
954
|
-
this._hasSetDefaultProperty = false;
|
|
955
|
-
this._debugMode = false;
|
|
956
|
-
this._senderList = [];
|
|
957
|
-
this._dynamicCalls = [];
|
|
890
|
+
this._intervalMap = new WeakMap();
|
|
958
891
|
}
|
|
959
|
-
|
|
892
|
+
enable(timer) {
|
|
893
|
+
if (this._intervalMap.has(timer)) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
const intervalId = setInterval(() => {
|
|
897
|
+
timer["_update"]();
|
|
898
|
+
}, 100);
|
|
899
|
+
this._intervalMap.set(timer, intervalId);
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
|
|
903
|
+
class MathUtils {
|
|
960
904
|
/**
|
|
961
|
-
*
|
|
905
|
+
* 随机
|
|
906
|
+
* @param min
|
|
907
|
+
* @param max
|
|
962
908
|
*/
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
App.onShow(this._onAppShow, this);
|
|
968
|
-
App.onHide(this._onAppHide, this);
|
|
969
|
-
this._initSenderList(senders);
|
|
970
|
-
this._inited = true;
|
|
971
|
-
return this;
|
|
909
|
+
static random(min, max) {
|
|
910
|
+
this.seed = (this.seed * 9301 + 49297) % 233280;
|
|
911
|
+
let c = this.seed / (233280.0);
|
|
912
|
+
return min + c * (max - min);
|
|
972
913
|
}
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
914
|
+
/**
|
|
915
|
+
* 随机整数值
|
|
916
|
+
* @param min
|
|
917
|
+
* @param max
|
|
918
|
+
*/
|
|
919
|
+
static randomInt(min, max) {
|
|
920
|
+
return Math.floor(this.random(min, max + 0.999));
|
|
978
921
|
}
|
|
979
922
|
/**
|
|
980
|
-
*
|
|
981
|
-
* @param
|
|
982
|
-
* @param
|
|
923
|
+
* 随机唯一稳定值
|
|
924
|
+
* @param min
|
|
925
|
+
* @param max
|
|
926
|
+
* @param clear 是否清除唯一记录
|
|
983
927
|
*/
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
928
|
+
static randomOnlyInt(min, max, clear) {
|
|
929
|
+
if (clear) {
|
|
930
|
+
this.onlyMap.clear();
|
|
931
|
+
}
|
|
932
|
+
let value = this.randomInt(min, max);
|
|
933
|
+
let tryCount = (max - min) * 2;
|
|
934
|
+
let tryIndex = 0;
|
|
935
|
+
while (this.onlyMap.has(value)) {
|
|
936
|
+
value = this.randomInt(min, max);
|
|
937
|
+
if (tryIndex > tryCount) {
|
|
938
|
+
console.error("所有唯一值已取完");
|
|
939
|
+
return 0;
|
|
994
940
|
}
|
|
941
|
+
tryIndex++;
|
|
995
942
|
}
|
|
943
|
+
this.onlyMap.set(value, true);
|
|
944
|
+
return value;
|
|
996
945
|
}
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
946
|
+
/**
|
|
947
|
+
* 获取两点间的角度
|
|
948
|
+
* @param p1 点1
|
|
949
|
+
* @param p2 点2
|
|
950
|
+
*/
|
|
951
|
+
static getAngle(p1, p2) {
|
|
952
|
+
return Math.atan((p2.y - p1.y) / (p2.x - p1.x));
|
|
1000
953
|
}
|
|
1001
|
-
|
|
1002
|
-
|
|
954
|
+
/**
|
|
955
|
+
* 获取两点间的距离
|
|
956
|
+
* @param p1 点1
|
|
957
|
+
* @param p2 点2
|
|
958
|
+
*/
|
|
959
|
+
static getDistance(p1, p2) {
|
|
960
|
+
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
|
|
1003
961
|
}
|
|
1004
962
|
/**
|
|
1005
|
-
*
|
|
963
|
+
* 将角度转为弧度
|
|
964
|
+
* @param angle 角度
|
|
1006
965
|
*/
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
return;
|
|
1010
|
-
}
|
|
1011
|
-
//设备信息
|
|
1012
|
-
let deviceInfo = publisher.device.deviceInfo;
|
|
1013
|
-
this.setProperty(InternalProperty.DEVICE_SYSTEM, deviceInfo.system);
|
|
1014
|
-
this.setProperty(InternalProperty.DEVICE_PLATFORM, deviceInfo.platform);
|
|
1015
|
-
this.setProperty(InternalProperty.DEVICE_BRAND, deviceInfo.brand);
|
|
1016
|
-
this.setProperty(InternalProperty.DEVICE_MODEL, deviceInfo.model);
|
|
1017
|
-
//宿主信息
|
|
1018
|
-
let hostInfo = publisher.device.hostInfo;
|
|
1019
|
-
this.setProperty(InternalProperty.HOST_NAME, hostInfo.name);
|
|
1020
|
-
this.setProperty(InternalProperty.HOST_VERSION, hostInfo.version);
|
|
1021
|
-
//安装时间
|
|
1022
|
-
this.setProperty(InternalProperty.INSTALL_TIME, Math.floor(App.installTime / 1000));
|
|
1023
|
-
//启动时间
|
|
1024
|
-
this.setProperty(InternalProperty.LAUNCH_TIME, Math.floor(App.launchTime / 1000));
|
|
1025
|
-
//启动次数
|
|
1026
|
-
this.setProperty(InternalProperty.LAUNCH_TIMES, App.launchTimes);
|
|
1027
|
-
//游戏版本
|
|
1028
|
-
this.setProperty(InternalProperty.APP_VERSION, publisher.publisher.appVersion);
|
|
1029
|
-
this.setDynamicProperties(() => {
|
|
1030
|
-
let dynamicData = {};
|
|
1031
|
-
//app运载(安装)时长
|
|
1032
|
-
dynamicData[InternalProperty.DURATION_OF_INSTALL] = Math.floor(App.durationOfInstall / 1000);
|
|
1033
|
-
//app运行(启动)时长
|
|
1034
|
-
dynamicData[InternalProperty.DURATION_OF_LAUNCH] = Math.floor(App.durationOfLaunch / 1000);
|
|
1035
|
-
return dynamicData;
|
|
1036
|
-
});
|
|
1037
|
-
//累积登录天数
|
|
1038
|
-
this.userSet(InternalProperty.LOGIN_DAYS, storage.loginDays);
|
|
1039
|
-
//安装来源type
|
|
1040
|
-
this.userSet(InternalProperty.INSTALL_SCENE, publisher.publisher.installOptions.channelScene);
|
|
1041
|
-
//安装来源source
|
|
1042
|
-
this.userSet(InternalProperty.INSTALL_CHANNEL, publisher.publisher.installOptions.channelSource);
|
|
1043
|
-
//启动来源场景
|
|
1044
|
-
this.userSet(InternalProperty.LAUNCH_SCENE, publisher.publisher.enterOptions.channelScene);
|
|
1045
|
-
//启动来源渠道
|
|
1046
|
-
this.userSet(InternalProperty.LAUNCH_CHANNEL, publisher.publisher.enterOptions.channelSource);
|
|
1047
|
-
this._hasSetDefaultProperty = true;
|
|
966
|
+
static angleToRadian(angle) {
|
|
967
|
+
return angle * Math.PI / 180;
|
|
1048
968
|
}
|
|
1049
969
|
/**
|
|
1050
|
-
*
|
|
1051
|
-
* @param
|
|
1052
|
-
* @param
|
|
970
|
+
* 求旋转后的点坐标
|
|
971
|
+
* @param angle 角度
|
|
972
|
+
* @param point 旋转前的坐标点
|
|
973
|
+
* @param result
|
|
1053
974
|
*/
|
|
1054
|
-
|
|
1055
|
-
if (
|
|
1056
|
-
|
|
1057
|
-
}
|
|
1058
|
-
if (typeof value === "boolean") {
|
|
1059
|
-
value = value ? 1 : 0;
|
|
975
|
+
static getRotationPoint(angle, pointOrDir, result) {
|
|
976
|
+
if (result == null) {
|
|
977
|
+
result = { x: 0, y: 0 };
|
|
1060
978
|
}
|
|
1061
|
-
|
|
1062
|
-
|
|
979
|
+
//角度转弧度
|
|
980
|
+
angle = angle * (Math.PI / 180);
|
|
981
|
+
result.x = Math.cos(angle) * pointOrDir.x - Math.sin(angle) * pointOrDir.y;
|
|
982
|
+
result.y = Math.cos(angle) * pointOrDir.y + Math.sin(angle) * pointOrDir.x;
|
|
983
|
+
return result;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
/**
|
|
987
|
+
* 随机种子
|
|
988
|
+
*/
|
|
989
|
+
MathUtils.seed = 10;
|
|
990
|
+
/**
|
|
991
|
+
* 唯一记录
|
|
992
|
+
*/
|
|
993
|
+
MathUtils.onlyMap = new Map();
|
|
994
|
+
|
|
995
|
+
class StringUtils {
|
|
996
|
+
static random(length, chars = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") {
|
|
997
|
+
var result = '';
|
|
998
|
+
for (var i = length; i > 0; --i) {
|
|
999
|
+
result += chars[Math.floor(Math.random() * chars.length)];
|
|
1063
1000
|
}
|
|
1064
|
-
|
|
1001
|
+
return result;
|
|
1065
1002
|
}
|
|
1066
1003
|
/**
|
|
1067
|
-
*
|
|
1068
|
-
* @param
|
|
1004
|
+
* 判断是否是远程资源
|
|
1005
|
+
* @param url
|
|
1069
1006
|
* @returns
|
|
1070
1007
|
*/
|
|
1071
|
-
|
|
1072
|
-
if (
|
|
1073
|
-
return
|
|
1008
|
+
static isRemote(url) {
|
|
1009
|
+
if (url.indexOf("http://") == 0 || url.indexOf("https://") == 0) {
|
|
1010
|
+
return true;
|
|
1074
1011
|
}
|
|
1075
1012
|
return false;
|
|
1076
1013
|
}
|
|
1077
1014
|
/**
|
|
1078
|
-
*
|
|
1079
|
-
* @param
|
|
1015
|
+
* 是否为空
|
|
1016
|
+
* @param str
|
|
1080
1017
|
*/
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1018
|
+
static isEmpty(str) {
|
|
1019
|
+
if (str == null || str == undefined || str.length == 0) {
|
|
1020
|
+
return true;
|
|
1021
|
+
}
|
|
1022
|
+
return false;
|
|
1023
|
+
}
|
|
1024
|
+
/**
|
|
1025
|
+
* 替换全部字符串
|
|
1026
|
+
* @param string src 源串
|
|
1027
|
+
* @param string from_ch 被替换的字符
|
|
1028
|
+
* @param string to_ch 替换的字符
|
|
1029
|
+
*
|
|
1030
|
+
* @return string 结果字符串
|
|
1031
|
+
*/
|
|
1032
|
+
static replaceAll(src, from_ch, to_ch) {
|
|
1033
|
+
let regExp = new RegExp(from_ch, "g");
|
|
1034
|
+
return src.replace(regExp, to_ch);
|
|
1035
|
+
}
|
|
1036
|
+
/**
|
|
1037
|
+
* 参数替换
|
|
1038
|
+
* @param str
|
|
1039
|
+
* @param rest
|
|
1040
|
+
*
|
|
1041
|
+
* @example
|
|
1042
|
+
*
|
|
1043
|
+
* var str:string = "here is some info '{0}' and {1}";
|
|
1044
|
+
* trace(StringUtil.substitute(str, 15.4, true));
|
|
1045
|
+
*
|
|
1046
|
+
* // this will output the following string:
|
|
1047
|
+
* // "here is some info '15.4' and true"
|
|
1096
1048
|
*/
|
|
1097
|
-
|
|
1098
|
-
if (
|
|
1099
|
-
|
|
1049
|
+
static format(str, ...rest) {
|
|
1050
|
+
if (str == null)
|
|
1051
|
+
return '';
|
|
1052
|
+
// Replace all of the parameters in the msg string.
|
|
1053
|
+
var len = rest.length;
|
|
1054
|
+
var args;
|
|
1055
|
+
if (len == 1 && rest[0] instanceof Array) {
|
|
1056
|
+
args = rest[0];
|
|
1057
|
+
len = args.length;
|
|
1100
1058
|
}
|
|
1101
|
-
|
|
1102
|
-
|
|
1059
|
+
else {
|
|
1060
|
+
args = rest;
|
|
1103
1061
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
1062
|
+
for (var i = 0; i < len; i++) {
|
|
1063
|
+
str = str.replace(new RegExp("\\{" + i + "\\}", "g"), args[i]);
|
|
1106
1064
|
}
|
|
1107
|
-
|
|
1065
|
+
return str;
|
|
1108
1066
|
}
|
|
1109
1067
|
/**
|
|
1110
|
-
*
|
|
1111
|
-
*
|
|
1112
|
-
*
|
|
1113
|
-
*
|
|
1114
|
-
* userOnce("first_payment_time", "2018-01-01 01:23:45.678");
|
|
1115
|
-
* //first_payment_time仍然为2018-01-01 01:23:45.678
|
|
1116
|
-
* userOnce("first_payment_time", "2018-12-31 01:23:45.678");
|
|
1117
|
-
* ```
|
|
1118
|
-
* @param key 属性key
|
|
1119
|
-
* @param value
|
|
1068
|
+
*
|
|
1069
|
+
* 格式化数字
|
|
1070
|
+
* @param amount 目标数字
|
|
1071
|
+
* @param format 支持{1:"", 100:"百", 1000:"千", 10000:"万", 100000:"十万", 1000000:"百万", 10000000:"千万", 100000000:"亿", 1000000000000:"兆"}等
|
|
1120
1072
|
* @returns
|
|
1121
1073
|
*/
|
|
1122
|
-
|
|
1123
|
-
if (!
|
|
1124
|
-
|
|
1074
|
+
static formatNumber(amount, format) {
|
|
1075
|
+
if (!format || amount == 0) {
|
|
1076
|
+
return `${amount}`;
|
|
1125
1077
|
}
|
|
1126
|
-
|
|
1127
|
-
|
|
1078
|
+
let formatKey = [];
|
|
1079
|
+
for (const key in format) {
|
|
1080
|
+
if (format.hasOwnProperty(key)) {
|
|
1081
|
+
formatKey.push(Number(key));
|
|
1082
|
+
}
|
|
1128
1083
|
}
|
|
1129
|
-
if (
|
|
1130
|
-
|
|
1131
|
-
return;
|
|
1084
|
+
if (!formatKey || formatKey.length == 0) {
|
|
1085
|
+
return `${amount}`;
|
|
1132
1086
|
}
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1087
|
+
formatKey.sort((a, b) => b - a);
|
|
1088
|
+
let result = [];
|
|
1089
|
+
let total = amount;
|
|
1090
|
+
for (let i = 0; i < formatKey.length; i++) {
|
|
1091
|
+
const key = formatKey[i];
|
|
1092
|
+
let count = Math.floor(Math.abs(total / key)) * (total / Math.abs(total));
|
|
1093
|
+
if (count == 0) {
|
|
1094
|
+
continue;
|
|
1095
|
+
}
|
|
1096
|
+
total = total % key;
|
|
1097
|
+
result.push(`${count}${format[key]}`);
|
|
1138
1098
|
}
|
|
1139
|
-
|
|
1140
|
-
this._userOnceProperties[key] = value;
|
|
1099
|
+
return result.length ? result.join("") : `${amount}`;
|
|
1141
1100
|
}
|
|
1142
1101
|
/**
|
|
1143
|
-
|
|
1144
|
-
*
|
|
1145
|
-
*
|
|
1146
|
-
*
|
|
1147
|
-
*
|
|
1148
|
-
*
|
|
1149
|
-
*
|
|
1150
|
-
*
|
|
1151
|
-
*
|
|
1102
|
+
* 带自动补全的秒数格式。
|
|
1103
|
+
* @param second 秒数
|
|
1104
|
+
* @param format 格式化文本
|
|
1105
|
+
* @param pad 填充文本,默认为“0”
|
|
1106
|
+
* @example
|
|
1107
|
+
* formatSecond(86400, "ddd天hh小时mm分ss秒")=>"001天00小时00分00秒"
|
|
1108
|
+
* formatSecond(86400, "dd天hh小时mm分ss秒")=>"01天00小时00分00秒"
|
|
1109
|
+
* formatSecond(86400, "hh小时mm分ss秒")=>"24小时00分00秒"
|
|
1110
|
+
* formatSecond(86400, "mm分ss秒")=>"1440分00秒"
|
|
1111
|
+
* @returns
|
|
1152
1112
|
*/
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1113
|
+
static formatSecond(second, format, pad = "0") {
|
|
1114
|
+
const formatArr = [
|
|
1115
|
+
{ unit: 86400, regExp: /D{2,}/i },
|
|
1116
|
+
{ unit: 3600, regExp: /H{1,2}/i },
|
|
1117
|
+
{ unit: 60, regExp: /m{1,2}/i },
|
|
1118
|
+
{ unit: 1, regExp: /s{1,2}/i },
|
|
1119
|
+
];
|
|
1120
|
+
for (let i = 0; i < formatArr.length; i++) {
|
|
1121
|
+
const element = formatArr[i];
|
|
1122
|
+
let match = element.regExp.exec(format);
|
|
1123
|
+
if (match) {
|
|
1124
|
+
format = format.replace(element.regExp, Math.floor(second / element.unit).toFixed(0).padStart(match[0].length, pad));
|
|
1125
|
+
second = second % element.unit;
|
|
1126
|
+
}
|
|
1159
1127
|
}
|
|
1160
|
-
|
|
1161
|
-
this._userAddProperties[key] = total + value;
|
|
1162
|
-
storage.setUValue(`analytics_user_add_${key}`, total + value);
|
|
1128
|
+
return format;
|
|
1163
1129
|
}
|
|
1164
1130
|
/**
|
|
1165
|
-
*
|
|
1166
|
-
* @param
|
|
1131
|
+
* 带自动补全的时间戳格式化
|
|
1132
|
+
* @param milliseconds
|
|
1133
|
+
* @param format
|
|
1134
|
+
* @param pad 填充文本,默认为“0”
|
|
1135
|
+
* @returns
|
|
1167
1136
|
*/
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1137
|
+
static formatTimestamp(milliseconds, format, pad = "0") {
|
|
1138
|
+
let date = new Date(milliseconds);
|
|
1139
|
+
date.getHours();
|
|
1140
|
+
const formatArr = [
|
|
1141
|
+
{ r: /Y{4}/, to: date.getFullYear() },
|
|
1142
|
+
{ r: /M{1,2}/, to: date.getMonth() + 1 },
|
|
1143
|
+
{ r: /D{1,2}/, to: date.getDate() },
|
|
1144
|
+
{ r: /H{1,2}/, to: date.getHours() },
|
|
1145
|
+
{ r: /m{1,2}/, to: date.getMinutes() },
|
|
1146
|
+
{ r: /s{1,2}/, to: date.getSeconds() },
|
|
1147
|
+
];
|
|
1148
|
+
for (let i = 0; i < formatArr.length; i++) {
|
|
1149
|
+
const element = formatArr[i];
|
|
1150
|
+
let match = element.r.exec(format);
|
|
1151
|
+
if (match) {
|
|
1152
|
+
format = format.replace(element.r, element.to.toString().padStart(match[0].length, "0"));
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
return format;
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/**
|
|
1160
|
+
* 对象池
|
|
1161
|
+
*/
|
|
1162
|
+
class Pool {
|
|
1163
|
+
constructor(clazz, maxCount) {
|
|
1164
|
+
/**池中闲置对象 */
|
|
1165
|
+
this._cacheStack = new Array();
|
|
1166
|
+
/**正在使用的对象 */
|
|
1167
|
+
this._usingArray = new Array();
|
|
1168
|
+
/**池中对象最大数 */
|
|
1169
|
+
this._maxCount = 0;
|
|
1170
|
+
this._class = clazz;
|
|
1171
|
+
if (!this._class) {
|
|
1172
|
+
throw new Error("构造函数不能为空!");
|
|
1173
|
+
}
|
|
1174
|
+
this._maxCount = maxCount == undefined ? Number.MAX_SAFE_INTEGER : maxCount;
|
|
1174
1175
|
}
|
|
1175
1176
|
/**
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
this.report("app_error", { error: error });
|
|
1177
|
+
* 在池中的对象
|
|
1178
|
+
*/
|
|
1179
|
+
get count() {
|
|
1180
|
+
return this._cacheStack.length;
|
|
1181
1181
|
}
|
|
1182
1182
|
/**
|
|
1183
|
-
*
|
|
1184
|
-
* 如果您需要记录某个事件的持续时长,可以调用 timeEvent 来开始计时。
|
|
1185
|
-
* 配置您想要计时的事件名称,当您上传该事件时,将会自动在您的事件属性中加入 _dt_ 这一属性来表示记录的时长,单位为秒。
|
|
1186
|
-
* 需要注意的是,同一个事件名只能有一个在计时的任务。
|
|
1187
|
-
* @param eventName
|
|
1183
|
+
* 使用中的数量
|
|
1188
1184
|
*/
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
this._debugMode && this._timeEvents.has(eventName) && console.warn(`已经存在${eventName}的计时,将被重置。`);
|
|
1192
|
-
this.cancelTime(eventName);
|
|
1193
|
-
this._timeEvents.set(eventName, { start: App.durationOfLaunch, once: once, offset: offset });
|
|
1185
|
+
get usingCount() {
|
|
1186
|
+
return this._usingArray.length;
|
|
1194
1187
|
}
|
|
1195
1188
|
/**
|
|
1196
|
-
*
|
|
1197
|
-
* @param eventName
|
|
1189
|
+
* 分配
|
|
1198
1190
|
* @returns
|
|
1199
1191
|
*/
|
|
1200
|
-
|
|
1201
|
-
|
|
1192
|
+
allocate(createData) {
|
|
1193
|
+
if (this.count + this.usingCount < this._maxCount) {
|
|
1194
|
+
let element = this._cacheStack.length > 0 ? this._cacheStack.pop() : (typeof createData === 'function' ? createData() : new this._class(createData));
|
|
1195
|
+
this._usingArray.push(element);
|
|
1196
|
+
return element;
|
|
1197
|
+
}
|
|
1198
|
+
throw new Error("对象池最大数量超出:" + this._maxCount);
|
|
1202
1199
|
}
|
|
1203
1200
|
/**
|
|
1204
|
-
*
|
|
1205
|
-
* @param
|
|
1206
|
-
* @
|
|
1201
|
+
* 回收到池中
|
|
1202
|
+
* @param value
|
|
1203
|
+
* @returns
|
|
1207
1204
|
*/
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
}
|
|
1221
|
-
this._setDefaultProperty();
|
|
1222
|
-
for (let i = 0; i < this._dynamicCalls.length; i++) {
|
|
1223
|
-
const call = this._dynamicCalls[i];
|
|
1224
|
-
this._copyToData(eventName, data, call());
|
|
1225
|
-
}
|
|
1226
|
-
this._copyToData(eventName, data, this._userProperties);
|
|
1227
|
-
this._copyToData(eventName, data, this._userAddProperties);
|
|
1228
|
-
this._copyToData(eventName, data, this._userOnceProperties);
|
|
1229
|
-
this._copyToData(eventName, data, this._staticProperties);
|
|
1230
|
-
if (this._timeEvents && this._timeEvents.has(eventName)) {
|
|
1231
|
-
let timeInfo = this._timeEvents.get(eventName);
|
|
1232
|
-
let duration = Math.floor((App.durationOfLaunch - timeInfo.start + timeInfo.offset) / 1000 * 100) / 100;
|
|
1233
|
-
data[InternalProperty.DURATION] = duration;
|
|
1234
|
-
timeInfo.once && this._timeEvents.delete(eventName);
|
|
1235
|
-
}
|
|
1236
|
-
this._debugMode && console.log("[Analytics]", eventName, JSON.stringify(data));
|
|
1237
|
-
return this._sendReport(eventName, data);
|
|
1238
|
-
});
|
|
1205
|
+
recover(value) {
|
|
1206
|
+
if (this._cacheStack.indexOf(value) > -1) {
|
|
1207
|
+
throw new Error("重复回收!");
|
|
1208
|
+
}
|
|
1209
|
+
let index = this._usingArray.indexOf(value);
|
|
1210
|
+
if (index < 0) {
|
|
1211
|
+
throw new Error("对象不属于改对象池!");
|
|
1212
|
+
}
|
|
1213
|
+
//重置
|
|
1214
|
+
value.reset();
|
|
1215
|
+
this._usingArray.splice(index, 1);
|
|
1216
|
+
this._cacheStack.push(value);
|
|
1239
1217
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1218
|
+
/**
|
|
1219
|
+
* 批量回收
|
|
1220
|
+
* @param list
|
|
1221
|
+
*/
|
|
1222
|
+
recoverList(list) {
|
|
1223
|
+
for (let index = 0; index < list.length; index++) {
|
|
1224
|
+
const element = list[index];
|
|
1225
|
+
this.recover(element);
|
|
1246
1226
|
}
|
|
1247
|
-
return false;
|
|
1248
1227
|
}
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
else {
|
|
1257
|
-
console.warn(`[Analytics] 事件${eventName}存在重复属性${key}=${data[key]}`);
|
|
1258
|
-
}
|
|
1259
|
-
}
|
|
1228
|
+
/**
|
|
1229
|
+
* 将所有使用中的对象都回收到池中
|
|
1230
|
+
*/
|
|
1231
|
+
recoverAll() {
|
|
1232
|
+
for (let index = 0; index < this._usingArray.length; index++) {
|
|
1233
|
+
const element = this._usingArray[index];
|
|
1234
|
+
this.recover(element);
|
|
1260
1235
|
}
|
|
1261
1236
|
}
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
}
|
|
1237
|
+
destroy() {
|
|
1238
|
+
this.recoverAll();
|
|
1239
|
+
for (let index = 0; index < this._cacheStack.length; index++) {
|
|
1240
|
+
const element = this._cacheStack[index];
|
|
1241
|
+
element.destroy();
|
|
1242
|
+
}
|
|
1243
|
+
this._cacheStack.length = 0;
|
|
1244
|
+
this._cacheStack = null;
|
|
1245
|
+
this._usingArray.length = 0;
|
|
1246
|
+
this._usingArray = null;
|
|
1268
1247
|
}
|
|
1269
1248
|
}
|
|
1270
1249
|
|
|
1271
|
-
const ALI_ANALYTICS_EVENT_CACHE = "ALI_ANALYTICS_EVENT_CACHE";
|
|
1272
1250
|
/**
|
|
1273
|
-
*
|
|
1251
|
+
* <p><code>Handler</code> 是事件处理器类。</p>
|
|
1252
|
+
* <p>推荐使用 Handler.create() 方法从对象池创建,减少对象创建消耗。创建的 Handler 对象不再使用后,可以使用 Handler.recover() 将其回收到对象池,回收后不要再使用此对象,否则会导致不可预料的错误。</p>
|
|
1253
|
+
* <p><b>注意:</b>由于鼠标事件也用本对象池,不正确的回收及调用,可能会影响鼠标事件的执行。</p>
|
|
1274
1254
|
*/
|
|
1275
|
-
class
|
|
1255
|
+
class Handler {
|
|
1276
1256
|
/**
|
|
1277
|
-
*
|
|
1278
|
-
* @param
|
|
1279
|
-
* @param
|
|
1280
|
-
* @param
|
|
1281
|
-
* @param
|
|
1282
|
-
* @param cd
|
|
1283
|
-
* @param count
|
|
1257
|
+
* 根据指定的属性值,创建一个 <code>Handler</code> 类的实例。
|
|
1258
|
+
* @param caller 执行域。
|
|
1259
|
+
* @param method 处理函数。
|
|
1260
|
+
* @param args 函数参数。
|
|
1261
|
+
* @param once 是否只执行一次。
|
|
1284
1262
|
*/
|
|
1285
|
-
constructor(
|
|
1286
|
-
|
|
1287
|
-
this.
|
|
1288
|
-
|
|
1289
|
-
this.
|
|
1290
|
-
this.
|
|
1291
|
-
this._setConfig(config);
|
|
1292
|
-
this._eventCache = this._loadStoredEvents();
|
|
1293
|
-
this._tracker = window['SLS_Tracker'] && new window['SLS_Tracker']({
|
|
1294
|
-
host: config.host || "cn-hangzhou.log.aliyuncs.com",
|
|
1295
|
-
project: config.project,
|
|
1296
|
-
logstore: config.logstore,
|
|
1297
|
-
time: config.cd,
|
|
1298
|
-
count: config.count, // 定义数据条数,默认是10条,number类型,选填
|
|
1299
|
-
});
|
|
1300
|
-
this._addListen();
|
|
1301
|
-
this._startLoop();
|
|
1302
|
-
this._doReport();
|
|
1263
|
+
constructor(caller = null, method = null, args = null, once = false) {
|
|
1264
|
+
/** 表示是否只执行一次。如果为true,回调后执行recover()进行回收,回收后会被再利用,默认为false 。*/
|
|
1265
|
+
this.once = false;
|
|
1266
|
+
/**@private */
|
|
1267
|
+
this._id = 0;
|
|
1268
|
+
this.setTo(caller, method, args, once);
|
|
1303
1269
|
}
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1270
|
+
/**
|
|
1271
|
+
* 设置此对象的指定属性值。
|
|
1272
|
+
* @param caller 执行域(this)。
|
|
1273
|
+
* @param method 回调方法。
|
|
1274
|
+
* @param args 携带的参数。
|
|
1275
|
+
* @param once 是否只执行一次,如果为true,执行后执行recover()进行回收。
|
|
1276
|
+
* @return 返回 handler 本身。
|
|
1277
|
+
*/
|
|
1278
|
+
setTo(caller, method, args, once = false) {
|
|
1279
|
+
this._id = Handler._gid++;
|
|
1280
|
+
this.caller = caller;
|
|
1281
|
+
this.method = method;
|
|
1282
|
+
this.args = args;
|
|
1283
|
+
this.once = once;
|
|
1284
|
+
return this;
|
|
1308
1285
|
}
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1286
|
+
/**
|
|
1287
|
+
* 执行处理器。
|
|
1288
|
+
*/
|
|
1289
|
+
run() {
|
|
1290
|
+
if (this.method == null)
|
|
1291
|
+
return null;
|
|
1292
|
+
var id = this._id;
|
|
1293
|
+
var result = this.method.apply(this.caller, this.args);
|
|
1294
|
+
this._id === id && this.once && this.recover();
|
|
1295
|
+
return result;
|
|
1316
1296
|
}
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1297
|
+
/**
|
|
1298
|
+
* 执行处理器,并携带额外数据。
|
|
1299
|
+
* @param data 附加的回调数据,可以是单数据或者Array(作为多参)。
|
|
1300
|
+
*/
|
|
1301
|
+
runWith(data) {
|
|
1302
|
+
if (this.method == null)
|
|
1303
|
+
return null;
|
|
1304
|
+
var id = this._id;
|
|
1305
|
+
if (data == null)
|
|
1306
|
+
var result = this.method.apply(this.caller, this.args);
|
|
1307
|
+
else if (!this.args && !data.unshift)
|
|
1308
|
+
result = this.method.call(this.caller, data);
|
|
1309
|
+
else if (this.args)
|
|
1310
|
+
result = this.method.apply(this.caller, this.args.concat(data));
|
|
1311
|
+
else
|
|
1312
|
+
result = this.method.apply(this.caller, data);
|
|
1313
|
+
this._id === id && this.once && this.recover();
|
|
1314
|
+
return result;
|
|
1323
1315
|
}
|
|
1324
|
-
|
|
1325
|
-
|
|
1316
|
+
/**
|
|
1317
|
+
* 清理对象引用。
|
|
1318
|
+
*/
|
|
1319
|
+
clear() {
|
|
1320
|
+
this.caller = null;
|
|
1321
|
+
this.method = null;
|
|
1322
|
+
this.args = null;
|
|
1323
|
+
return this;
|
|
1326
1324
|
}
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
this._doReport(this._eventCache.length, true);
|
|
1330
|
-
});
|
|
1331
|
-
App.onShow(this._restoreEvents, this);
|
|
1325
|
+
reset() {
|
|
1326
|
+
this.clear();
|
|
1332
1327
|
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
if (eventsInLocalStore.length > 0) {
|
|
1336
|
-
this._eventCache.unshift(...eventsInLocalStore);
|
|
1337
|
-
console.log(`[ALiAnalyticsSender] 恢复本地events(length:${eventsInLocalStore.length}, total:${this._eventCache.length})`);
|
|
1338
|
-
}
|
|
1328
|
+
destroy() {
|
|
1329
|
+
this.clear();
|
|
1339
1330
|
}
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
if (
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
const events = this._eventCache.splice(0, Math.min(this._eventCache.length, maxCount));
|
|
1348
|
-
try {
|
|
1349
|
-
this._tracker.sendBatchLogsImmediate(events);
|
|
1350
|
-
return true;
|
|
1351
|
-
}
|
|
1352
|
-
catch (e) {
|
|
1353
|
-
if (!store) {
|
|
1354
|
-
this._eventCache.unshift(...events);
|
|
1355
|
-
console.log(`[ALiAnalyticsSender] 事件发送失败,恢复事件到队列,等待下次发送。`);
|
|
1356
|
-
}
|
|
1357
|
-
else {
|
|
1358
|
-
this._storeEvents([...events, ...this._eventCache]);
|
|
1359
|
-
this._eventCache.length = 0;
|
|
1360
|
-
console.log(`[ALiAnalyticsSender] 事件发送失败,存储到缓存,等待下次切回游戏。`);
|
|
1361
|
-
}
|
|
1362
|
-
return false;
|
|
1331
|
+
/**
|
|
1332
|
+
* 清理并回收到 Handler 对象池内。
|
|
1333
|
+
*/
|
|
1334
|
+
recover() {
|
|
1335
|
+
if (this._id > 0) {
|
|
1336
|
+
this._id = 0;
|
|
1337
|
+
Handler._pool.recover(this);
|
|
1363
1338
|
}
|
|
1364
1339
|
}
|
|
1365
|
-
|
|
1366
|
-
return
|
|
1367
|
-
data['_event'] = eventName;
|
|
1368
|
-
let msg_data = {
|
|
1369
|
-
appid: publisher.appId,
|
|
1370
|
-
openid: storage.userId || publisher.login.openID || "",
|
|
1371
|
-
deviceid: App.deviceId,
|
|
1372
|
-
sessionid: App.sessionId,
|
|
1373
|
-
level: ++this._index,
|
|
1374
|
-
data: JSON.stringify(data),
|
|
1375
|
-
event: eventName,
|
|
1376
|
-
timestamp: this._config.timestampCall() || Date.now()
|
|
1377
|
-
};
|
|
1378
|
-
this._eventCache.push(msg_data);
|
|
1379
|
-
});
|
|
1340
|
+
equal(value) {
|
|
1341
|
+
return value === this;
|
|
1380
1342
|
}
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
})(DimensionType || (DimensionType = {}));
|
|
1392
|
-
|
|
1393
|
-
class BaseDimension {
|
|
1394
|
-
constructor(name) {
|
|
1395
|
-
this._data = {};
|
|
1396
|
-
this._initialized = false;
|
|
1397
|
-
this._dirty = false;
|
|
1398
|
-
this._name = name;
|
|
1343
|
+
/**
|
|
1344
|
+
* 从对象池内创建一个Handler,默认会执行一次并立即回收,如果不需要自动回收,设置once参数为false。
|
|
1345
|
+
* @param caller 执行域(this)。
|
|
1346
|
+
* @param method 回调方法。
|
|
1347
|
+
* @param args 携带的参数。
|
|
1348
|
+
* @param once 是否只执行一次,如果为true,回调后执行recover()进行回收,默认为true。
|
|
1349
|
+
* @return 返回创建的handler实例。
|
|
1350
|
+
*/
|
|
1351
|
+
static create(caller, method, args = null, once = true) {
|
|
1352
|
+
return Handler._pool.allocate().setTo(caller, method, args, once);
|
|
1399
1353
|
}
|
|
1400
|
-
|
|
1401
|
-
|
|
1354
|
+
}
|
|
1355
|
+
/**@private handler对象池*/
|
|
1356
|
+
Handler._pool = new Pool(Handler);
|
|
1357
|
+
/**@private */
|
|
1358
|
+
Handler._gid = 1;
|
|
1359
|
+
|
|
1360
|
+
var InternalProperty;
|
|
1361
|
+
(function (InternalProperty) {
|
|
1362
|
+
/**设备系统版本 */
|
|
1363
|
+
InternalProperty["DEVICE_SYSTEM"] = "device_system";
|
|
1364
|
+
/**设备操作系统类型 */
|
|
1365
|
+
InternalProperty["DEVICE_PLATFORM"] = "device_platform";
|
|
1366
|
+
/**设备手机品牌 */
|
|
1367
|
+
InternalProperty["DEVICE_BRAND"] = "device_brand";
|
|
1368
|
+
/**设备手机型号 */
|
|
1369
|
+
InternalProperty["DEVICE_MODEL"] = "device_model";
|
|
1370
|
+
/**宿主名称 */
|
|
1371
|
+
InternalProperty["HOST_NAME"] = "host_name";
|
|
1372
|
+
/**宿主版本 */
|
|
1373
|
+
InternalProperty["HOST_VERSION"] = "host_version";
|
|
1374
|
+
/**启动时间 */
|
|
1375
|
+
InternalProperty["LAUNCH_TIME"] = "launch_time";
|
|
1376
|
+
/**启动次数 */
|
|
1377
|
+
InternalProperty["LAUNCH_TIMES"] = "launch_times";
|
|
1378
|
+
/**安装时间 */
|
|
1379
|
+
InternalProperty["INSTALL_TIME"] = "install_time";
|
|
1380
|
+
/**app运载(安装)时长 */
|
|
1381
|
+
InternalProperty["DURATION_OF_INSTALL"] = "duration_of_install";
|
|
1382
|
+
/**app运行(启动)时长 */
|
|
1383
|
+
InternalProperty["DURATION_OF_LAUNCH"] = "duration_of_launch";
|
|
1384
|
+
/**累积登录天数 */
|
|
1385
|
+
InternalProperty["LOGIN_DAYS"] = "login_days";
|
|
1386
|
+
/**安装来源type */
|
|
1387
|
+
InternalProperty["INSTALL_SCENE"] = "install_scene";
|
|
1388
|
+
/**安装来源source */
|
|
1389
|
+
InternalProperty["INSTALL_CHANNEL"] = "install_channel";
|
|
1390
|
+
/**启动来源场景 */
|
|
1391
|
+
InternalProperty["LAUNCH_SCENE"] = "launch_scene";
|
|
1392
|
+
/**启动来源渠道 */
|
|
1393
|
+
InternalProperty["LAUNCH_CHANNEL"] = "launch_channel";
|
|
1394
|
+
/**事件计时时长 */
|
|
1395
|
+
InternalProperty["DURATION"] = "duration";
|
|
1396
|
+
/**游戏版本 */
|
|
1397
|
+
InternalProperty["APP_VERSION"] = "app_version";
|
|
1398
|
+
})(InternalProperty || (InternalProperty = {}));
|
|
1399
|
+
class EmptyAnalyticsSender {
|
|
1400
|
+
/**
|
|
1401
|
+
* @param [exclude] 其他排除的上报日志, 默认不排除
|
|
1402
|
+
*/
|
|
1403
|
+
constructor(exclude) {
|
|
1404
|
+
this._exclude = exclude || [];
|
|
1402
1405
|
}
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1406
|
+
/**
|
|
1407
|
+
* 设置排除
|
|
1408
|
+
* @param excludeList 排除事件名称或者名称列表。
|
|
1409
|
+
*/
|
|
1410
|
+
exclude(...excludeList) {
|
|
1411
|
+
this._exclude = excludeList.concat();
|
|
1408
1412
|
}
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
if (data) {
|
|
1413
|
-
try {
|
|
1414
|
-
this._data = this._decompress(data);
|
|
1415
|
-
}
|
|
1416
|
-
catch (e) {
|
|
1417
|
-
console.error(`加载维度${this._name}失败:`, e);
|
|
1418
|
-
this._data = {};
|
|
1419
|
-
}
|
|
1413
|
+
sendAble(eventName) {
|
|
1414
|
+
if (this._exclude && this._exclude.indexOf(eventName) != -1) {
|
|
1415
|
+
return false;
|
|
1420
1416
|
}
|
|
1417
|
+
return true;
|
|
1421
1418
|
}
|
|
1422
|
-
|
|
1419
|
+
send(eventName, data) {
|
|
1423
1420
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1424
|
-
if (!this._dirty)
|
|
1425
|
-
return;
|
|
1426
|
-
const key = `dim_${this._name}`;
|
|
1427
|
-
const compressed = this._compress(this._data);
|
|
1428
|
-
storage.setUValue(key, compressed);
|
|
1429
|
-
this._dirty = false;
|
|
1430
|
-
});
|
|
1431
|
-
}
|
|
1432
|
-
_updateValue(key, value) {
|
|
1433
|
-
this._ensureInit();
|
|
1434
|
-
this._data[key] = value;
|
|
1435
|
-
this._dirty = true;
|
|
1436
|
-
this._saveToStorage();
|
|
1437
|
-
}
|
|
1438
|
-
_incrementValue(key, delta = 1) {
|
|
1439
|
-
this._ensureInit();
|
|
1440
|
-
this._data[key] = (this._data[key] || 0) + delta;
|
|
1441
|
-
this._dirty = true;
|
|
1442
|
-
this._saveToStorage();
|
|
1443
|
-
}
|
|
1444
|
-
_getValue(key, defaultValue = 0) {
|
|
1445
|
-
var _a;
|
|
1446
|
-
this._ensureInit();
|
|
1447
|
-
return (_a = this._data[key]) !== null && _a !== void 0 ? _a : defaultValue;
|
|
1448
|
-
}
|
|
1449
|
-
getData() {
|
|
1450
|
-
this._ensureInit();
|
|
1451
|
-
return Object.assign({}, this._data);
|
|
1452
|
-
}
|
|
1453
|
-
_compress(data) {
|
|
1454
|
-
return Object.entries(data)
|
|
1455
|
-
.map(([k, v]) => `${k}:${v}`)
|
|
1456
|
-
.join(',');
|
|
1457
|
-
}
|
|
1458
|
-
_decompress(str) {
|
|
1459
|
-
const data = {};
|
|
1460
|
-
if (!str)
|
|
1461
|
-
return data;
|
|
1462
|
-
str.split(',').forEach(pair => {
|
|
1463
|
-
const [k, v] = pair.split(':');
|
|
1464
|
-
if (k && v !== undefined) {
|
|
1465
|
-
data[k] = Number(v);
|
|
1466
|
-
}
|
|
1467
1421
|
});
|
|
1468
|
-
return data;
|
|
1469
|
-
}
|
|
1470
|
-
clear() {
|
|
1471
|
-
this._data = {};
|
|
1472
|
-
this._dirty = true;
|
|
1473
|
-
this._saveToStorage();
|
|
1474
1422
|
}
|
|
1475
1423
|
}
|
|
1476
|
-
|
|
1477
|
-
var AdDataKey;
|
|
1478
|
-
(function (AdDataKey) {
|
|
1479
|
-
// 累计展示次数
|
|
1480
|
-
AdDataKey["SHOW_COUNT"] = "sc";
|
|
1481
|
-
// 展示天数
|
|
1482
|
-
AdDataKey["SHOW_DAYS"] = "sd";
|
|
1483
|
-
// 累计完成次数
|
|
1484
|
-
AdDataKey["COMPLETE_COUNT"] = "cpc";
|
|
1485
|
-
// 最后展示天数
|
|
1486
|
-
AdDataKey["LAST_SHOW_DAY"] = "lsd";
|
|
1487
|
-
// 今日展示次数
|
|
1488
|
-
AdDataKey["TODAY_SHOW_COUNT"] = "tsc";
|
|
1489
|
-
// 首次展示时间(相对安装时间,秒)
|
|
1490
|
-
AdDataKey["FIRST_SHOW_TIME"] = "fst";
|
|
1491
|
-
// 上次展示时间(相对安装时间,秒)
|
|
1492
|
-
AdDataKey["LAST_SHOW_TIME"] = "lst";
|
|
1493
|
-
// 平均展示间隔(秒)
|
|
1494
|
-
AdDataKey["AVG_SHOW_INTERVAL"] = "asi";
|
|
1495
|
-
// 今日平均展示间隔(秒)
|
|
1496
|
-
AdDataKey["TODAY_AVG_SHOW_INTERVAL"] = "tasi";
|
|
1497
|
-
})(AdDataKey || (AdDataKey = {}));
|
|
1498
|
-
const AD_METRIC_ORDER_V1 = [
|
|
1499
|
-
'AD.show_count',
|
|
1500
|
-
'AD.show_days',
|
|
1501
|
-
'AD.complete_count',
|
|
1502
|
-
'AD.last_show_day',
|
|
1503
|
-
'AD.today_show_count',
|
|
1504
|
-
'AD.first_show_time',
|
|
1505
|
-
'AD.last_show_time',
|
|
1506
|
-
'AD.avg_show_interval',
|
|
1507
|
-
'AD.today_avg_show_interval',
|
|
1508
|
-
'AD.avg_show_per_day',
|
|
1509
|
-
'AD.avg_complete_per_day',
|
|
1510
|
-
];
|
|
1511
1424
|
/**
|
|
1512
|
-
*
|
|
1425
|
+
* 统计分析能力
|
|
1513
1426
|
*/
|
|
1514
|
-
class
|
|
1427
|
+
class AnalyticsAble {
|
|
1515
1428
|
constructor() {
|
|
1516
|
-
|
|
1517
|
-
this.
|
|
1518
|
-
this.
|
|
1429
|
+
this._inited = false;
|
|
1430
|
+
this._hasSetDefaultProperty = false;
|
|
1431
|
+
this._debugMode = false;
|
|
1432
|
+
this._senderList = [];
|
|
1433
|
+
this._dynamicCalls = [];
|
|
1434
|
+
this._userId = null;
|
|
1435
|
+
this._hasSetUserId = false;
|
|
1519
1436
|
}
|
|
1437
|
+
get inited() { return this._inited; }
|
|
1520
1438
|
/**
|
|
1521
|
-
*
|
|
1439
|
+
* 初始化
|
|
1522
1440
|
*/
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
this
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
this._todayPrevShowTime = 0;
|
|
1533
|
-
}
|
|
1534
|
-
});
|
|
1441
|
+
init(senders, debugMode = false) {
|
|
1442
|
+
if (this._inited)
|
|
1443
|
+
return this;
|
|
1444
|
+
this._debugMode = debugMode;
|
|
1445
|
+
App.onShow(this._onAppShow, this);
|
|
1446
|
+
App.onHide(this._onAppHide, this);
|
|
1447
|
+
this._initSenderList(senders);
|
|
1448
|
+
this._inited = true;
|
|
1449
|
+
return this;
|
|
1535
1450
|
}
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
const today = App.daysInstall;
|
|
1542
|
-
const lastDay = this._getValue(AdDataKey.LAST_SHOW_DAY, -1);
|
|
1543
|
-
// 更新展示天数和最后展示天数
|
|
1544
|
-
if (lastDay !== today) {
|
|
1545
|
-
this._incrementValue(AdDataKey.SHOW_DAYS);
|
|
1546
|
-
this._updateValue(AdDataKey.LAST_SHOW_DAY, today);
|
|
1547
|
-
}
|
|
1548
|
-
// 记录当前展示时间(相对安装时间)
|
|
1549
|
-
const currentTime = Math.floor(App.durationOfInstall / 1000);
|
|
1550
|
-
const currentLaunchTime = Math.floor(App.durationOfLaunch / 1000);
|
|
1551
|
-
// 计算总体平均间隔(从第二次展示开始)
|
|
1552
|
-
if (showCount > 0 && prevShowTime > 0) {
|
|
1553
|
-
const interval = currentTime - prevShowTime;
|
|
1554
|
-
const oldAvg = this._getValue(AdDataKey.AVG_SHOW_INTERVAL, 0);
|
|
1555
|
-
// 增量平均公式: 新平均 = 旧平均 + (本次值 - 旧平均) / 新的数据个数
|
|
1556
|
-
const newAvg = Math.floor(oldAvg + (interval - oldAvg) / showCount);
|
|
1557
|
-
this._updateValue(AdDataKey.AVG_SHOW_INTERVAL, newAvg);
|
|
1451
|
+
_initSenderList(senders) {
|
|
1452
|
+
for (let i = 0; i < senders.length; i++) {
|
|
1453
|
+
const sender = senders[i];
|
|
1454
|
+
this._senderList.push(sender);
|
|
1455
|
+
this._syncSenderUserState(sender);
|
|
1558
1456
|
}
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
if (
|
|
1562
|
-
|
|
1563
|
-
const oldTodayAvg = this._getValue(AdDataKey.TODAY_AVG_SHOW_INTERVAL, 0);
|
|
1564
|
-
// 今日间隔数 = 今日展示次数 - 1
|
|
1565
|
-
const newTodayAvg = Math.floor(oldTodayAvg + (todayInterval - oldTodayAvg) / (todayShowCount - 1));
|
|
1566
|
-
this._updateValue(AdDataKey.TODAY_AVG_SHOW_INTERVAL, newTodayAvg);
|
|
1457
|
+
}
|
|
1458
|
+
_usesSenderUserProperties(sender) {
|
|
1459
|
+
if (sender.userPropertyMode !== "sender") {
|
|
1460
|
+
return false;
|
|
1567
1461
|
}
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
this._updateValue(AdDataKey.FIRST_SHOW_TIME, currentTime);
|
|
1462
|
+
if (typeof sender.setUserProperty === "function") {
|
|
1463
|
+
return true;
|
|
1571
1464
|
}
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
this._todayPrevShowTime = currentLaunchTime;
|
|
1465
|
+
this._debugMode && console.error("[Analytics] userPropertyMode=sender 的 Sender 必须实现 setUserProperty,已回退为 event 模式。");
|
|
1466
|
+
return false;
|
|
1575
1467
|
}
|
|
1576
|
-
|
|
1577
|
-
|
|
1468
|
+
_getUserProperties() {
|
|
1469
|
+
return Object.assign({}, this._userOnceProperties || {}, this._userAddProperties || {}, this._userProperties || {});
|
|
1578
1470
|
}
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
const days = this._getValue(AdDataKey.SHOW_DAYS, 0);
|
|
1593
|
-
return days > 0 ? Math.floor(total / days) : 0;
|
|
1471
|
+
_syncSenderUserState(sender) {
|
|
1472
|
+
if (this._hasSetUserId && typeof sender.setUserId === "function") {
|
|
1473
|
+
sender.setUserId(this._userId);
|
|
1474
|
+
}
|
|
1475
|
+
if (!this._usesSenderUserProperties(sender)) {
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
const properties = this._getUserProperties();
|
|
1479
|
+
for (const key in properties) {
|
|
1480
|
+
if (Object.prototype.hasOwnProperty.call(properties, key)) {
|
|
1481
|
+
sender.setUserProperty(key, properties[key]);
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1594
1484
|
}
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1485
|
+
_notifyUserProperty(key, value) {
|
|
1486
|
+
for (let i = 0; i < this._senderList.length; i++) {
|
|
1487
|
+
const sender = this._senderList[i];
|
|
1488
|
+
if (this._usesSenderUserProperties(sender)) {
|
|
1489
|
+
sender.setUserProperty(key, value);
|
|
1490
|
+
}
|
|
1491
|
+
}
|
|
1601
1492
|
}
|
|
1602
1493
|
/**
|
|
1603
|
-
*
|
|
1604
|
-
*
|
|
1494
|
+
* 设置排除
|
|
1495
|
+
* @param excludeEvents 排除事件名称或者名称列表。
|
|
1496
|
+
* @param senderClass [可选] 特定的SenderClass
|
|
1605
1497
|
*/
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
AdDataKey.AVG_SHOW_INTERVAL,
|
|
1618
|
-
AdDataKey.TODAY_AVG_SHOW_INTERVAL
|
|
1619
|
-
]) {
|
|
1620
|
-
this._ensureInit();
|
|
1621
|
-
const result = [];
|
|
1622
|
-
for (const key of order) {
|
|
1623
|
-
result.push(this._getValue(key, 0));
|
|
1498
|
+
setExclude(excludeEvents, senderClass) {
|
|
1499
|
+
for (let i = 0; i < this._senderList.length; i++) {
|
|
1500
|
+
const sender = this._senderList[i];
|
|
1501
|
+
if (!senderClass || sender instanceof senderClass) {
|
|
1502
|
+
if (typeof excludeEvents === "string") {
|
|
1503
|
+
sender.exclude(excludeEvents);
|
|
1504
|
+
}
|
|
1505
|
+
else {
|
|
1506
|
+
sender.exclude(...excludeEvents);
|
|
1507
|
+
}
|
|
1508
|
+
}
|
|
1624
1509
|
}
|
|
1625
|
-
result.push(this.avgShowPerDay);
|
|
1626
|
-
result.push(this.avgCompletePerDay);
|
|
1627
|
-
return result;
|
|
1628
1510
|
}
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
ADType[ADType["REWARD_VIDEO"] = 2] = "REWARD_VIDEO";
|
|
1636
|
-
ADType[ADType["NATIVE"] = 3] = "NATIVE";
|
|
1637
|
-
})(ADType || (ADType = {}));
|
|
1638
|
-
var ADEvent;
|
|
1639
|
-
(function (ADEvent) {
|
|
1640
|
-
ADEvent[ADEvent["LOAD"] = 0] = "LOAD";
|
|
1641
|
-
ADEvent[ADEvent["LOAD_COMPLETE"] = 1] = "LOAD_COMPLETE";
|
|
1642
|
-
ADEvent[ADEvent["LOAD_FAIL"] = 2] = "LOAD_FAIL";
|
|
1643
|
-
ADEvent[ADEvent["SHOW"] = 3] = "SHOW";
|
|
1644
|
-
ADEvent[ADEvent["SHOW_COMPLETE"] = 4] = "SHOW_COMPLETE";
|
|
1645
|
-
ADEvent[ADEvent["SHOW_FAIL"] = 5] = "SHOW_FAIL";
|
|
1646
|
-
ADEvent[ADEvent["CLOSE_BY_USER"] = 6] = "CLOSE_BY_USER";
|
|
1647
|
-
})(ADEvent || (ADEvent = {}));
|
|
1648
|
-
class ADBehaviorReporter {
|
|
1649
|
-
constructor(baseAble) {
|
|
1650
|
-
this.baseAble = baseAble;
|
|
1651
|
-
this._groupCount = {};
|
|
1511
|
+
_onAppShow() {
|
|
1512
|
+
this.time("app_hide");
|
|
1513
|
+
this.report("app_show", publisher.publisher.enterOptions.getAllOption());
|
|
1514
|
+
}
|
|
1515
|
+
_onAppHide() {
|
|
1516
|
+
this.report("app_hide");
|
|
1652
1517
|
}
|
|
1653
1518
|
/**
|
|
1654
|
-
*
|
|
1655
|
-
* @param group 模块名称,默认为game
|
|
1519
|
+
* 设置所有上报事件的基础属性
|
|
1656
1520
|
*/
|
|
1657
|
-
|
|
1658
|
-
this.
|
|
1521
|
+
_setDefaultProperty() {
|
|
1522
|
+
if (this._hasSetDefaultProperty) {
|
|
1523
|
+
return;
|
|
1524
|
+
}
|
|
1525
|
+
//设备信息
|
|
1526
|
+
let deviceInfo = publisher.device.deviceInfo;
|
|
1527
|
+
this.setProperty(InternalProperty.DEVICE_SYSTEM, deviceInfo.system);
|
|
1528
|
+
this.setProperty(InternalProperty.DEVICE_PLATFORM, deviceInfo.platform);
|
|
1529
|
+
this.setProperty(InternalProperty.DEVICE_BRAND, deviceInfo.brand);
|
|
1530
|
+
this.setProperty(InternalProperty.DEVICE_MODEL, deviceInfo.model);
|
|
1531
|
+
//宿主信息
|
|
1532
|
+
let hostInfo = publisher.device.hostInfo;
|
|
1533
|
+
this.setProperty(InternalProperty.HOST_NAME, hostInfo.name);
|
|
1534
|
+
this.setProperty(InternalProperty.HOST_VERSION, hostInfo.version);
|
|
1535
|
+
//安装时间
|
|
1536
|
+
this.setProperty(InternalProperty.INSTALL_TIME, Math.floor(App.installTime / 1000));
|
|
1537
|
+
//启动时间
|
|
1538
|
+
this.setProperty(InternalProperty.LAUNCH_TIME, Math.floor(App.launchTime / 1000));
|
|
1539
|
+
//启动次数
|
|
1540
|
+
this.setProperty(InternalProperty.LAUNCH_TIMES, App.launchTimes);
|
|
1541
|
+
//游戏版本
|
|
1542
|
+
this.setProperty(InternalProperty.APP_VERSION, publisher.publisher.appVersion);
|
|
1543
|
+
this.setDynamicProperties(() => {
|
|
1544
|
+
let dynamicData = {};
|
|
1545
|
+
//app运载(安装)时长
|
|
1546
|
+
dynamicData[InternalProperty.DURATION_OF_INSTALL] = Math.floor(App.durationOfInstall / 1000);
|
|
1547
|
+
//app运行(启动)时长
|
|
1548
|
+
dynamicData[InternalProperty.DURATION_OF_LAUNCH] = Math.floor(App.durationOfLaunch / 1000);
|
|
1549
|
+
return dynamicData;
|
|
1550
|
+
});
|
|
1551
|
+
//累积登录天数
|
|
1552
|
+
this.userSet(InternalProperty.LOGIN_DAYS, storage.loginDays);
|
|
1553
|
+
//安装来源type
|
|
1554
|
+
this.userSet(InternalProperty.INSTALL_SCENE, publisher.publisher.installOptions.channelScene);
|
|
1555
|
+
//安装来源source
|
|
1556
|
+
this.userSet(InternalProperty.INSTALL_CHANNEL, publisher.publisher.installOptions.channelSource);
|
|
1557
|
+
//启动来源场景
|
|
1558
|
+
this.userSet(InternalProperty.LAUNCH_SCENE, publisher.publisher.enterOptions.channelScene);
|
|
1559
|
+
//启动来源渠道
|
|
1560
|
+
this.userSet(InternalProperty.LAUNCH_CHANNEL, publisher.publisher.enterOptions.channelSource);
|
|
1561
|
+
this._hasSetDefaultProperty = true;
|
|
1659
1562
|
}
|
|
1660
1563
|
/**
|
|
1661
|
-
*
|
|
1662
|
-
* @param
|
|
1663
|
-
* @param
|
|
1664
|
-
* @param msg 广告失败原因
|
|
1564
|
+
* 设置事件公共属性
|
|
1565
|
+
* @param key 公共属性key
|
|
1566
|
+
* @param value
|
|
1665
1567
|
*/
|
|
1666
|
-
|
|
1667
|
-
this.
|
|
1568
|
+
setProperty(key, value) {
|
|
1569
|
+
if (!this._staticProperties) {
|
|
1570
|
+
this._staticProperties = {};
|
|
1571
|
+
}
|
|
1572
|
+
if (typeof value === "boolean") {
|
|
1573
|
+
value = value ? 1 : 0;
|
|
1574
|
+
}
|
|
1575
|
+
if (this._debugMode && Object.prototype.hasOwnProperty.call(this._staticProperties, key)) {
|
|
1576
|
+
console.warn(`静态公共属性中已经存在${key}:${this._staticProperties[key]}, 将替换为${value}`);
|
|
1577
|
+
}
|
|
1578
|
+
this._staticProperties[key] = value;
|
|
1668
1579
|
}
|
|
1669
1580
|
/**
|
|
1670
|
-
*
|
|
1671
|
-
* @param
|
|
1672
|
-
* @
|
|
1581
|
+
* 删除事件的公共属性
|
|
1582
|
+
* @param key
|
|
1583
|
+
* @returns
|
|
1673
1584
|
*/
|
|
1674
|
-
|
|
1675
|
-
this.
|
|
1585
|
+
unsetProperty(key) {
|
|
1586
|
+
if (this._staticProperties) {
|
|
1587
|
+
return delete this._staticProperties[key];
|
|
1588
|
+
}
|
|
1589
|
+
return false;
|
|
1676
1590
|
}
|
|
1677
1591
|
/**
|
|
1678
|
-
*
|
|
1679
|
-
* @param
|
|
1680
|
-
* @param from 广告来源
|
|
1681
|
-
* @param msg 广告失败原因
|
|
1592
|
+
* 设置事件公共动态属性,上报事件时会触发回调函数,并把返回的AnalyticsData加入到事件属性中
|
|
1593
|
+
* @param dynamicCall 动态属性的生成函数,事件上报时实时调用
|
|
1682
1594
|
*/
|
|
1683
|
-
|
|
1684
|
-
this.
|
|
1595
|
+
setDynamicProperties(dynamicCall) {
|
|
1596
|
+
this._dynamicCalls.push(dynamicCall);
|
|
1685
1597
|
}
|
|
1686
1598
|
/**
|
|
1687
|
-
*
|
|
1688
|
-
* @param type 广告类型
|
|
1689
|
-
* @param from 广告来源
|
|
1599
|
+
* 设置平台用户标识。该值不会自动作为事件参数发送。
|
|
1690
1600
|
*/
|
|
1691
|
-
|
|
1692
|
-
this.
|
|
1601
|
+
setUserId(userId) {
|
|
1602
|
+
this._userId = userId;
|
|
1603
|
+
this._hasSetUserId = true;
|
|
1604
|
+
for (let i = 0; i < this._senderList.length; i++) {
|
|
1605
|
+
const sender = this._senderList[i];
|
|
1606
|
+
if (typeof sender.setUserId === "function") {
|
|
1607
|
+
sender.setUserId(userId);
|
|
1608
|
+
}
|
|
1609
|
+
}
|
|
1693
1610
|
}
|
|
1694
1611
|
/**
|
|
1695
|
-
*
|
|
1696
|
-
*
|
|
1697
|
-
*
|
|
1612
|
+
* 对于一般的用户属性,您可以调用 userSet 来进行设置。
|
|
1613
|
+
* 使用该接口上传的属性将会覆盖原有的属性值,如果之前不存在该用户属性,则会新建该用户属性,类型与传入属性的类型一致。
|
|
1614
|
+
* 用户名为例:
|
|
1615
|
+
* ```
|
|
1616
|
+
* // username为TA
|
|
1617
|
+
* userSet("username", "TA");
|
|
1618
|
+
* //username为TE
|
|
1619
|
+
* userSet("username", "TE");
|
|
1620
|
+
* ```
|
|
1621
|
+
* @param key 属性key
|
|
1622
|
+
* @param value
|
|
1698
1623
|
*/
|
|
1699
|
-
|
|
1700
|
-
this.
|
|
1624
|
+
userSet(key, value) {
|
|
1625
|
+
if (!this._userProperties) {
|
|
1626
|
+
this._userProperties = {};
|
|
1627
|
+
}
|
|
1628
|
+
if (typeof value === "boolean") {
|
|
1629
|
+
value = value ? 1 : 0;
|
|
1630
|
+
}
|
|
1631
|
+
if (this._debugMode && Object.prototype.hasOwnProperty.call(this._userProperties, key)) {
|
|
1632
|
+
console.warn(`用户属性中已经存在${key}:${this._userProperties[key]}, 将替换为${value}`);
|
|
1633
|
+
}
|
|
1634
|
+
this._userProperties[key] = value;
|
|
1635
|
+
this._notifyUserProperty(key, value);
|
|
1701
1636
|
}
|
|
1702
1637
|
/**
|
|
1703
|
-
*
|
|
1704
|
-
*
|
|
1705
|
-
*
|
|
1706
|
-
*
|
|
1638
|
+
* 如果您要上传的用户属性只要设置一次,则可以调用 userSetOnce 来进行设置。
|
|
1639
|
+
* 当该属性之前已经有值的时候,将会忽略这条信息,比如设置首次付费时间:
|
|
1640
|
+
* ```
|
|
1641
|
+
* //first_payment_time为2018-01-01 01:23:45.678
|
|
1642
|
+
* userOnce("first_payment_time", "2018-01-01 01:23:45.678");
|
|
1643
|
+
* //first_payment_time仍然为2018-01-01 01:23:45.678
|
|
1644
|
+
* userOnce("first_payment_time", "2018-12-31 01:23:45.678");
|
|
1645
|
+
* ```
|
|
1646
|
+
* @param key 属性key
|
|
1647
|
+
* @param value
|
|
1648
|
+
* @returns
|
|
1707
1649
|
*/
|
|
1708
|
-
|
|
1709
|
-
this.
|
|
1650
|
+
userOnce(key, value) {
|
|
1651
|
+
if (!this._userOnceProperties) {
|
|
1652
|
+
this._userOnceProperties = {};
|
|
1653
|
+
}
|
|
1654
|
+
if (typeof value === "boolean") {
|
|
1655
|
+
value = value ? 1 : 0;
|
|
1656
|
+
}
|
|
1657
|
+
if (Object.prototype.hasOwnProperty.call(this._userOnceProperties, key)) {
|
|
1658
|
+
this._debugMode && console.info(`用户属性中已经存在once属性${key}:${this._userOnceProperties[key]}`);
|
|
1659
|
+
return;
|
|
1660
|
+
}
|
|
1661
|
+
let storageValue = storage.getUValue(`analytics_user_once_${key}`, "");
|
|
1662
|
+
if (storageValue != null && storageValue != "") {
|
|
1663
|
+
this._debugMode && console.info(`用户属性中已经存在once属性${key}:${storageValue}`);
|
|
1664
|
+
this._userOnceProperties[key] = typeof value === "number" ? Number(storageValue) : storageValue;
|
|
1665
|
+
this._notifyUserProperty(key, this._userOnceProperties[key]);
|
|
1666
|
+
return;
|
|
1667
|
+
}
|
|
1668
|
+
storage.setUValue(`analytics_user_once_${key}`, value);
|
|
1669
|
+
this._userOnceProperties[key] = value;
|
|
1670
|
+
this._notifyUserProperty(key, value);
|
|
1710
1671
|
}
|
|
1711
1672
|
/**
|
|
1712
|
-
|
|
1713
|
-
*
|
|
1714
|
-
*
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
*
|
|
1721
|
-
* @param type 广告类型
|
|
1722
|
-
* @param event 广告事件
|
|
1723
|
-
* @param from 广告来源
|
|
1724
|
-
* @param msg 广告失败原因
|
|
1673
|
+
*设置用户累积属性,当您要上传数值型的属性时,您可以调用 userAdd 来对该属性进行累加操作,
|
|
1674
|
+
* 如果该属性还未被设置,则会赋值 0 后再进行计算。
|
|
1675
|
+
* 如果传入负值,等同于减法操作。
|
|
1676
|
+
* ```
|
|
1677
|
+
* userAdd("total_revenue", 30); //此时total_revenue为30
|
|
1678
|
+
* userAdd("total_revenue", 648); //此时total_revenue为678
|
|
1679
|
+
* ```
|
|
1680
|
+
* @param key 累积属性key
|
|
1681
|
+
* @param value 累积的偏移值
|
|
1725
1682
|
*/
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
from.group && delete from.group;
|
|
1730
|
-
from._adtype = type;
|
|
1731
|
-
from._adevent = event;
|
|
1732
|
-
if (!this._groupCount[group]) {
|
|
1733
|
-
this._groupCount[group] = 0;
|
|
1734
|
-
}
|
|
1735
|
-
from.count = event == ADEvent.SHOW_COMPLETE ? ++this._groupCount[group] : this._groupCount[group];
|
|
1736
|
-
if (msg) {
|
|
1737
|
-
from.msg = msg;
|
|
1683
|
+
userAdd(key, value) {
|
|
1684
|
+
if (!this._userAddProperties) {
|
|
1685
|
+
this._userAddProperties = {};
|
|
1738
1686
|
}
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
}
|
|
1742
|
-
|
|
1743
|
-
/**
|
|
1744
|
-
* 对象池
|
|
1745
|
-
*/
|
|
1746
|
-
class Pool {
|
|
1747
|
-
constructor(clazz, maxCount) {
|
|
1748
|
-
/**池中闲置对象 */
|
|
1749
|
-
this._cacheStack = new Array();
|
|
1750
|
-
/**正在使用的对象 */
|
|
1751
|
-
this._usingArray = new Array();
|
|
1752
|
-
/**池中对象最大数 */
|
|
1753
|
-
this._maxCount = 0;
|
|
1754
|
-
this._class = clazz;
|
|
1755
|
-
if (!this._class) {
|
|
1756
|
-
throw new Error("构造函数不能为空!");
|
|
1687
|
+
if (typeof value === "boolean") {
|
|
1688
|
+
value = value ? 1 : 0;
|
|
1757
1689
|
}
|
|
1758
|
-
|
|
1690
|
+
let total = Number(this._userAddProperties[key] || storage.getUValue(`analytics_user_add_${key}`, 0) || 0);
|
|
1691
|
+
this._userAddProperties[key] = total + value;
|
|
1692
|
+
storage.setUValue(`analytics_user_add_${key}`, total + value);
|
|
1693
|
+
this._notifyUserProperty(key, total + value);
|
|
1759
1694
|
}
|
|
1760
1695
|
/**
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1696
|
+
* 移除用户属性。当您要清空用户的用户属性值时,您可以调用 userUnset 来对指定属性进行清空操作,如果该属性还未在集群中被创建,则 user_unset 不会创建该属性
|
|
1697
|
+
* @param key
|
|
1698
|
+
*/
|
|
1699
|
+
userUnset(key) {
|
|
1700
|
+
this._userProperties && delete this._userProperties[key];
|
|
1701
|
+
this._userOnceProperties && delete this._userOnceProperties[key];
|
|
1702
|
+
this._userAddProperties && delete this._userAddProperties[key];
|
|
1703
|
+
storage.removeUValue(`analytics_user_once_${key}`);
|
|
1704
|
+
storage.removeUValue(`analytics_user_add_${key}`);
|
|
1705
|
+
this._notifyUserProperty(key, null);
|
|
1765
1706
|
}
|
|
1766
1707
|
/**
|
|
1767
|
-
*
|
|
1708
|
+
* 上报错误
|
|
1709
|
+
* @param error 错误信息
|
|
1768
1710
|
*/
|
|
1769
|
-
|
|
1770
|
-
|
|
1711
|
+
error(error) {
|
|
1712
|
+
this.report("app_error", { error: error });
|
|
1771
1713
|
}
|
|
1772
1714
|
/**
|
|
1773
|
-
*
|
|
1774
|
-
*
|
|
1715
|
+
* 开始事件计时
|
|
1716
|
+
* 如果您需要记录某个事件的持续时长,可以调用 timeEvent 来开始计时。
|
|
1717
|
+
* 配置您想要计时的事件名称,当您上传该事件时,将会自动在您的事件属性中加入 duration 属性来表示记录的时长,单位为秒。
|
|
1718
|
+
* 需要注意的是,同一个事件名只能有一个在计时的任务。
|
|
1719
|
+
* @param eventName
|
|
1775
1720
|
*/
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
}
|
|
1782
|
-
throw new Error("对象池最大数量超出:" + this._maxCount);
|
|
1721
|
+
time(eventName, once = true, offset = 0) {
|
|
1722
|
+
this._timeEvents = this._timeEvents || new Map();
|
|
1723
|
+
this._debugMode && this._timeEvents.has(eventName) && console.warn(`已经存在${eventName}的计时,将被重置。`);
|
|
1724
|
+
this.cancelTime(eventName);
|
|
1725
|
+
this._timeEvents.set(eventName, { start: App.durationOfLaunch, once: once, offset: offset });
|
|
1783
1726
|
}
|
|
1784
1727
|
/**
|
|
1785
|
-
*
|
|
1786
|
-
* @param
|
|
1728
|
+
* 取消事件计时
|
|
1729
|
+
* @param eventName
|
|
1787
1730
|
* @returns
|
|
1788
1731
|
*/
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
throw new Error("重复回收!");
|
|
1792
|
-
}
|
|
1793
|
-
let index = this._usingArray.indexOf(value);
|
|
1794
|
-
if (index < 0) {
|
|
1795
|
-
throw new Error("对象不属于改对象池!");
|
|
1796
|
-
}
|
|
1797
|
-
//重置
|
|
1798
|
-
value.reset();
|
|
1799
|
-
this._usingArray.splice(index, 1);
|
|
1800
|
-
this._cacheStack.push(value);
|
|
1732
|
+
cancelTime(eventName) {
|
|
1733
|
+
return this._timeEvents && this._timeEvents.has(eventName) && this._timeEvents.delete(eventName);
|
|
1801
1734
|
}
|
|
1802
1735
|
/**
|
|
1803
|
-
*
|
|
1804
|
-
* @param
|
|
1736
|
+
* 上报事件
|
|
1737
|
+
* @param eventName 事件名称
|
|
1738
|
+
* @param data 事件数据
|
|
1805
1739
|
*/
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1740
|
+
report(eventName, data) {
|
|
1741
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1742
|
+
if (!this._sendAble(eventName)) {
|
|
1743
|
+
this._debugMode && console.log(`[Analytics] ${eventName}事件不上报`);
|
|
1744
|
+
return;
|
|
1745
|
+
}
|
|
1746
|
+
if (!data) {
|
|
1747
|
+
this._debugMode && console.warn(`[Analytics] ${eventName}无上报事件数据`);
|
|
1748
|
+
data = {};
|
|
1749
|
+
}
|
|
1750
|
+
else {
|
|
1751
|
+
data = Object.assign({}, data);
|
|
1752
|
+
}
|
|
1753
|
+
this._setDefaultProperty();
|
|
1754
|
+
for (let i = 0; i < this._dynamicCalls.length; i++) {
|
|
1755
|
+
const call = this._dynamicCalls[i];
|
|
1756
|
+
this._copyToData(eventName, data, call());
|
|
1757
|
+
}
|
|
1758
|
+
this._copyToData(eventName, data, this._staticProperties);
|
|
1759
|
+
if (this._timeEvents && this._timeEvents.has(eventName)) {
|
|
1760
|
+
let timeInfo = this._timeEvents.get(eventName);
|
|
1761
|
+
let duration = Math.floor((App.durationOfLaunch - timeInfo.start + timeInfo.offset) / 1000 * 100) / 100;
|
|
1762
|
+
data[InternalProperty.DURATION] = duration;
|
|
1763
|
+
timeInfo.once && this._timeEvents.delete(eventName);
|
|
1764
|
+
}
|
|
1765
|
+
this._debugMode && console.log("[Analytics]", eventName, JSON.stringify(data));
|
|
1766
|
+
return this._sendReport(eventName, data);
|
|
1767
|
+
});
|
|
1811
1768
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
this.recover(element);
|
|
1769
|
+
_sendAble(eventName) {
|
|
1770
|
+
for (let i = 0; i < this._senderList.length; i++) {
|
|
1771
|
+
const sender = this._senderList[i];
|
|
1772
|
+
if (sender.sendAble(eventName)) {
|
|
1773
|
+
return true;
|
|
1774
|
+
}
|
|
1819
1775
|
}
|
|
1776
|
+
return false;
|
|
1820
1777
|
}
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1778
|
+
_copyToData(eventName, data, target) {
|
|
1779
|
+
for (const key in target) {
|
|
1780
|
+
if (Object.prototype.hasOwnProperty.call(target, key)) {
|
|
1781
|
+
const value = target[key];
|
|
1782
|
+
if (data[key] == undefined) {
|
|
1783
|
+
data[key] = value;
|
|
1784
|
+
}
|
|
1785
|
+
else {
|
|
1786
|
+
console.warn(`[Analytics] 事件${eventName}存在重复属性${key}=${data[key]}`);
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1826
1789
|
}
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
this
|
|
1830
|
-
|
|
1790
|
+
}
|
|
1791
|
+
_sendReport(eventName, data) {
|
|
1792
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1793
|
+
const userProperties = this._getUserProperties();
|
|
1794
|
+
return Promise.all(this._senderList
|
|
1795
|
+
.filter((sender) => sender.sendAble(eventName))
|
|
1796
|
+
.map((sender) => {
|
|
1797
|
+
const senderData = Object.assign({}, data);
|
|
1798
|
+
if (!this._usesSenderUserProperties(sender)) {
|
|
1799
|
+
this._copyToData(eventName, senderData, userProperties);
|
|
1800
|
+
}
|
|
1801
|
+
return sender.send(eventName, senderData);
|
|
1802
|
+
})).then();
|
|
1803
|
+
});
|
|
1831
1804
|
}
|
|
1832
1805
|
}
|
|
1833
1806
|
|
|
1807
|
+
const ALI_ANALYTICS_EVENT_CACHE = "ALI_ANALYTICS_EVENT_CACHE";
|
|
1834
1808
|
/**
|
|
1835
|
-
*
|
|
1836
|
-
* <p>推荐使用 Handler.create() 方法从对象池创建,减少对象创建消耗。创建的 Handler 对象不再使用后,可以使用 Handler.recover() 将其回收到对象池,回收后不要再使用此对象,否则会导致不可预料的错误。</p>
|
|
1837
|
-
* <p><b>注意:</b>由于鼠标事件也用本对象池,不正确的回收及调用,可能会影响鼠标事件的执行。</p>
|
|
1809
|
+
* 内部阿里云日志事件上报器
|
|
1838
1810
|
*/
|
|
1839
|
-
class
|
|
1811
|
+
class ALiAnalyticsSender extends EmptyAnalyticsSender {
|
|
1840
1812
|
/**
|
|
1841
|
-
*
|
|
1842
|
-
* @param
|
|
1843
|
-
* @param
|
|
1844
|
-
* @param
|
|
1845
|
-
* @param
|
|
1813
|
+
*
|
|
1814
|
+
* @param project
|
|
1815
|
+
* @param logstore
|
|
1816
|
+
* @param host
|
|
1817
|
+
* @param exclude
|
|
1818
|
+
* @param cd
|
|
1819
|
+
* @param count
|
|
1846
1820
|
*/
|
|
1847
|
-
constructor(
|
|
1848
|
-
|
|
1849
|
-
this.
|
|
1850
|
-
|
|
1851
|
-
this.
|
|
1852
|
-
this.
|
|
1821
|
+
constructor(config, exclude) {
|
|
1822
|
+
super(exclude);
|
|
1823
|
+
this._eventCache = [];
|
|
1824
|
+
this._index = 0;
|
|
1825
|
+
this._storageKey = ALI_ANALYTICS_EVENT_CACHE;
|
|
1826
|
+
this._index = 0;
|
|
1827
|
+
this._setConfig(config);
|
|
1828
|
+
this._eventCache = this._loadStoredEvents();
|
|
1829
|
+
this._tracker = window['SLS_Tracker'] && new window['SLS_Tracker']({
|
|
1830
|
+
host: config.host || "cn-hangzhou.log.aliyuncs.com",
|
|
1831
|
+
project: config.project,
|
|
1832
|
+
logstore: config.logstore,
|
|
1833
|
+
time: config.cd,
|
|
1834
|
+
count: config.count, // 定义数据条数,默认是10条,number类型,选填
|
|
1835
|
+
});
|
|
1836
|
+
this._addListen();
|
|
1837
|
+
this._startLoop();
|
|
1838
|
+
this._doReport();
|
|
1853
1839
|
}
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
* @param args 携带的参数。
|
|
1859
|
-
* @param once 是否只执行一次,如果为true,执行后执行recover()进行回收。
|
|
1860
|
-
* @return 返回 handler 本身。
|
|
1861
|
-
*/
|
|
1862
|
-
setTo(caller, method, args, once = false) {
|
|
1863
|
-
this._id = Handler._gid++;
|
|
1864
|
-
this.caller = caller;
|
|
1865
|
-
this.method = method;
|
|
1866
|
-
this.args = args;
|
|
1867
|
-
this.once = once;
|
|
1868
|
-
return this;
|
|
1840
|
+
_setConfig(config) {
|
|
1841
|
+
config.cd = config.cd || 2;
|
|
1842
|
+
config.count = config.count || 5;
|
|
1843
|
+
this._config = config;
|
|
1869
1844
|
}
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
var result = this.method.apply(this.caller, this.args);
|
|
1878
|
-
this._id === id && this.once && this.recover();
|
|
1879
|
-
return result;
|
|
1880
|
-
}
|
|
1881
|
-
/**
|
|
1882
|
-
* 执行处理器,并携带额外数据。
|
|
1883
|
-
* @param data 附加的回调数据,可以是单数据或者Array(作为多参)。
|
|
1884
|
-
*/
|
|
1885
|
-
runWith(data) {
|
|
1886
|
-
if (this.method == null)
|
|
1887
|
-
return null;
|
|
1888
|
-
var id = this._id;
|
|
1889
|
-
if (data == null)
|
|
1890
|
-
var result = this.method.apply(this.caller, this.args);
|
|
1891
|
-
else if (!this.args && !data.unshift)
|
|
1892
|
-
result = this.method.call(this.caller, data);
|
|
1893
|
-
else if (this.args)
|
|
1894
|
-
result = this.method.apply(this.caller, this.args.concat(data));
|
|
1895
|
-
else
|
|
1896
|
-
result = this.method.apply(this.caller, data);
|
|
1897
|
-
this._id === id && this.once && this.recover();
|
|
1898
|
-
return result;
|
|
1845
|
+
_loadStoredEvents() {
|
|
1846
|
+
const storedValue = storage.getGValue(this._storageKey, []);
|
|
1847
|
+
const events = Array.isArray(storedValue) ? storedValue : [];
|
|
1848
|
+
if (events.length > 0) {
|
|
1849
|
+
storage.removeGValue(this._storageKey);
|
|
1850
|
+
}
|
|
1851
|
+
return events;
|
|
1899
1852
|
}
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
this.
|
|
1906
|
-
this.args = null;
|
|
1907
|
-
return this;
|
|
1853
|
+
_storeEvents(events) {
|
|
1854
|
+
if (!events.length) {
|
|
1855
|
+
storage.removeGValue(this._storageKey);
|
|
1856
|
+
return;
|
|
1857
|
+
}
|
|
1858
|
+
storage.setGValue(this._storageKey, events);
|
|
1908
1859
|
}
|
|
1909
|
-
|
|
1910
|
-
this.
|
|
1860
|
+
_startLoop() {
|
|
1861
|
+
timer.loop((this._config.cd || 2) * 1000, this, this._doReport, [this._config.count]);
|
|
1911
1862
|
}
|
|
1912
|
-
|
|
1913
|
-
|
|
1863
|
+
_addListen() {
|
|
1864
|
+
App.onHide(() => {
|
|
1865
|
+
this._doReport(this._eventCache.length, true);
|
|
1866
|
+
});
|
|
1867
|
+
App.onShow(this._restoreEvents, this);
|
|
1914
1868
|
}
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
this._id = 0;
|
|
1921
|
-
Handler._pool.recover(this);
|
|
1869
|
+
_restoreEvents() {
|
|
1870
|
+
const eventsInLocalStore = this._loadStoredEvents();
|
|
1871
|
+
if (eventsInLocalStore.length > 0) {
|
|
1872
|
+
this._eventCache.unshift(...eventsInLocalStore);
|
|
1873
|
+
console.log(`[ALiAnalyticsSender] 恢复本地events(length:${eventsInLocalStore.length}, total:${this._eventCache.length})`);
|
|
1922
1874
|
}
|
|
1923
1875
|
}
|
|
1924
|
-
|
|
1925
|
-
|
|
1876
|
+
_doReport(maxCount = 5, store = false) {
|
|
1877
|
+
if (this._eventCache.length <= 0) {
|
|
1878
|
+
return false;
|
|
1879
|
+
}
|
|
1880
|
+
if (!this._tracker) {
|
|
1881
|
+
return false;
|
|
1882
|
+
}
|
|
1883
|
+
const events = this._eventCache.splice(0, Math.min(this._eventCache.length, maxCount));
|
|
1884
|
+
try {
|
|
1885
|
+
this._tracker.sendBatchLogsImmediate(events);
|
|
1886
|
+
return true;
|
|
1887
|
+
}
|
|
1888
|
+
catch (e) {
|
|
1889
|
+
if (!store) {
|
|
1890
|
+
this._eventCache.unshift(...events);
|
|
1891
|
+
console.log(`[ALiAnalyticsSender] 事件发送失败,恢复事件到队列,等待下次发送。`);
|
|
1892
|
+
}
|
|
1893
|
+
else {
|
|
1894
|
+
this._storeEvents([...events, ...this._eventCache]);
|
|
1895
|
+
this._eventCache.length = 0;
|
|
1896
|
+
console.log(`[ALiAnalyticsSender] 事件发送失败,存储到缓存,等待下次切回游戏。`);
|
|
1897
|
+
}
|
|
1898
|
+
return false;
|
|
1899
|
+
}
|
|
1926
1900
|
}
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1901
|
+
send(eventName, data) {
|
|
1902
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1903
|
+
const payload = data && typeof data === "object" && !Array.isArray(data)
|
|
1904
|
+
? Object.assign({}, data) : { value: data };
|
|
1905
|
+
payload['_event'] = eventName;
|
|
1906
|
+
let msg_data = {
|
|
1907
|
+
appid: publisher.appId,
|
|
1908
|
+
openid: storage.userId || publisher.login.openID || "",
|
|
1909
|
+
deviceid: App.deviceId,
|
|
1910
|
+
sessionid: App.sessionId,
|
|
1911
|
+
level: ++this._index,
|
|
1912
|
+
data: JSON.stringify(payload),
|
|
1913
|
+
event: eventName,
|
|
1914
|
+
timestamp: this._config.timestampCall() || Date.now()
|
|
1915
|
+
};
|
|
1916
|
+
this._eventCache.push(msg_data);
|
|
1917
|
+
});
|
|
1937
1918
|
}
|
|
1938
1919
|
}
|
|
1939
|
-
/**@private handler对象池*/
|
|
1940
|
-
Handler._pool = new Pool(Handler);
|
|
1941
|
-
/**@private */
|
|
1942
|
-
Handler._gid = 1;
|
|
1943
1920
|
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
1953
|
-
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1921
|
+
var DimensionType;
|
|
1922
|
+
(function (DimensionType) {
|
|
1923
|
+
DimensionType[DimensionType["ACTIVITY"] = 0] = "ACTIVITY";
|
|
1924
|
+
DimensionType[DimensionType["AD"] = 1] = "AD";
|
|
1925
|
+
DimensionType[DimensionType["PAYMENT"] = 2] = "PAYMENT";
|
|
1926
|
+
DimensionType[DimensionType["SOCIAL"] = 3] = "SOCIAL";
|
|
1927
|
+
DimensionType[DimensionType["PLAYER_ABILITY"] = 4] = "PLAYER_ABILITY";
|
|
1928
|
+
DimensionType[DimensionType["LEVEL_STATS"] = 5] = "LEVEL_STATS";
|
|
1929
|
+
})(DimensionType || (DimensionType = {}));
|
|
1930
|
+
|
|
1931
|
+
class BaseDimension {
|
|
1932
|
+
constructor(name) {
|
|
1933
|
+
this._data = {};
|
|
1934
|
+
this._initialized = false;
|
|
1935
|
+
this._dirty = false;
|
|
1936
|
+
this._name = name;
|
|
1957
1937
|
}
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
1961
|
-
|
|
1962
|
-
|
|
1963
|
-
|
|
1964
|
-
|
|
1965
|
-
if (!this._events || !this._events[type])
|
|
1966
|
-
return false;
|
|
1967
|
-
var listeners = this._events[type];
|
|
1968
|
-
if (listeners.run) {
|
|
1969
|
-
if (listeners.once)
|
|
1970
|
-
delete this._events[type];
|
|
1971
|
-
data != null ? listeners.runWith(data) : listeners.run();
|
|
1938
|
+
get name() {
|
|
1939
|
+
return this._name;
|
|
1940
|
+
}
|
|
1941
|
+
_ensureInit() {
|
|
1942
|
+
if (!this._initialized) {
|
|
1943
|
+
this._loadFromStorage();
|
|
1944
|
+
this._initialized = true;
|
|
1972
1945
|
}
|
|
1973
|
-
|
|
1974
|
-
|
|
1975
|
-
|
|
1976
|
-
|
|
1977
|
-
|
|
1978
|
-
|
|
1979
|
-
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
|
|
1983
|
-
}
|
|
1946
|
+
}
|
|
1947
|
+
_loadFromStorage() {
|
|
1948
|
+
const key = `dim_${this._name}`;
|
|
1949
|
+
const data = storage.getUValue(key, "");
|
|
1950
|
+
if (data) {
|
|
1951
|
+
try {
|
|
1952
|
+
this._data = this._decompress(data);
|
|
1953
|
+
}
|
|
1954
|
+
catch (e) {
|
|
1955
|
+
console.error(`加载维度${this._name}失败:`, e);
|
|
1956
|
+
this._data = {};
|
|
1984
1957
|
}
|
|
1985
|
-
if (listeners.length === 0 && this._events)
|
|
1986
|
-
delete this._events[type];
|
|
1987
1958
|
}
|
|
1988
|
-
return true;
|
|
1989
|
-
}
|
|
1990
|
-
/**
|
|
1991
|
-
* 使用 EventDispatcher 对象注册指定类型的事件侦听器对象,以使侦听器能够接收事件通知。
|
|
1992
|
-
* @param type 事件的类型。
|
|
1993
|
-
* @param caller 事件侦听函数的执行域。
|
|
1994
|
-
* @param listener 事件侦听函数。
|
|
1995
|
-
* @param args (可选)事件侦听函数的回调参数。
|
|
1996
|
-
* @return 此 EventDispatcher 对象。
|
|
1997
|
-
*/
|
|
1998
|
-
on(type, caller, listener, args = null) {
|
|
1999
|
-
return this._createListener(type, caller, listener, args, false);
|
|
2000
1959
|
}
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
return this._createListener(type, caller, listener, args, true);
|
|
1960
|
+
_saveToStorage() {
|
|
1961
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1962
|
+
if (!this._dirty)
|
|
1963
|
+
return;
|
|
1964
|
+
const key = `dim_${this._name}`;
|
|
1965
|
+
const compressed = this._compress(this._data);
|
|
1966
|
+
storage.setUValue(key, compressed);
|
|
1967
|
+
this._dirty = false;
|
|
1968
|
+
});
|
|
2011
1969
|
}
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
var handler = Handler.create(caller || this, listener, args, once);
|
|
2018
|
-
this._events || (this._events = {});
|
|
2019
|
-
var events = this._events;
|
|
2020
|
-
//默认单个,每个对象只有多个监听才用数组,节省一个数组的消耗
|
|
2021
|
-
if (!events[type])
|
|
2022
|
-
events[type] = handler;
|
|
2023
|
-
else {
|
|
2024
|
-
if (!events[type].run)
|
|
2025
|
-
events[type].push(handler);
|
|
2026
|
-
else
|
|
2027
|
-
events[type] = [events[type], handler];
|
|
2028
|
-
}
|
|
2029
|
-
return this;
|
|
1970
|
+
_updateValue(key, value) {
|
|
1971
|
+
this._ensureInit();
|
|
1972
|
+
this._data[key] = value;
|
|
1973
|
+
this._dirty = true;
|
|
1974
|
+
this._saveToStorage();
|
|
2030
1975
|
}
|
|
2031
|
-
|
|
2032
|
-
|
|
1976
|
+
_incrementValue(key, delta = 1) {
|
|
1977
|
+
this._ensureInit();
|
|
1978
|
+
this._data[key] = (this._data[key] || 0) + delta;
|
|
1979
|
+
this._dirty = true;
|
|
1980
|
+
this._saveToStorage();
|
|
1981
|
+
}
|
|
1982
|
+
_getValue(key, defaultValue = 0) {
|
|
1983
|
+
var _a;
|
|
1984
|
+
this._ensureInit();
|
|
1985
|
+
return (_a = this._data[key]) !== null && _a !== void 0 ? _a : defaultValue;
|
|
1986
|
+
}
|
|
1987
|
+
getData() {
|
|
1988
|
+
this._ensureInit();
|
|
1989
|
+
return Object.assign({}, this._data);
|
|
1990
|
+
}
|
|
1991
|
+
_compress(data) {
|
|
1992
|
+
return Object.entries(data)
|
|
1993
|
+
.map(([k, v]) => `${k}:${v}`)
|
|
1994
|
+
.join(',');
|
|
1995
|
+
}
|
|
1996
|
+
_decompress(str) {
|
|
1997
|
+
const data = {};
|
|
1998
|
+
if (!str)
|
|
1999
|
+
return data;
|
|
2000
|
+
str.split(',').forEach(pair => {
|
|
2001
|
+
const [k, v] = pair.split(':');
|
|
2002
|
+
if (k && v !== undefined) {
|
|
2003
|
+
data[k] = Number(v);
|
|
2004
|
+
}
|
|
2005
|
+
});
|
|
2006
|
+
return data;
|
|
2007
|
+
}
|
|
2008
|
+
clear() {
|
|
2009
|
+
this._data = {};
|
|
2010
|
+
this._dirty = true;
|
|
2011
|
+
this._saveToStorage();
|
|
2012
|
+
}
|
|
2013
|
+
}
|
|
2014
|
+
|
|
2015
|
+
var AdDataKey;
|
|
2016
|
+
(function (AdDataKey) {
|
|
2017
|
+
// 累计展示次数
|
|
2018
|
+
AdDataKey["SHOW_COUNT"] = "sc";
|
|
2019
|
+
// 展示天数
|
|
2020
|
+
AdDataKey["SHOW_DAYS"] = "sd";
|
|
2021
|
+
// 累计完成次数
|
|
2022
|
+
AdDataKey["COMPLETE_COUNT"] = "cpc";
|
|
2023
|
+
// 最后展示天数
|
|
2024
|
+
AdDataKey["LAST_SHOW_DAY"] = "lsd";
|
|
2025
|
+
// 今日展示次数
|
|
2026
|
+
AdDataKey["TODAY_SHOW_COUNT"] = "tsc";
|
|
2027
|
+
// 首次展示时间(相对安装时间,秒)
|
|
2028
|
+
AdDataKey["FIRST_SHOW_TIME"] = "fst";
|
|
2029
|
+
// 上次展示时间(相对安装时间,秒)
|
|
2030
|
+
AdDataKey["LAST_SHOW_TIME"] = "lst";
|
|
2031
|
+
// 平均展示间隔(秒)
|
|
2032
|
+
AdDataKey["AVG_SHOW_INTERVAL"] = "asi";
|
|
2033
|
+
// 今日平均展示间隔(秒)
|
|
2034
|
+
AdDataKey["TODAY_AVG_SHOW_INTERVAL"] = "tasi";
|
|
2035
|
+
})(AdDataKey || (AdDataKey = {}));
|
|
2036
|
+
const AD_METRIC_ORDER_V1 = [
|
|
2037
|
+
'AD.show_count',
|
|
2038
|
+
'AD.show_days',
|
|
2039
|
+
'AD.complete_count',
|
|
2040
|
+
'AD.last_show_day',
|
|
2041
|
+
'AD.today_show_count',
|
|
2042
|
+
'AD.first_show_time',
|
|
2043
|
+
'AD.last_show_time',
|
|
2044
|
+
'AD.avg_show_interval',
|
|
2045
|
+
'AD.today_avg_show_interval',
|
|
2046
|
+
'AD.avg_show_per_day',
|
|
2047
|
+
'AD.avg_complete_per_day',
|
|
2048
|
+
];
|
|
2049
|
+
/**
|
|
2050
|
+
* 广告维度,记录广告展示次数、完成次数、展示天数、展示间隔等信息
|
|
2051
|
+
*/
|
|
2052
|
+
class AdDimension extends BaseDimension {
|
|
2053
|
+
constructor() {
|
|
2054
|
+
super(DimensionType[DimensionType.AD]);
|
|
2055
|
+
this._todayPrevShowTime = 0; // 内存中记录今日上次展示时间(相对启动),用于计算今日间隔
|
|
2056
|
+
this._checkAndResetDailyData();
|
|
2057
|
+
}
|
|
2058
|
+
/**
|
|
2059
|
+
* 检测并重置今日数据(仅在新的一天的新启动时)
|
|
2060
|
+
*/
|
|
2061
|
+
_checkAndResetDailyData() {
|
|
2062
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
2063
|
+
this._ensureInit();
|
|
2064
|
+
const today = App.daysInstall;
|
|
2065
|
+
const lastDay = this._getValue(AdDataKey.LAST_SHOW_DAY, -1);
|
|
2066
|
+
// 只有在新的一天才重置今日数据
|
|
2067
|
+
if (lastDay !== -1 && lastDay !== today) {
|
|
2068
|
+
this._updateValue(AdDataKey.TODAY_SHOW_COUNT, 0);
|
|
2069
|
+
this._updateValue(AdDataKey.TODAY_AVG_SHOW_INTERVAL, 0);
|
|
2070
|
+
this._todayPrevShowTime = 0;
|
|
2071
|
+
}
|
|
2072
|
+
});
|
|
2073
|
+
}
|
|
2074
|
+
recordShow() {
|
|
2075
|
+
const showCount = this._getValue(AdDataKey.SHOW_COUNT, 0);
|
|
2076
|
+
const prevShowTime = this._getValue(AdDataKey.LAST_SHOW_TIME, 0);
|
|
2077
|
+
this._incrementValue(AdDataKey.SHOW_COUNT);
|
|
2078
|
+
this._incrementValue(AdDataKey.TODAY_SHOW_COUNT);
|
|
2079
|
+
const today = App.daysInstall;
|
|
2080
|
+
const lastDay = this._getValue(AdDataKey.LAST_SHOW_DAY, -1);
|
|
2081
|
+
// 更新展示天数和最后展示天数
|
|
2082
|
+
if (lastDay !== today) {
|
|
2083
|
+
this._incrementValue(AdDataKey.SHOW_DAYS);
|
|
2084
|
+
this._updateValue(AdDataKey.LAST_SHOW_DAY, today);
|
|
2085
|
+
}
|
|
2086
|
+
// 记录当前展示时间(相对安装时间)
|
|
2087
|
+
const currentTime = Math.floor(App.durationOfInstall / 1000);
|
|
2088
|
+
const currentLaunchTime = Math.floor(App.durationOfLaunch / 1000);
|
|
2089
|
+
// 计算总体平均间隔(从第二次展示开始)
|
|
2090
|
+
if (showCount > 0 && prevShowTime > 0) {
|
|
2091
|
+
const interval = currentTime - prevShowTime;
|
|
2092
|
+
const oldAvg = this._getValue(AdDataKey.AVG_SHOW_INTERVAL, 0);
|
|
2093
|
+
// 增量平均公式: 新平均 = 旧平均 + (本次值 - 旧平均) / 新的数据个数
|
|
2094
|
+
const newAvg = Math.floor(oldAvg + (interval - oldAvg) / showCount);
|
|
2095
|
+
this._updateValue(AdDataKey.AVG_SHOW_INTERVAL, newAvg);
|
|
2096
|
+
}
|
|
2097
|
+
// 计算今日平均间隔(从今日第二次展示开始)
|
|
2098
|
+
const todayShowCount = this._getValue(AdDataKey.TODAY_SHOW_COUNT, 0);
|
|
2099
|
+
if (todayShowCount > 1 && this._todayPrevShowTime > 0) {
|
|
2100
|
+
const todayInterval = currentLaunchTime - this._todayPrevShowTime;
|
|
2101
|
+
const oldTodayAvg = this._getValue(AdDataKey.TODAY_AVG_SHOW_INTERVAL, 0);
|
|
2102
|
+
// 今日间隔数 = 今日展示次数 - 1
|
|
2103
|
+
const newTodayAvg = Math.floor(oldTodayAvg + (todayInterval - oldTodayAvg) / (todayShowCount - 1));
|
|
2104
|
+
this._updateValue(AdDataKey.TODAY_AVG_SHOW_INTERVAL, newTodayAvg);
|
|
2105
|
+
}
|
|
2106
|
+
// 记录首次展示时间(仅首次)
|
|
2107
|
+
if (showCount === 0) {
|
|
2108
|
+
this._updateValue(AdDataKey.FIRST_SHOW_TIME, currentTime);
|
|
2109
|
+
}
|
|
2110
|
+
// 更新时间记录
|
|
2111
|
+
this._updateValue(AdDataKey.LAST_SHOW_TIME, currentTime);
|
|
2112
|
+
this._todayPrevShowTime = currentLaunchTime;
|
|
2113
|
+
}
|
|
2114
|
+
recordComplete() {
|
|
2115
|
+
this._incrementValue(AdDataKey.COMPLETE_COUNT);
|
|
2116
|
+
}
|
|
2117
|
+
/**
|
|
2118
|
+
* 获取日均展示次数
|
|
2119
|
+
*/
|
|
2120
|
+
get avgShowPerDay() {
|
|
2121
|
+
const total = this._getValue(AdDataKey.SHOW_COUNT, 0);
|
|
2122
|
+
const days = this._getValue(AdDataKey.SHOW_DAYS, 0);
|
|
2123
|
+
return days > 0 ? Math.floor(total / days) : 0;
|
|
2124
|
+
}
|
|
2125
|
+
/**
|
|
2126
|
+
* 获取日均完成次数
|
|
2127
|
+
*/
|
|
2128
|
+
get avgCompletePerDay() {
|
|
2129
|
+
const total = this._getValue(AdDataKey.COMPLETE_COUNT, 0);
|
|
2130
|
+
const days = this._getValue(AdDataKey.SHOW_DAYS, 0);
|
|
2131
|
+
return days > 0 ? Math.floor(total / days) : 0;
|
|
2132
|
+
}
|
|
2133
|
+
/**
|
|
2134
|
+
* 获取平均广告展示间隔(秒)
|
|
2135
|
+
* 使用增量平均算法计算相邻两次展示的平均间隔
|
|
2136
|
+
*/
|
|
2137
|
+
get avgShowInterval() {
|
|
2138
|
+
return this._getValue(AdDataKey.AVG_SHOW_INTERVAL, 0);
|
|
2139
|
+
}
|
|
2140
|
+
/**
|
|
2141
|
+
* 获取今日平均广告展示间隔(秒)
|
|
2142
|
+
* 使用增量平均算法计算今日相邻两次展示的平均间隔
|
|
2143
|
+
*/
|
|
2144
|
+
get todayAvgShowInterval() {
|
|
2145
|
+
return this._getValue(AdDataKey.TODAY_AVG_SHOW_INTERVAL, 0);
|
|
2146
|
+
}
|
|
2147
|
+
getMetrics(order = [
|
|
2148
|
+
AdDataKey.SHOW_COUNT,
|
|
2149
|
+
AdDataKey.SHOW_DAYS,
|
|
2150
|
+
AdDataKey.COMPLETE_COUNT,
|
|
2151
|
+
AdDataKey.LAST_SHOW_DAY,
|
|
2152
|
+
AdDataKey.TODAY_SHOW_COUNT,
|
|
2153
|
+
AdDataKey.FIRST_SHOW_TIME,
|
|
2154
|
+
AdDataKey.LAST_SHOW_TIME,
|
|
2155
|
+
AdDataKey.AVG_SHOW_INTERVAL,
|
|
2156
|
+
AdDataKey.TODAY_AVG_SHOW_INTERVAL
|
|
2157
|
+
]) {
|
|
2158
|
+
this._ensureInit();
|
|
2159
|
+
const result = [];
|
|
2160
|
+
for (const key of order) {
|
|
2161
|
+
result.push(this._getValue(key, 0));
|
|
2162
|
+
}
|
|
2163
|
+
result.push(this.avgShowPerDay);
|
|
2164
|
+
result.push(this.avgCompletePerDay);
|
|
2165
|
+
return result;
|
|
2166
|
+
}
|
|
2167
|
+
}
|
|
2168
|
+
|
|
2169
|
+
var ADType;
|
|
2170
|
+
(function (ADType) {
|
|
2171
|
+
ADType[ADType["BANNER"] = 0] = "BANNER";
|
|
2172
|
+
ADType[ADType["INTERSTITIAL"] = 1] = "INTERSTITIAL";
|
|
2173
|
+
ADType[ADType["REWARD_VIDEO"] = 2] = "REWARD_VIDEO";
|
|
2174
|
+
ADType[ADType["NATIVE"] = 3] = "NATIVE";
|
|
2175
|
+
})(ADType || (ADType = {}));
|
|
2176
|
+
var ADEvent;
|
|
2177
|
+
(function (ADEvent) {
|
|
2178
|
+
ADEvent[ADEvent["LOAD"] = 0] = "LOAD";
|
|
2179
|
+
ADEvent[ADEvent["LOAD_COMPLETE"] = 1] = "LOAD_COMPLETE";
|
|
2180
|
+
ADEvent[ADEvent["LOAD_FAIL"] = 2] = "LOAD_FAIL";
|
|
2181
|
+
ADEvent[ADEvent["SHOW"] = 3] = "SHOW";
|
|
2182
|
+
ADEvent[ADEvent["SHOW_COMPLETE"] = 4] = "SHOW_COMPLETE";
|
|
2183
|
+
ADEvent[ADEvent["SHOW_FAIL"] = 5] = "SHOW_FAIL";
|
|
2184
|
+
ADEvent[ADEvent["CLOSE_BY_USER"] = 6] = "CLOSE_BY_USER";
|
|
2185
|
+
})(ADEvent || (ADEvent = {}));
|
|
2186
|
+
class ADBehaviorReporter {
|
|
2187
|
+
constructor(baseAble) {
|
|
2188
|
+
this.baseAble = baseAble;
|
|
2189
|
+
this._groupCount = {};
|
|
2190
|
+
}
|
|
2191
|
+
/**
|
|
2192
|
+
* 重置单个模块的广告计数
|
|
2193
|
+
* @param group 模块名称,默认为game
|
|
2194
|
+
*/
|
|
2195
|
+
resetGroupCount(group = "game") {
|
|
2196
|
+
this._groupCount[group] = 0;
|
|
2197
|
+
}
|
|
2198
|
+
/**
|
|
2199
|
+
* 开始加载广告
|
|
2200
|
+
* @param type 广告类型
|
|
2201
|
+
* @param from 广告来源
|
|
2202
|
+
* @param msg 广告失败原因
|
|
2203
|
+
*/
|
|
2204
|
+
onStartLoad(type, from, msg) {
|
|
2205
|
+
this.report(type, ADEvent.LOAD, from, msg);
|
|
2206
|
+
}
|
|
2207
|
+
/**
|
|
2208
|
+
* 广告加载完成
|
|
2209
|
+
* @param type 广告类型
|
|
2210
|
+
* @param from 广告来源
|
|
2211
|
+
*/
|
|
2212
|
+
onLoadComplete(type, from) {
|
|
2213
|
+
this.report(type, ADEvent.LOAD_COMPLETE, from);
|
|
2214
|
+
}
|
|
2215
|
+
/**
|
|
2216
|
+
* 广告加载失败
|
|
2217
|
+
* @param type 广告类型
|
|
2218
|
+
* @param from 广告来源
|
|
2219
|
+
* @param msg 广告失败原因
|
|
2220
|
+
*/
|
|
2221
|
+
onLoadFail(type, from, msg) {
|
|
2222
|
+
this.report(type, ADEvent.LOAD_FAIL, from, msg);
|
|
2223
|
+
}
|
|
2224
|
+
/**
|
|
2225
|
+
* 显示广告
|
|
2226
|
+
* @param type 广告类型
|
|
2227
|
+
* @param from 广告来源
|
|
2228
|
+
*/
|
|
2229
|
+
onShow(type, from) {
|
|
2230
|
+
this.report(type, ADEvent.SHOW, from);
|
|
2231
|
+
}
|
|
2232
|
+
/**
|
|
2233
|
+
* 广告显示完成
|
|
2234
|
+
* @param type 广告类型
|
|
2235
|
+
* @param from 广告来源
|
|
2236
|
+
*/
|
|
2237
|
+
onShowComplete(type, from) {
|
|
2238
|
+
this.report(type, ADEvent.SHOW_COMPLETE, from);
|
|
2239
|
+
}
|
|
2240
|
+
/**
|
|
2241
|
+
* 广告显示失败
|
|
2242
|
+
* @param type 广告类型
|
|
2243
|
+
* @param from 广告来源
|
|
2244
|
+
* @param msg 广告失败原因
|
|
2245
|
+
*/
|
|
2246
|
+
onShowFail(type, from, msg) {
|
|
2247
|
+
this.report(type, ADEvent.SHOW_FAIL, from, msg);
|
|
2248
|
+
}
|
|
2249
|
+
/**
|
|
2250
|
+
* 用户关闭广告
|
|
2251
|
+
* @param type 广告类型
|
|
2252
|
+
* @param from 广告来源
|
|
2253
|
+
*/
|
|
2254
|
+
onCloseByUser(type, from) {
|
|
2255
|
+
this.report(type, ADEvent.CLOSE_BY_USER, from);
|
|
2256
|
+
}
|
|
2257
|
+
/**
|
|
2258
|
+
* 上报广告事件
|
|
2259
|
+
* @param type 广告类型
|
|
2260
|
+
* @param event 广告事件
|
|
2261
|
+
* @param from 广告来源
|
|
2262
|
+
* @param msg 广告失败原因
|
|
2263
|
+
*/
|
|
2264
|
+
report(type, event, from, msg) {
|
|
2265
|
+
from = from || {};
|
|
2266
|
+
const group = from.group || "game";
|
|
2267
|
+
from.group && delete from.group;
|
|
2268
|
+
from._adtype = type;
|
|
2269
|
+
from._adevent = event;
|
|
2270
|
+
if (!this._groupCount[group]) {
|
|
2271
|
+
this._groupCount[group] = 0;
|
|
2272
|
+
}
|
|
2273
|
+
from.count = event == ADEvent.SHOW_COMPLETE ? ++this._groupCount[group] : this._groupCount[group];
|
|
2274
|
+
if (msg) {
|
|
2275
|
+
from.msg = msg;
|
|
2276
|
+
}
|
|
2277
|
+
this.baseAble.report("ad", from);
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
2280
|
+
|
|
2281
|
+
/**
|
|
2282
|
+
* <code>EventDispatcher</code> 类是可调度事件的所有类的基类。
|
|
2283
|
+
*/
|
|
2284
|
+
class EventDispatcher {
|
|
2285
|
+
//[IF-JS]Object.defineProperty(EventDispatcher.prototype, "_events", {enumerable: false,writable:true});
|
|
2286
|
+
/**
|
|
2287
|
+
* 检查 EventDispatcher 对象是否为特定事件类型注册了任何侦听器。
|
|
2288
|
+
* @param type 事件的类型。
|
|
2289
|
+
* @return 如果指定类型的侦听器已注册,则值为 true;否则,值为 false。
|
|
2290
|
+
*/
|
|
2291
|
+
hasListener(type) {
|
|
2292
|
+
var listener = this._events && this._events[type];
|
|
2293
|
+
return !!listener;
|
|
2294
|
+
}
|
|
2295
|
+
/**
|
|
2296
|
+
* 派发事件。
|
|
2297
|
+
* @param type 事件类型。
|
|
2298
|
+
* @param data (可选)回调数据。<b>注意:</b>如果是需要传递多个参数 p1,p2,p3,...可以使用数组结构如:[p1,p2,p3,...] ;如果需要回调单个参数 p ,且 p 是一个数组,则需要使用结构如:[p],其他的单个参数 p ,可以直接传入参数 p。
|
|
2299
|
+
* @return 此事件类型是否有侦听者,如果有侦听者则值为 true,否则值为 false。
|
|
2300
|
+
*/
|
|
2301
|
+
event(type, data = null) {
|
|
2302
|
+
if (!this._events || !this._events[type])
|
|
2303
|
+
return false;
|
|
2304
|
+
var listeners = this._events[type];
|
|
2305
|
+
if (listeners.run) {
|
|
2306
|
+
if (listeners.once)
|
|
2307
|
+
delete this._events[type];
|
|
2308
|
+
data != null ? listeners.runWith(data) : listeners.run();
|
|
2309
|
+
}
|
|
2310
|
+
else {
|
|
2311
|
+
for (var i = 0, n = listeners.length; i < n; i++) {
|
|
2312
|
+
var listener = listeners[i];
|
|
2313
|
+
if (listener) {
|
|
2314
|
+
(data != null) ? listener.runWith(data) : listener.run();
|
|
2315
|
+
}
|
|
2316
|
+
if (!listener || listener.once) {
|
|
2317
|
+
listeners.splice(i, 1);
|
|
2318
|
+
i--;
|
|
2319
|
+
n--;
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
if (listeners.length === 0 && this._events)
|
|
2323
|
+
delete this._events[type];
|
|
2324
|
+
}
|
|
2325
|
+
return true;
|
|
2326
|
+
}
|
|
2327
|
+
/**
|
|
2328
|
+
* 使用 EventDispatcher 对象注册指定类型的事件侦听器对象,以使侦听器能够接收事件通知。
|
|
2329
|
+
* @param type 事件的类型。
|
|
2330
|
+
* @param caller 事件侦听函数的执行域。
|
|
2331
|
+
* @param listener 事件侦听函数。
|
|
2332
|
+
* @param args (可选)事件侦听函数的回调参数。
|
|
2333
|
+
* @return 此 EventDispatcher 对象。
|
|
2334
|
+
*/
|
|
2335
|
+
on(type, caller, listener, args = null) {
|
|
2336
|
+
return this._createListener(type, caller, listener, args, false);
|
|
2337
|
+
}
|
|
2338
|
+
/**
|
|
2339
|
+
* 使用 EventDispatcher 对象注册指定类型的事件侦听器对象,以使侦听器能够接收事件通知,此侦听事件响应一次后自动移除。
|
|
2340
|
+
* @param type 事件的类型。
|
|
2341
|
+
* @param caller 事件侦听函数的执行域。
|
|
2342
|
+
* @param listener 事件侦听函数。
|
|
2343
|
+
* @param args (可选)事件侦听函数的回调参数。
|
|
2344
|
+
* @return 此 EventDispatcher 对象。
|
|
2345
|
+
*/
|
|
2346
|
+
once(type, caller, listener, args = null) {
|
|
2347
|
+
return this._createListener(type, caller, listener, args, true);
|
|
2348
|
+
}
|
|
2349
|
+
/**@internal */
|
|
2350
|
+
_createListener(type, caller, listener, args, once, offBefore = true) {
|
|
2351
|
+
//移除之前相同的监听
|
|
2352
|
+
offBefore && this.off(type, caller, listener, once);
|
|
2353
|
+
//使用对象池进行创建回收
|
|
2354
|
+
var handler = Handler.create(caller || this, listener, args, once);
|
|
2355
|
+
this._events || (this._events = {});
|
|
2356
|
+
var events = this._events;
|
|
2357
|
+
//默认单个,每个对象只有多个监听才用数组,节省一个数组的消耗
|
|
2358
|
+
if (!events[type])
|
|
2359
|
+
events[type] = handler;
|
|
2360
|
+
else {
|
|
2361
|
+
if (!events[type].run)
|
|
2362
|
+
events[type].push(handler);
|
|
2363
|
+
else
|
|
2364
|
+
events[type] = [events[type], handler];
|
|
2365
|
+
}
|
|
2366
|
+
return this;
|
|
2367
|
+
}
|
|
2368
|
+
/**
|
|
2369
|
+
* 从 EventDispatcher 对象中删除侦听器。
|
|
2033
2370
|
* @param type 事件的类型。
|
|
2034
2371
|
* @param caller 事件侦听函数的执行域。
|
|
2035
2372
|
* @param listener 事件侦听函数。
|
|
@@ -5367,983 +5704,708 @@ class Socket extends EventDispatcher {
|
|
|
5367
5704
|
this._onError(e);
|
|
5368
5705
|
};
|
|
5369
5706
|
}
|
|
5370
|
-
/**
|
|
5371
|
-
* 清理Socket:关闭Socket链接,关闭事件监听,重置Socket
|
|
5372
|
-
*/
|
|
5373
|
-
cleanSocket() {
|
|
5374
|
-
this.close();
|
|
5375
|
-
this._connected = false;
|
|
5376
|
-
this._socket.onopen = null;
|
|
5377
|
-
this._socket.onmessage = null;
|
|
5378
|
-
this._socket.onclose = null;
|
|
5379
|
-
this._socket.onerror = null;
|
|
5380
|
-
this._socket = null;
|
|
5381
|
-
}
|
|
5382
|
-
/**
|
|
5383
|
-
* 关闭连接。
|
|
5384
|
-
*/
|
|
5385
|
-
close(code, reason) {
|
|
5386
|
-
if (this._socket != null) {
|
|
5387
|
-
let event = null;
|
|
5388
|
-
try {
|
|
5389
|
-
this._socket.close(code, reason);
|
|
5390
|
-
}
|
|
5391
|
-
catch (e) {
|
|
5392
|
-
event = e;
|
|
5393
|
-
}
|
|
5394
|
-
return event == null;
|
|
5395
|
-
}
|
|
5396
|
-
}
|
|
5397
|
-
/**
|
|
5398
|
-
* @private
|
|
5399
|
-
* 连接建立成功 。
|
|
5400
|
-
*/
|
|
5401
|
-
_onOpen(e) {
|
|
5402
|
-
this._connected = true;
|
|
5403
|
-
this.event(Event$2.OPEN, e);
|
|
5404
|
-
}
|
|
5405
|
-
/**
|
|
5406
|
-
* @private
|
|
5407
|
-
* 接收到数据处理方法。
|
|
5408
|
-
* @param msg 数据。
|
|
5409
|
-
*/
|
|
5410
|
-
_onMessage(msg) {
|
|
5411
|
-
if (!msg || !msg.data)
|
|
5412
|
-
return;
|
|
5413
|
-
var data = msg.data;
|
|
5414
|
-
if (this.disableInput && data) {
|
|
5415
|
-
let byte = new ByteView(data);
|
|
5416
|
-
byte.endian = this.endian;
|
|
5417
|
-
this.event(Event$2.MESSAGE, byte);
|
|
5418
|
-
return;
|
|
5419
|
-
}
|
|
5420
|
-
if (this._input.length > 0 && this._input.bytesAvailable < 1) {
|
|
5421
|
-
this._input.clear();
|
|
5422
|
-
this._addInputPosition = 0;
|
|
5423
|
-
}
|
|
5424
|
-
var pre = this._input.pos;
|
|
5425
|
-
!this._addInputPosition && (this._addInputPosition = 0);
|
|
5426
|
-
this._input.pos = this._addInputPosition;
|
|
5427
|
-
if (data) {
|
|
5428
|
-
if (typeof (data) == 'string') {
|
|
5429
|
-
this._input.writeUTFBytes(data);
|
|
5430
|
-
}
|
|
5431
|
-
else {
|
|
5432
|
-
this._input.writeArrayBuffer(data);
|
|
5433
|
-
}
|
|
5434
|
-
this._addInputPosition = this._input.pos;
|
|
5435
|
-
this._input.pos = pre;
|
|
5436
|
-
}
|
|
5437
|
-
this.event(Event$2.MESSAGE, data);
|
|
5438
|
-
}
|
|
5439
|
-
/**
|
|
5440
|
-
* @private
|
|
5441
|
-
* 连接被关闭处理方法。
|
|
5442
|
-
*/
|
|
5443
|
-
_onClose(e) {
|
|
5444
|
-
this._connected = false;
|
|
5445
|
-
this.event(Event$2.CLOSE, e);
|
|
5446
|
-
}
|
|
5447
|
-
/**
|
|
5448
|
-
* @private
|
|
5449
|
-
* 出现异常处理方法。
|
|
5450
|
-
*/
|
|
5451
|
-
_onError(e) {
|
|
5452
|
-
this.event(Event$2.ERROR, e);
|
|
5453
|
-
}
|
|
5454
|
-
/**
|
|
5455
|
-
* 发送数据到服务器。
|
|
5456
|
-
* @param data 需要发送的数据,可以是String或者ArrayBuffer。
|
|
5457
|
-
*/
|
|
5458
|
-
send(data) {
|
|
5459
|
-
if (this._socket && this._socket.readyState === WebSocket.CONNECTING) {
|
|
5460
|
-
this._socket.send(data);
|
|
5461
|
-
return true;
|
|
5462
|
-
}
|
|
5463
|
-
return false;
|
|
5464
|
-
}
|
|
5465
|
-
/**
|
|
5466
|
-
* 发送缓冲区中的数据到服务器。
|
|
5467
|
-
*/
|
|
5468
|
-
flush() {
|
|
5469
|
-
if (this._output && this._output.length > 0) {
|
|
5470
|
-
var evt;
|
|
5471
|
-
try {
|
|
5472
|
-
this._socket && this._socket.send(this._output._getBuffer().slice(0, this._output.length));
|
|
5473
|
-
}
|
|
5474
|
-
catch (e) {
|
|
5475
|
-
evt = e;
|
|
5476
|
-
}
|
|
5477
|
-
this._output.endian = this.endian;
|
|
5478
|
-
this._output.clear();
|
|
5479
|
-
if (evt)
|
|
5480
|
-
this.event(Event$2.ERROR, evt);
|
|
5481
|
-
}
|
|
5482
|
-
}
|
|
5483
|
-
get state() { return this._socket ? this._socket.readyState : WebSocket.CLOSED; }
|
|
5484
|
-
get url() { return this._socket ? this._socket.url : ""; }
|
|
5485
|
-
get binaryType() { return this._socket ? this._socket.binaryType : "arraybuffer"; }
|
|
5486
|
-
}
|
|
5487
|
-
/**
|
|
5488
|
-
* <p>主机字节序,是 CPU 存放数据的两种不同顺序,包括小端字节序和大端字节序。</p>
|
|
5489
|
-
* <p> LITTLE_ENDIAN :小端字节序,地址低位存储值的低位,地址高位存储值的高位。</p>
|
|
5490
|
-
* <p> BIG_ENDIAN :大端字节序,地址低位存储值的高位,地址高位存储值的低位。有时也称之为网络字节序。</p>
|
|
5491
|
-
*/
|
|
5492
|
-
Socket.LITTLE_ENDIAN = "littleEndian";
|
|
5493
|
-
/**
|
|
5494
|
-
* <p>主机字节序,是 CPU 存放数据的两种不同顺序,包括小端字节序和大端字节序。</p>
|
|
5495
|
-
* <p> BIG_ENDIAN :大端字节序,地址低位存储值的高位,地址高位存储值的低位。有时也称之为网络字节序。</p>
|
|
5496
|
-
* <p> LITTLE_ENDIAN :小端字节序,地址低位存储值的低位,地址高位存储值的高位。</p>
|
|
5497
|
-
*/
|
|
5498
|
-
Socket.BIG_ENDIAN = "bigEndian";
|
|
5499
|
-
|
|
5500
|
-
var SocketLogType;
|
|
5501
|
-
(function (SocketLogType) {
|
|
5502
|
-
SocketLogType["Connected"] = "[C==S]";
|
|
5503
|
-
SocketLogType["C2S"] = "[C=>S]";
|
|
5504
|
-
SocketLogType["S2C"] = "[S=>C]";
|
|
5505
|
-
SocketLogType["S2xC"] = "[S=xC]";
|
|
5506
|
-
SocketLogType["CxS"] = "[CxS]";
|
|
5507
|
-
})(SocketLogType || (SocketLogType = {}));
|
|
5508
|
-
/**
|
|
5509
|
-
* Socket节点,实现断线重连、心跳等机制
|
|
5510
|
-
*/
|
|
5511
|
-
class SocketNode {
|
|
5512
|
-
constructor() {
|
|
5513
|
-
this._socket = null;
|
|
5514
|
-
this._protocolHelper = null;
|
|
5515
|
-
this._networkTips = null;
|
|
5516
|
-
this._reconnectTimes = 3;
|
|
5517
|
-
this._loger = null;
|
|
5518
|
-
this._reconnectCD = 3 * 1000;
|
|
5519
|
-
this._reconnectCount = null;
|
|
5520
|
-
this._heartbeatID = 0;
|
|
5521
|
-
this._heartbeatCD = 5 * 1000;
|
|
5522
|
-
}
|
|
5523
|
-
init(socket, protocolHelper, networkTips) {
|
|
5524
|
-
if (this._socket)
|
|
5525
|
-
return;
|
|
5526
|
-
this._socket = socket;
|
|
5527
|
-
this._protocolHelper = protocolHelper;
|
|
5528
|
-
this._networkTips = networkTips;
|
|
5529
|
-
}
|
|
5530
|
-
_addEvent() {
|
|
5531
|
-
this._socket.on(Event$2.OPEN, this, this._onConnected);
|
|
5532
|
-
this._socket.on(Event$2.MESSAGE, this, this._onMessage);
|
|
5533
|
-
this._socket.on(Event$2.ERROR, this, this._onError);
|
|
5534
|
-
this._socket.on(Event$2.CLOSE, this, this._onClosed);
|
|
5535
|
-
}
|
|
5536
|
-
_removeEvent() {
|
|
5537
|
-
this._socket.off(Event$2.OPEN, this, this._onConnected);
|
|
5538
|
-
this._socket.off(Event$2.MESSAGE, this, this._onMessage);
|
|
5539
|
-
this._socket.off(Event$2.ERROR, this, this._onError);
|
|
5540
|
-
this._socket.off(Event$2.CLOSE, this, this._onClosed);
|
|
5541
|
-
}
|
|
5542
|
-
/**
|
|
5543
|
-
* 连接服务器
|
|
5544
|
-
* @param url 服务器地址 wss://localhost:80
|
|
5545
|
-
* @param binaryType BinaryType "arraybuffer" | "blob"
|
|
5546
|
-
* @param reconnectTimes 重连次数 -1: 一直尝试重连 0: 不重连 >0: 重连尝试次数
|
|
5547
|
-
* @returns
|
|
5548
|
-
*/
|
|
5549
|
-
connect(url, binaryType = "arraybuffer", reconnectTimes = 3) {
|
|
5550
|
-
this._removeEvent();
|
|
5551
|
-
this._addEvent();
|
|
5552
|
-
this._networkTips.onConnecting();
|
|
5553
|
-
this._reconnectTimes = reconnectTimes;
|
|
5554
|
-
this._socket.connectByUrl(url, binaryType);
|
|
5707
|
+
/**
|
|
5708
|
+
* 清理Socket:关闭Socket链接,关闭事件监听,重置Socket
|
|
5709
|
+
*/
|
|
5710
|
+
cleanSocket() {
|
|
5711
|
+
this.close();
|
|
5712
|
+
this._connected = false;
|
|
5713
|
+
this._socket.onopen = null;
|
|
5714
|
+
this._socket.onmessage = null;
|
|
5715
|
+
this._socket.onclose = null;
|
|
5716
|
+
this._socket.onerror = null;
|
|
5717
|
+
this._socket = null;
|
|
5555
5718
|
}
|
|
5556
5719
|
/**
|
|
5557
|
-
*
|
|
5558
|
-
* @returns
|
|
5720
|
+
* 关闭连接。
|
|
5559
5721
|
*/
|
|
5560
|
-
|
|
5561
|
-
if (this._socket
|
|
5562
|
-
|
|
5722
|
+
close(code, reason) {
|
|
5723
|
+
if (this._socket != null) {
|
|
5724
|
+
let event = null;
|
|
5563
5725
|
try {
|
|
5564
|
-
this._socket.
|
|
5565
|
-
return true;
|
|
5726
|
+
this._socket.close(code, reason);
|
|
5566
5727
|
}
|
|
5567
5728
|
catch (e) {
|
|
5568
|
-
|
|
5729
|
+
event = e;
|
|
5569
5730
|
}
|
|
5570
|
-
|
|
5571
|
-
return false;
|
|
5572
|
-
}
|
|
5573
|
-
log(type, data) {
|
|
5574
|
-
if (this._loger) {
|
|
5575
|
-
this._loger(type, typeof data === 'string' ? data : `ID:${data.id} Len:${data.len} Data:${JSON.stringify(data.data)}`);
|
|
5731
|
+
return event == null;
|
|
5576
5732
|
}
|
|
5577
5733
|
}
|
|
5578
5734
|
/**
|
|
5579
|
-
*
|
|
5580
|
-
*
|
|
5581
|
-
* @param reason
|
|
5735
|
+
* @private
|
|
5736
|
+
* 连接建立成功 。
|
|
5582
5737
|
*/
|
|
5583
|
-
|
|
5584
|
-
this.
|
|
5585
|
-
this.
|
|
5586
|
-
this._socket.close(code, reason);
|
|
5587
|
-
this.log(SocketLogType.CxS, `${this._socket.url} By Client`);
|
|
5588
|
-
}
|
|
5589
|
-
_onConnected(event) {
|
|
5590
|
-
this.log(SocketLogType.Connected, this._socket.url);
|
|
5591
|
-
this._reconnectCount = null;
|
|
5592
|
-
if (this._socket.state == WebSocket.OPEN) {
|
|
5593
|
-
this._networkTips.onConnectSuccess();
|
|
5594
|
-
}
|
|
5595
|
-
else {
|
|
5596
|
-
this._onError(event);
|
|
5597
|
-
}
|
|
5598
|
-
}
|
|
5599
|
-
_onMessage(data) {
|
|
5600
|
-
this._protocolHelper.decode(this._socket.disableInput ? data : this._socket.input);
|
|
5601
|
-
this._networkTips.onMessage();
|
|
5602
|
-
this._socket.input.clear();
|
|
5603
|
-
this._heartbeat(); //重置心跳
|
|
5604
|
-
}
|
|
5605
|
-
_onError(event) {
|
|
5606
|
-
this._networkTips.onConnectFailed();
|
|
5607
|
-
this.log(SocketLogType.CxS, `${this._socket.url}`);
|
|
5608
|
-
this._retry();
|
|
5609
|
-
}
|
|
5610
|
-
_onClosed(event) {
|
|
5611
|
-
this._networkTips.onClosed();
|
|
5612
|
-
this.log(SocketLogType.CxS, `${this._socket.url} Code:${event.code} Reason:${event.reason} WasClean:${event.wasClean}`);
|
|
5613
|
-
this._retry();
|
|
5614
|
-
}
|
|
5615
|
-
_retry() {
|
|
5616
|
-
if (this._reconnectTimes != 0) {
|
|
5617
|
-
if (this._reconnectCount == null) {
|
|
5618
|
-
this._reconnectCount = this._reconnectTimes == -1 ? 9999 : this._reconnectTimes + 1;
|
|
5619
|
-
}
|
|
5620
|
-
if (--this._reconnectCount > 0) {
|
|
5621
|
-
this._reconnectHandler = setTimeout(() => {
|
|
5622
|
-
this.connect(this._socket.url, this._socket.binaryType, this._reconnectTimes);
|
|
5623
|
-
}, this._reconnectCD);
|
|
5624
|
-
}
|
|
5625
|
-
}
|
|
5626
|
-
}
|
|
5627
|
-
_heartbeat() {
|
|
5628
|
-
this._cleanupTimeout();
|
|
5629
|
-
this._keepAliveHandler = setTimeout(() => {
|
|
5630
|
-
this._protocolHelper.heartbeat();
|
|
5631
|
-
}, this._heartbeatCD);
|
|
5632
|
-
}
|
|
5633
|
-
_cleanupTimeout() {
|
|
5634
|
-
clearTimeout(this._keepAliveHandler);
|
|
5635
|
-
clearTimeout(this._reconnectHandler);
|
|
5636
|
-
}
|
|
5637
|
-
set logable(value) {
|
|
5638
|
-
this._loger = value ? console.log : null;
|
|
5639
|
-
}
|
|
5640
|
-
get reconnectCD() {
|
|
5641
|
-
return this._reconnectCD;
|
|
5642
|
-
}
|
|
5643
|
-
set reconnectCD(value) {
|
|
5644
|
-
this._reconnectCD = value;
|
|
5645
|
-
}
|
|
5646
|
-
get heartbeatCD() {
|
|
5647
|
-
return this._heartbeatCD;
|
|
5648
|
-
}
|
|
5649
|
-
set heartbeatCD(value) {
|
|
5650
|
-
this._heartbeatCD = value;
|
|
5651
|
-
}
|
|
5652
|
-
get output() { return this._socket.output; }
|
|
5653
|
-
;
|
|
5654
|
-
get input() { return this._socket.input; }
|
|
5655
|
-
;
|
|
5656
|
-
}
|
|
5657
|
-
|
|
5658
|
-
class NetManager {
|
|
5659
|
-
constructor() {
|
|
5660
|
-
this._logable = false;
|
|
5661
|
-
this._socket = null;
|
|
5662
|
-
this._http = null;
|
|
5663
|
-
if (NetManager.created) {
|
|
5664
|
-
throw new Error("NetManager 是单例");
|
|
5665
|
-
}
|
|
5666
|
-
NetManager.created = true;
|
|
5667
|
-
}
|
|
5668
|
-
initSocket() {
|
|
5669
|
-
if (this._socket)
|
|
5670
|
-
return;
|
|
5671
|
-
this._protocol = Injector.getInject(NetManager.PROTOCOL_HELPER_KEY);
|
|
5672
|
-
this._netTips = Injector.getInject(NetManager.NETWORK_TIPS_KEY);
|
|
5673
|
-
this._socket = new SocketNode();
|
|
5674
|
-
let socket = new Socket();
|
|
5675
|
-
socket.disableInput = true;
|
|
5676
|
-
// socket.protocols = [];
|
|
5677
|
-
// socket.endian = Socket.BIG_ENDIAN;
|
|
5678
|
-
this._socket.init(socket, this._protocol, this._netTips);
|
|
5679
|
-
this._socket.logable = this._logable;
|
|
5738
|
+
_onOpen(e) {
|
|
5739
|
+
this._connected = true;
|
|
5740
|
+
this.event(Event$2.OPEN, e);
|
|
5680
5741
|
}
|
|
5681
|
-
|
|
5682
|
-
|
|
5742
|
+
/**
|
|
5743
|
+
* @private
|
|
5744
|
+
* 接收到数据处理方法。
|
|
5745
|
+
* @param msg 数据。
|
|
5746
|
+
*/
|
|
5747
|
+
_onMessage(msg) {
|
|
5748
|
+
if (!msg || !msg.data)
|
|
5683
5749
|
return;
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
this._socket.logable = value;
|
|
5690
|
-
}
|
|
5691
|
-
}
|
|
5692
|
-
get socket() {
|
|
5693
|
-
this.initSocket();
|
|
5694
|
-
return this._socket;
|
|
5695
|
-
}
|
|
5696
|
-
get http() {
|
|
5697
|
-
this.initHttp();
|
|
5698
|
-
return this._http;
|
|
5699
|
-
}
|
|
5700
|
-
get server() {
|
|
5701
|
-
this.initSocket();
|
|
5702
|
-
return this._protocol;
|
|
5703
|
-
}
|
|
5704
|
-
}
|
|
5705
|
-
NetManager.PROTOCOL_HELPER_KEY = "net.protocolHelper";
|
|
5706
|
-
NetManager.NETWORK_TIPS_KEY = "net.networkTips";
|
|
5707
|
-
NetManager.created = false;
|
|
5708
|
-
/**
|
|
5709
|
-
* 网络
|
|
5710
|
-
*/
|
|
5711
|
-
const net = new NetManager();
|
|
5712
|
-
|
|
5713
|
-
const KEYTOPS_ANALYTICS_EVENT_CACHE = "KEYTOPS_ANALYTICS_EVENT_CACHE";
|
|
5714
|
-
const KEYTOPS_RETRY_DELAYS = [100, 500, 2000];
|
|
5715
|
-
const KEYTOPS_REQUEST_TIMEOUT = 10000;
|
|
5716
|
-
class KeytopsAnalyticsSender extends EmptyAnalyticsSender {
|
|
5717
|
-
constructor(config, exclude) {
|
|
5718
|
-
super(exclude);
|
|
5719
|
-
this._sending = false;
|
|
5720
|
-
this._flushScheduled = false;
|
|
5721
|
-
this._eventCache = [];
|
|
5722
|
-
this._index = 0;
|
|
5723
|
-
this._index = 0;
|
|
5724
|
-
this._setConfig(config);
|
|
5725
|
-
this._storageKey = this._config.cacheKey || KEYTOPS_ANALYTICS_EVENT_CACHE;
|
|
5726
|
-
this._eventCache = this._loadStoredEvents();
|
|
5727
|
-
this._addListen();
|
|
5728
|
-
this._startLoop();
|
|
5729
|
-
void this._doReport();
|
|
5730
|
-
}
|
|
5731
|
-
_setConfig(config) {
|
|
5732
|
-
config.cd = config.cd || 1;
|
|
5733
|
-
config.count = config.count || 20;
|
|
5734
|
-
config.maxBufferedBytes = config.maxBufferedBytes || 512 * 1024;
|
|
5735
|
-
this._config = config;
|
|
5736
|
-
}
|
|
5737
|
-
_loadStoredEvents() {
|
|
5738
|
-
const storedValue = storage.getGValue(this._storageKey, []);
|
|
5739
|
-
const events = Array.isArray(storedValue) ? storedValue : [];
|
|
5740
|
-
if (events.length > 0) {
|
|
5741
|
-
storage.removeGValue(this._storageKey);
|
|
5742
|
-
}
|
|
5743
|
-
return events;
|
|
5744
|
-
}
|
|
5745
|
-
_storeEvents(events) {
|
|
5746
|
-
if (!events.length) {
|
|
5747
|
-
storage.removeGValue(this._storageKey);
|
|
5750
|
+
var data = msg.data;
|
|
5751
|
+
if (this.disableInput && data) {
|
|
5752
|
+
let byte = new ByteView(data);
|
|
5753
|
+
byte.endian = this.endian;
|
|
5754
|
+
this.event(Event$2.MESSAGE, byte);
|
|
5748
5755
|
return;
|
|
5749
5756
|
}
|
|
5750
|
-
|
|
5751
|
-
|
|
5752
|
-
|
|
5753
|
-
try {
|
|
5754
|
-
const text = JSON.stringify(value) || "";
|
|
5755
|
-
return encodeURIComponent(text).replace(/%[A-F\d]{2}/g, "U").length;
|
|
5757
|
+
if (this._input.length > 0 && this._input.bytesAvailable < 1) {
|
|
5758
|
+
this._input.clear();
|
|
5759
|
+
this._addInputPosition = 0;
|
|
5756
5760
|
}
|
|
5757
|
-
|
|
5758
|
-
|
|
5761
|
+
var pre = this._input.pos;
|
|
5762
|
+
!this._addInputPosition && (this._addInputPosition = 0);
|
|
5763
|
+
this._input.pos = this._addInputPosition;
|
|
5764
|
+
if (data) {
|
|
5765
|
+
if (typeof (data) == 'string') {
|
|
5766
|
+
this._input.writeUTFBytes(data);
|
|
5767
|
+
}
|
|
5768
|
+
else {
|
|
5769
|
+
this._input.writeArrayBuffer(data);
|
|
5770
|
+
}
|
|
5771
|
+
this._addInputPosition = this._input.pos;
|
|
5772
|
+
this._input.pos = pre;
|
|
5759
5773
|
}
|
|
5774
|
+
this.event(Event$2.MESSAGE, data);
|
|
5760
5775
|
}
|
|
5761
|
-
|
|
5762
|
-
|
|
5763
|
-
|
|
5764
|
-
|
|
5765
|
-
|
|
5766
|
-
|
|
5767
|
-
|
|
5768
|
-
App.onHide(() => {
|
|
5769
|
-
void this._doReport(this._eventCache.length, true);
|
|
5770
|
-
});
|
|
5771
|
-
App.onShow(this._restoreEvents, this);
|
|
5776
|
+
/**
|
|
5777
|
+
* @private
|
|
5778
|
+
* 连接被关闭处理方法。
|
|
5779
|
+
*/
|
|
5780
|
+
_onClose(e) {
|
|
5781
|
+
this._connected = false;
|
|
5782
|
+
this.event(Event$2.CLOSE, e);
|
|
5772
5783
|
}
|
|
5773
|
-
|
|
5774
|
-
|
|
5775
|
-
|
|
5776
|
-
|
|
5777
|
-
|
|
5778
|
-
|
|
5784
|
+
/**
|
|
5785
|
+
* @private
|
|
5786
|
+
* 出现异常处理方法。
|
|
5787
|
+
*/
|
|
5788
|
+
_onError(e) {
|
|
5789
|
+
this.event(Event$2.ERROR, e);
|
|
5790
|
+
}
|
|
5791
|
+
/**
|
|
5792
|
+
* 发送数据到服务器。
|
|
5793
|
+
* @param data 需要发送的数据,可以是String或者ArrayBuffer。
|
|
5794
|
+
*/
|
|
5795
|
+
send(data) {
|
|
5796
|
+
if (this._socket && this._socket.readyState === WebSocket.CONNECTING) {
|
|
5797
|
+
this._socket.send(data);
|
|
5798
|
+
return true;
|
|
5779
5799
|
}
|
|
5800
|
+
return false;
|
|
5780
5801
|
}
|
|
5781
|
-
|
|
5782
|
-
|
|
5783
|
-
|
|
5802
|
+
/**
|
|
5803
|
+
* 发送缓冲区中的数据到服务器。
|
|
5804
|
+
*/
|
|
5805
|
+
flush() {
|
|
5806
|
+
if (this._output && this._output.length > 0) {
|
|
5807
|
+
var evt;
|
|
5808
|
+
try {
|
|
5809
|
+
this._socket && this._socket.send(this._output._getBuffer().slice(0, this._output.length));
|
|
5810
|
+
}
|
|
5811
|
+
catch (e) {
|
|
5812
|
+
evt = e;
|
|
5813
|
+
}
|
|
5814
|
+
this._output.endian = this.endian;
|
|
5815
|
+
this._output.clear();
|
|
5816
|
+
if (evt)
|
|
5817
|
+
this.event(Event$2.ERROR, evt);
|
|
5784
5818
|
}
|
|
5785
|
-
this._flushScheduled = true;
|
|
5786
|
-
timer.once(delay, this, () => {
|
|
5787
|
-
this._flushScheduled = false;
|
|
5788
|
-
void this._doReport();
|
|
5789
|
-
}, null, false);
|
|
5790
5819
|
}
|
|
5791
|
-
|
|
5792
|
-
|
|
5793
|
-
|
|
5794
|
-
|
|
5795
|
-
|
|
5796
|
-
|
|
5820
|
+
get state() { return this._socket ? this._socket.readyState : WebSocket.CLOSED; }
|
|
5821
|
+
get url() { return this._socket ? this._socket.url : ""; }
|
|
5822
|
+
get binaryType() { return this._socket ? this._socket.binaryType : "arraybuffer"; }
|
|
5823
|
+
}
|
|
5824
|
+
/**
|
|
5825
|
+
* <p>主机字节序,是 CPU 存放数据的两种不同顺序,包括小端字节序和大端字节序。</p>
|
|
5826
|
+
* <p> LITTLE_ENDIAN :小端字节序,地址低位存储值的低位,地址高位存储值的高位。</p>
|
|
5827
|
+
* <p> BIG_ENDIAN :大端字节序,地址低位存储值的高位,地址高位存储值的低位。有时也称之为网络字节序。</p>
|
|
5828
|
+
*/
|
|
5829
|
+
Socket.LITTLE_ENDIAN = "littleEndian";
|
|
5830
|
+
/**
|
|
5831
|
+
* <p>主机字节序,是 CPU 存放数据的两种不同顺序,包括小端字节序和大端字节序。</p>
|
|
5832
|
+
* <p> BIG_ENDIAN :大端字节序,地址低位存储值的高位,地址高位存储值的低位。有时也称之为网络字节序。</p>
|
|
5833
|
+
* <p> LITTLE_ENDIAN :小端字节序,地址低位存储值的低位,地址高位存储值的高位。</p>
|
|
5834
|
+
*/
|
|
5835
|
+
Socket.BIG_ENDIAN = "bigEndian";
|
|
5836
|
+
|
|
5837
|
+
var SocketLogType;
|
|
5838
|
+
(function (SocketLogType) {
|
|
5839
|
+
SocketLogType["Connected"] = "[C==S]";
|
|
5840
|
+
SocketLogType["C2S"] = "[C=>S]";
|
|
5841
|
+
SocketLogType["S2C"] = "[S=>C]";
|
|
5842
|
+
SocketLogType["S2xC"] = "[S=xC]";
|
|
5843
|
+
SocketLogType["CxS"] = "[CxS]";
|
|
5844
|
+
})(SocketLogType || (SocketLogType = {}));
|
|
5845
|
+
/**
|
|
5846
|
+
* Socket节点,实现断线重连、心跳等机制
|
|
5847
|
+
*/
|
|
5848
|
+
class SocketNode {
|
|
5849
|
+
constructor() {
|
|
5850
|
+
this._socket = null;
|
|
5851
|
+
this._protocolHelper = null;
|
|
5852
|
+
this._networkTips = null;
|
|
5853
|
+
this._reconnectTimes = 3;
|
|
5854
|
+
this._loger = null;
|
|
5855
|
+
this._reconnectCD = 3 * 1000;
|
|
5856
|
+
this._reconnectCount = null;
|
|
5857
|
+
this._heartbeatID = 0;
|
|
5858
|
+
this._heartbeatCD = 5 * 1000;
|
|
5797
5859
|
}
|
|
5798
|
-
|
|
5799
|
-
|
|
5860
|
+
init(socket, protocolHelper, networkTips) {
|
|
5861
|
+
if (this._socket)
|
|
5862
|
+
return;
|
|
5863
|
+
this._socket = socket;
|
|
5864
|
+
this._protocolHelper = protocolHelper;
|
|
5865
|
+
this._networkTips = networkTips;
|
|
5800
5866
|
}
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
const payload = {
|
|
5807
|
-
requestId: this.buildRequestId(),
|
|
5808
|
-
appid: publisher.appId,
|
|
5809
|
-
sentAt: this._config.timestampCall ? (this._config.timestampCall() || Date.now()) : Date.now(),
|
|
5810
|
-
events,
|
|
5811
|
-
};
|
|
5812
|
-
const request = this._buildRequestBody(payload);
|
|
5813
|
-
const response = yield net.http.sendsync(this._config.url, {
|
|
5814
|
-
method: "post",
|
|
5815
|
-
responseType: "json",
|
|
5816
|
-
data: request.data,
|
|
5817
|
-
headers: request.headers,
|
|
5818
|
-
timeout: KEYTOPS_REQUEST_TIMEOUT,
|
|
5819
|
-
});
|
|
5820
|
-
const code = response && response.code;
|
|
5821
|
-
if (!(code === 0 || code === "0" || code === 200 || code === "200")) {
|
|
5822
|
-
throw new Error(response && response.message ? response.message : "relay request failed");
|
|
5823
|
-
}
|
|
5824
|
-
});
|
|
5867
|
+
_addEvent() {
|
|
5868
|
+
this._socket.on(Event$2.OPEN, this, this._onConnected);
|
|
5869
|
+
this._socket.on(Event$2.MESSAGE, this, this._onMessage);
|
|
5870
|
+
this._socket.on(Event$2.ERROR, this, this._onError);
|
|
5871
|
+
this._socket.on(Event$2.CLOSE, this, this._onClosed);
|
|
5825
5872
|
}
|
|
5826
|
-
|
|
5827
|
-
|
|
5828
|
-
|
|
5829
|
-
|
|
5830
|
-
|
|
5831
|
-
yield this._sendBatch(events);
|
|
5832
|
-
return;
|
|
5833
|
-
}
|
|
5834
|
-
catch (error) {
|
|
5835
|
-
lastError = error;
|
|
5836
|
-
if (i >= KEYTOPS_RETRY_DELAYS.length) {
|
|
5837
|
-
break;
|
|
5838
|
-
}
|
|
5839
|
-
yield new Promise((resolve) => timer.once(KEYTOPS_RETRY_DELAYS[i], this, resolve, null, false));
|
|
5840
|
-
}
|
|
5841
|
-
}
|
|
5842
|
-
throw lastError || new Error("relay request failed");
|
|
5843
|
-
});
|
|
5873
|
+
_removeEvent() {
|
|
5874
|
+
this._socket.off(Event$2.OPEN, this, this._onConnected);
|
|
5875
|
+
this._socket.off(Event$2.MESSAGE, this, this._onMessage);
|
|
5876
|
+
this._socket.off(Event$2.ERROR, this, this._onError);
|
|
5877
|
+
this._socket.off(Event$2.CLOSE, this, this._onClosed);
|
|
5844
5878
|
}
|
|
5845
|
-
|
|
5846
|
-
|
|
5847
|
-
|
|
5848
|
-
|
|
5849
|
-
|
|
5850
|
-
|
|
5851
|
-
|
|
5879
|
+
/**
|
|
5880
|
+
* 连接服务器
|
|
5881
|
+
* @param url 服务器地址 wss://localhost:80
|
|
5882
|
+
* @param binaryType BinaryType "arraybuffer" | "blob"
|
|
5883
|
+
* @param reconnectTimes 重连次数 -1: 一直尝试重连 0: 不重连 >0: 重连尝试次数
|
|
5884
|
+
* @returns
|
|
5885
|
+
*/
|
|
5886
|
+
connect(url, binaryType = "arraybuffer", reconnectTimes = 3) {
|
|
5887
|
+
this._removeEvent();
|
|
5888
|
+
this._addEvent();
|
|
5889
|
+
this._networkTips.onConnecting();
|
|
5890
|
+
this._reconnectTimes = reconnectTimes;
|
|
5891
|
+
this._socket.connectByUrl(url, binaryType);
|
|
5892
|
+
}
|
|
5893
|
+
/**
|
|
5894
|
+
* 发送数据
|
|
5895
|
+
* @returns
|
|
5896
|
+
*/
|
|
5897
|
+
flush() {
|
|
5898
|
+
if (this._socket.connected) {
|
|
5899
|
+
this._networkTips.onFlush();
|
|
5852
5900
|
try {
|
|
5853
|
-
|
|
5901
|
+
this._socket.flush();
|
|
5854
5902
|
return true;
|
|
5855
5903
|
}
|
|
5856
5904
|
catch (e) {
|
|
5857
|
-
if (!store) {
|
|
5858
|
-
this._eventCache.unshift(...events);
|
|
5859
|
-
console.log(`[KeytopsAnalyticsSender] 事件发送失败,恢复事件到队列,等待下次发送。`);
|
|
5860
|
-
}
|
|
5861
|
-
else {
|
|
5862
|
-
this._storeEvents([...events, ...this._eventCache]);
|
|
5863
|
-
this._eventCache.length = 0;
|
|
5864
|
-
console.log(`[KeytopsAnalyticsSender] 事件发送失败,存储到缓存,等待下次切回游戏。`);
|
|
5865
|
-
}
|
|
5866
5905
|
return false;
|
|
5867
5906
|
}
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
if (!store && this._eventCache.length > 0) {
|
|
5871
|
-
const countLimit = this._config.count || 20;
|
|
5872
|
-
const byteLimit = this._config.maxBufferedBytes || 512 * 1024;
|
|
5873
|
-
if (this._eventCache.length >= countLimit || this._currentBufferedBytes() >= byteLimit) {
|
|
5874
|
-
this._scheduleFlush(0);
|
|
5875
|
-
}
|
|
5876
|
-
}
|
|
5877
|
-
}
|
|
5878
|
-
});
|
|
5907
|
+
}
|
|
5908
|
+
return false;
|
|
5879
5909
|
}
|
|
5880
|
-
|
|
5881
|
-
|
|
5882
|
-
|
|
5883
|
-
|
|
5884
|
-
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
|
|
5889
|
-
|
|
5890
|
-
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5894
|
-
|
|
5895
|
-
|
|
5896
|
-
|
|
5897
|
-
|
|
5898
|
-
|
|
5910
|
+
log(type, data) {
|
|
5911
|
+
if (this._loger) {
|
|
5912
|
+
this._loger(type, typeof data === 'string' ? data : `ID:${data.id} Len:${data.len} Data:${JSON.stringify(data.data)}`);
|
|
5913
|
+
}
|
|
5914
|
+
}
|
|
5915
|
+
/**
|
|
5916
|
+
* 关闭连接
|
|
5917
|
+
* @param code
|
|
5918
|
+
* @param reason
|
|
5919
|
+
*/
|
|
5920
|
+
close(code, reason) {
|
|
5921
|
+
this._cleanupTimeout();
|
|
5922
|
+
this._removeEvent();
|
|
5923
|
+
this._socket.close(code, reason);
|
|
5924
|
+
this.log(SocketLogType.CxS, `${this._socket.url} By Client`);
|
|
5925
|
+
}
|
|
5926
|
+
_onConnected(event) {
|
|
5927
|
+
this.log(SocketLogType.Connected, this._socket.url);
|
|
5928
|
+
this._reconnectCount = null;
|
|
5929
|
+
if (this._socket.state == WebSocket.OPEN) {
|
|
5930
|
+
this._networkTips.onConnectSuccess();
|
|
5931
|
+
}
|
|
5932
|
+
else {
|
|
5933
|
+
this._onError(event);
|
|
5934
|
+
}
|
|
5935
|
+
}
|
|
5936
|
+
_onMessage(data) {
|
|
5937
|
+
this._protocolHelper.decode(this._socket.disableInput ? data : this._socket.input);
|
|
5938
|
+
this._networkTips.onMessage();
|
|
5939
|
+
this._socket.input.clear();
|
|
5940
|
+
this._heartbeat(); //重置心跳
|
|
5941
|
+
}
|
|
5942
|
+
_onError(event) {
|
|
5943
|
+
this._networkTips.onConnectFailed();
|
|
5944
|
+
this.log(SocketLogType.CxS, `${this._socket.url}`);
|
|
5945
|
+
this._retry();
|
|
5946
|
+
}
|
|
5947
|
+
_onClosed(event) {
|
|
5948
|
+
this._networkTips.onClosed();
|
|
5949
|
+
this.log(SocketLogType.CxS, `${this._socket.url} Code:${event.code} Reason:${event.reason} WasClean:${event.wasClean}`);
|
|
5950
|
+
this._retry();
|
|
5951
|
+
}
|
|
5952
|
+
_retry() {
|
|
5953
|
+
if (this._reconnectTimes != 0) {
|
|
5954
|
+
if (this._reconnectCount == null) {
|
|
5955
|
+
this._reconnectCount = this._reconnectTimes == -1 ? 9999 : this._reconnectTimes + 1;
|
|
5899
5956
|
}
|
|
5900
|
-
|
|
5957
|
+
if (--this._reconnectCount > 0) {
|
|
5958
|
+
this._reconnectHandler = setTimeout(() => {
|
|
5959
|
+
this.connect(this._socket.url, this._socket.binaryType, this._reconnectTimes);
|
|
5960
|
+
}, this._reconnectCD);
|
|
5961
|
+
}
|
|
5962
|
+
}
|
|
5963
|
+
}
|
|
5964
|
+
_heartbeat() {
|
|
5965
|
+
this._cleanupTimeout();
|
|
5966
|
+
this._keepAliveHandler = setTimeout(() => {
|
|
5967
|
+
this._protocolHelper.heartbeat();
|
|
5968
|
+
}, this._heartbeatCD);
|
|
5969
|
+
}
|
|
5970
|
+
_cleanupTimeout() {
|
|
5971
|
+
clearTimeout(this._keepAliveHandler);
|
|
5972
|
+
clearTimeout(this._reconnectHandler);
|
|
5973
|
+
}
|
|
5974
|
+
set logable(value) {
|
|
5975
|
+
this._loger = value ? console.log : null;
|
|
5976
|
+
}
|
|
5977
|
+
get reconnectCD() {
|
|
5978
|
+
return this._reconnectCD;
|
|
5979
|
+
}
|
|
5980
|
+
set reconnectCD(value) {
|
|
5981
|
+
this._reconnectCD = value;
|
|
5982
|
+
}
|
|
5983
|
+
get heartbeatCD() {
|
|
5984
|
+
return this._heartbeatCD;
|
|
5985
|
+
}
|
|
5986
|
+
set heartbeatCD(value) {
|
|
5987
|
+
this._heartbeatCD = value;
|
|
5901
5988
|
}
|
|
5989
|
+
get output() { return this._socket.output; }
|
|
5990
|
+
;
|
|
5991
|
+
get input() { return this._socket.input; }
|
|
5992
|
+
;
|
|
5902
5993
|
}
|
|
5903
5994
|
|
|
5904
|
-
class
|
|
5995
|
+
class NetManager {
|
|
5905
5996
|
constructor() {
|
|
5906
|
-
this.
|
|
5907
|
-
this.
|
|
5908
|
-
this.
|
|
5909
|
-
|
|
5910
|
-
|
|
5911
|
-
|
|
5912
|
-
|
|
5997
|
+
this._logable = false;
|
|
5998
|
+
this._socket = null;
|
|
5999
|
+
this._http = null;
|
|
6000
|
+
if (NetManager.created) {
|
|
6001
|
+
throw new Error("NetManager 是单例");
|
|
6002
|
+
}
|
|
6003
|
+
NetManager.created = true;
|
|
5913
6004
|
}
|
|
5914
|
-
|
|
5915
|
-
|
|
6005
|
+
initSocket() {
|
|
6006
|
+
if (this._socket)
|
|
6007
|
+
return;
|
|
6008
|
+
this._protocol = Injector.getInject(NetManager.PROTOCOL_HELPER_KEY);
|
|
6009
|
+
this._netTips = Injector.getInject(NetManager.NETWORK_TIPS_KEY);
|
|
6010
|
+
this._socket = new SocketNode();
|
|
6011
|
+
let socket = new Socket();
|
|
6012
|
+
socket.disableInput = true;
|
|
6013
|
+
// socket.protocols = [];
|
|
6014
|
+
// socket.endian = Socket.BIG_ENDIAN;
|
|
6015
|
+
this._socket.init(socket, this._protocol, this._netTips);
|
|
6016
|
+
this._socket.logable = this._logable;
|
|
5916
6017
|
}
|
|
5917
|
-
|
|
5918
|
-
|
|
6018
|
+
initHttp() {
|
|
6019
|
+
if (this._http)
|
|
6020
|
+
return;
|
|
6021
|
+
this._http = new HttpNode();
|
|
5919
6022
|
}
|
|
5920
|
-
|
|
5921
|
-
|
|
5922
|
-
if (this.
|
|
5923
|
-
|
|
5924
|
-
}
|
|
5925
|
-
if (this.hasAliDirectConfig(analyticsConfig)) {
|
|
5926
|
-
return [new ALiAnalyticsSender(analyticsConfig)];
|
|
6023
|
+
set logable(value) {
|
|
6024
|
+
this._logable = value;
|
|
6025
|
+
if (this._socket) {
|
|
6026
|
+
this._socket.logable = value;
|
|
5927
6027
|
}
|
|
5928
|
-
return [];
|
|
5929
6028
|
}
|
|
5930
|
-
|
|
5931
|
-
|
|
5932
|
-
|
|
5933
|
-
get base() {
|
|
5934
|
-
if (!this._base) {
|
|
5935
|
-
this._base = new AnalyticsAble();
|
|
5936
|
-
}
|
|
5937
|
-
return this._base;
|
|
6029
|
+
get socket() {
|
|
6030
|
+
this.initSocket();
|
|
6031
|
+
return this._socket;
|
|
5938
6032
|
}
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
get level() {
|
|
5943
|
-
if (!this.base.inited) {
|
|
5944
|
-
throw new Error("请先通过enable初始化基础统计能力");
|
|
5945
|
-
}
|
|
5946
|
-
if (!this._level) {
|
|
5947
|
-
this._level = new LevelAnalyticsAble(this.base);
|
|
5948
|
-
}
|
|
5949
|
-
return this._level;
|
|
6033
|
+
get http() {
|
|
6034
|
+
this.initHttp();
|
|
6035
|
+
return this._http;
|
|
5950
6036
|
}
|
|
5951
|
-
|
|
5952
|
-
|
|
5953
|
-
|
|
5954
|
-
get ad() {
|
|
5955
|
-
if (!this.base.inited) {
|
|
5956
|
-
throw new Error("请先通过enable初始化基础统计能力");
|
|
5957
|
-
}
|
|
5958
|
-
if (!this._ad) {
|
|
5959
|
-
this._ad = new ADAnalyticsAble(this.base);
|
|
5960
|
-
}
|
|
5961
|
-
return this._ad;
|
|
6037
|
+
get server() {
|
|
6038
|
+
this.initSocket();
|
|
6039
|
+
return this._protocol;
|
|
5962
6040
|
}
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
5972
|
-
|
|
5973
|
-
|
|
6041
|
+
}
|
|
6042
|
+
NetManager.PROTOCOL_HELPER_KEY = "net.protocolHelper";
|
|
6043
|
+
NetManager.NETWORK_TIPS_KEY = "net.networkTips";
|
|
6044
|
+
NetManager.created = false;
|
|
6045
|
+
/**
|
|
6046
|
+
* 网络
|
|
6047
|
+
*/
|
|
6048
|
+
const net = new NetManager();
|
|
6049
|
+
|
|
6050
|
+
const KEYTOPS_ANALYTICS_EVENT_CACHE = "KEYTOPS_ANALYTICS_EVENT_CACHE";
|
|
6051
|
+
const KEYTOPS_RETRY_DELAYS = [100, 500, 2000];
|
|
6052
|
+
const KEYTOPS_REQUEST_TIMEOUT = 10000;
|
|
6053
|
+
class KeytopsAnalyticsSender extends EmptyAnalyticsSender {
|
|
6054
|
+
constructor(config, exclude) {
|
|
6055
|
+
super(exclude);
|
|
6056
|
+
this._sending = false;
|
|
6057
|
+
this._flushScheduled = false;
|
|
6058
|
+
this._eventCache = [];
|
|
6059
|
+
this._index = 0;
|
|
6060
|
+
this._index = 0;
|
|
6061
|
+
this._setConfig(config);
|
|
6062
|
+
this._storageKey = this._config.cacheKey || KEYTOPS_ANALYTICS_EVENT_CACHE;
|
|
6063
|
+
this._eventCache = this._loadStoredEvents();
|
|
6064
|
+
this._addListen();
|
|
6065
|
+
this._startLoop();
|
|
6066
|
+
void this._doReport();
|
|
5974
6067
|
}
|
|
5975
|
-
|
|
5976
|
-
|
|
5977
|
-
|
|
5978
|
-
|
|
5979
|
-
|
|
5980
|
-
|
|
6068
|
+
_setConfig(config) {
|
|
6069
|
+
config.cd = config.cd || 1;
|
|
6070
|
+
config.count = config.count || 20;
|
|
6071
|
+
config.maxBufferedBytes = config.maxBufferedBytes || 512 * 1024;
|
|
6072
|
+
this._config = config;
|
|
6073
|
+
}
|
|
6074
|
+
_loadStoredEvents() {
|
|
6075
|
+
const storedValue = storage.getGValue(this._storageKey, []);
|
|
6076
|
+
const events = Array.isArray(storedValue) ? storedValue : [];
|
|
6077
|
+
if (events.length > 0) {
|
|
6078
|
+
storage.removeGValue(this._storageKey);
|
|
5981
6079
|
}
|
|
5982
|
-
|
|
5983
|
-
|
|
6080
|
+
return events;
|
|
6081
|
+
}
|
|
6082
|
+
_storeEvents(events) {
|
|
6083
|
+
if (!events.length) {
|
|
6084
|
+
storage.removeGValue(this._storageKey);
|
|
6085
|
+
return;
|
|
5984
6086
|
}
|
|
5985
|
-
|
|
6087
|
+
storage.setGValue(this._storageKey, events);
|
|
5986
6088
|
}
|
|
5987
|
-
|
|
5988
|
-
|
|
5989
|
-
|
|
5990
|
-
|
|
5991
|
-
if (!this.base.inited) {
|
|
5992
|
-
throw new Error("请先通过enable初始化基础统计能力");
|
|
6089
|
+
_estimateBytes(value) {
|
|
6090
|
+
try {
|
|
6091
|
+
const text = JSON.stringify(value) || "";
|
|
6092
|
+
return encodeURIComponent(text).replace(/%[A-F\d]{2}/g, "U").length;
|
|
5993
6093
|
}
|
|
5994
|
-
|
|
5995
|
-
|
|
6094
|
+
catch (error) {
|
|
6095
|
+
return 0;
|
|
5996
6096
|
}
|
|
5997
|
-
return this._social;
|
|
5998
6097
|
}
|
|
5999
|
-
|
|
6000
|
-
|
|
6001
|
-
* @param debugMode 调试模式,是否打印日志信息,默认为false
|
|
6002
|
-
* @param senders 上报器列表,允许多个 默认为ALiAnalyticsSender
|
|
6003
|
-
* @returns
|
|
6004
|
-
*/
|
|
6005
|
-
enable(debugMode = false, senders) {
|
|
6006
|
-
if (this.base.inited) {
|
|
6007
|
-
return;
|
|
6008
|
-
}
|
|
6009
|
-
senders = senders || this.createBehaviorSenders();
|
|
6010
|
-
this.base.init(senders, debugMode);
|
|
6011
|
-
this.launch.report("enabled");
|
|
6098
|
+
_currentBufferedBytes() {
|
|
6099
|
+
return this._estimateBytes(this._eventCache);
|
|
6012
6100
|
}
|
|
6013
|
-
|
|
6014
|
-
|
|
6101
|
+
_startLoop() {
|
|
6102
|
+
timer.loop((this._config.cd || 1) * 1000, this, this._doReport, [this._config.count], true);
|
|
6015
6103
|
}
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
const result = {};
|
|
6022
|
-
result[DimensionType[DimensionType.ACTIVITY]] = this.session.getMetrics();
|
|
6023
|
-
result[DimensionType[DimensionType.AD]] = this.ad.getMetrics();
|
|
6024
|
-
result[DimensionType[DimensionType.SOCIAL]] = this.social.getMetrics();
|
|
6025
|
-
return result;
|
|
6104
|
+
_addListen() {
|
|
6105
|
+
App.onHide(() => {
|
|
6106
|
+
void this._doReport(this._eventCache.length, true);
|
|
6107
|
+
});
|
|
6108
|
+
App.onShow(this._restoreEvents, this);
|
|
6026
6109
|
}
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
|
|
6030
|
-
this.
|
|
6031
|
-
|
|
6032
|
-
|
|
6110
|
+
_restoreEvents() {
|
|
6111
|
+
const eventsInLocalStore = this._loadStoredEvents();
|
|
6112
|
+
if (eventsInLocalStore.length > 0) {
|
|
6113
|
+
this._eventCache.unshift(...eventsInLocalStore);
|
|
6114
|
+
console.log(`[KeytopsAnalyticsSender] 恢复本地events(length:${eventsInLocalStore.length}, total:${this._eventCache.length})`);
|
|
6115
|
+
this._scheduleFlush(0);
|
|
6033
6116
|
}
|
|
6034
|
-
return this._assignmentSender;
|
|
6035
6117
|
}
|
|
6036
|
-
|
|
6037
|
-
if (
|
|
6118
|
+
_scheduleFlush(delay = 0) {
|
|
6119
|
+
if (this._flushScheduled) {
|
|
6038
6120
|
return;
|
|
6039
6121
|
}
|
|
6040
|
-
|
|
6041
|
-
|
|
6042
|
-
|
|
6043
|
-
|
|
6044
|
-
|
|
6045
|
-
|
|
6122
|
+
this._flushScheduled = true;
|
|
6123
|
+
timer.once(delay, this, () => {
|
|
6124
|
+
this._flushScheduled = false;
|
|
6125
|
+
void this._doReport();
|
|
6126
|
+
}, null, false);
|
|
6127
|
+
}
|
|
6128
|
+
_buildRequestBody(payload) {
|
|
6129
|
+
const body = JSON.stringify(payload);
|
|
6130
|
+
return {
|
|
6131
|
+
data: body,
|
|
6132
|
+
headers: ["Content-Type", "application/json"],
|
|
6133
|
+
};
|
|
6134
|
+
}
|
|
6135
|
+
buildRequestId() {
|
|
6136
|
+
return `relay-${Date.now()}-${Math.random().toString(16).slice(2, 10)}`;
|
|
6137
|
+
}
|
|
6138
|
+
_sendBatch(events) {
|
|
6139
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6140
|
+
if (!this._config.url) {
|
|
6141
|
+
throw new Error("Keytops analytics relay url is empty");
|
|
6142
|
+
}
|
|
6046
6143
|
const payload = {
|
|
6047
|
-
|
|
6048
|
-
|
|
6049
|
-
|
|
6050
|
-
|
|
6144
|
+
requestId: this.buildRequestId(),
|
|
6145
|
+
appid: publisher.appId,
|
|
6146
|
+
sentAt: this._config.timestampCall ? (this._config.timestampCall() || Date.now()) : Date.now(),
|
|
6147
|
+
events,
|
|
6051
6148
|
};
|
|
6052
|
-
|
|
6053
|
-
|
|
6149
|
+
const request = this._buildRequestBody(payload);
|
|
6150
|
+
const response = yield net.http.sendsync(this._config.url, {
|
|
6151
|
+
method: "post",
|
|
6152
|
+
responseType: "json",
|
|
6153
|
+
data: request.data,
|
|
6154
|
+
headers: request.headers,
|
|
6155
|
+
timeout: KEYTOPS_REQUEST_TIMEOUT,
|
|
6156
|
+
});
|
|
6157
|
+
const code = response && response.code;
|
|
6158
|
+
if (!(code === 0 || code === "0" || code === 200 || code === "200")) {
|
|
6159
|
+
throw new Error(response && response.message ? response.message : "relay request failed");
|
|
6054
6160
|
}
|
|
6055
|
-
|
|
6056
|
-
|
|
6161
|
+
});
|
|
6162
|
+
}
|
|
6163
|
+
_sendBatchWithRetry(events) {
|
|
6164
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6165
|
+
let lastError = null;
|
|
6166
|
+
for (let i = 0; i <= KEYTOPS_RETRY_DELAYS.length; i++) {
|
|
6167
|
+
try {
|
|
6168
|
+
yield this._sendBatch(events);
|
|
6169
|
+
return;
|
|
6170
|
+
}
|
|
6171
|
+
catch (error) {
|
|
6172
|
+
lastError = error;
|
|
6173
|
+
if (i >= KEYTOPS_RETRY_DELAYS.length) {
|
|
6174
|
+
break;
|
|
6175
|
+
}
|
|
6176
|
+
yield new Promise((resolve) => timer.once(KEYTOPS_RETRY_DELAYS[i], this, resolve, null, false));
|
|
6177
|
+
}
|
|
6057
6178
|
}
|
|
6058
|
-
|
|
6059
|
-
|
|
6179
|
+
throw lastError || new Error("relay request failed");
|
|
6180
|
+
});
|
|
6181
|
+
}
|
|
6182
|
+
_doReport(maxCount = this._config.count || 20, store = false) {
|
|
6183
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6184
|
+
if (this._sending || this._eventCache.length <= 0) {
|
|
6185
|
+
return false;
|
|
6060
6186
|
}
|
|
6061
|
-
|
|
6062
|
-
|
|
6187
|
+
this._sending = true;
|
|
6188
|
+
const events = this._eventCache.splice(0, Math.min(this._eventCache.length, maxCount));
|
|
6189
|
+
try {
|
|
6190
|
+
yield this._sendBatchWithRetry(events);
|
|
6191
|
+
return true;
|
|
6192
|
+
}
|
|
6193
|
+
catch (e) {
|
|
6194
|
+
if (!store) {
|
|
6195
|
+
this._eventCache.unshift(...events);
|
|
6196
|
+
console.log(`[KeytopsAnalyticsSender] 事件发送失败,恢复事件到队列,等待下次发送。`);
|
|
6197
|
+
}
|
|
6198
|
+
else {
|
|
6199
|
+
this._storeEvents([...events, ...this._eventCache]);
|
|
6200
|
+
this._eventCache.length = 0;
|
|
6201
|
+
console.log(`[KeytopsAnalyticsSender] 事件发送失败,存储到缓存,等待下次切回游戏。`);
|
|
6202
|
+
}
|
|
6203
|
+
return false;
|
|
6204
|
+
}
|
|
6205
|
+
finally {
|
|
6206
|
+
this._sending = false;
|
|
6207
|
+
if (!store && this._eventCache.length > 0) {
|
|
6208
|
+
const countLimit = this._config.count || 20;
|
|
6209
|
+
const byteLimit = this._config.maxBufferedBytes || 512 * 1024;
|
|
6210
|
+
if (this._eventCache.length >= countLimit || this._currentBufferedBytes() >= byteLimit) {
|
|
6211
|
+
this._scheduleFlush(0);
|
|
6212
|
+
}
|
|
6213
|
+
}
|
|
6063
6214
|
}
|
|
6064
|
-
this.assignmentSender.send(eventName, payload);
|
|
6065
6215
|
});
|
|
6066
6216
|
}
|
|
6067
|
-
|
|
6068
|
-
|
|
6069
|
-
|
|
6070
|
-
|
|
6071
|
-
|
|
6072
|
-
|
|
6073
|
-
|
|
6074
|
-
|
|
6075
|
-
|
|
6076
|
-
|
|
6077
|
-
|
|
6078
|
-
|
|
6079
|
-
|
|
6080
|
-
|
|
6081
|
-
|
|
6082
|
-
|
|
6083
|
-
|
|
6084
|
-
|
|
6085
|
-
|
|
6086
|
-
timer["_update"]();
|
|
6087
|
-
}, 100);
|
|
6088
|
-
this._intervalMap.set(timer, intervalId);
|
|
6089
|
-
}
|
|
6090
|
-
}
|
|
6091
|
-
|
|
6092
|
-
class MathUtils {
|
|
6093
|
-
/**
|
|
6094
|
-
* 随机
|
|
6095
|
-
* @param min
|
|
6096
|
-
* @param max
|
|
6097
|
-
*/
|
|
6098
|
-
static random(min, max) {
|
|
6099
|
-
this.seed = (this.seed * 9301 + 49297) % 233280;
|
|
6100
|
-
let c = this.seed / (233280.0);
|
|
6101
|
-
return min + c * (max - min);
|
|
6102
|
-
}
|
|
6103
|
-
/**
|
|
6104
|
-
* 随机整数值
|
|
6105
|
-
* @param min
|
|
6106
|
-
* @param max
|
|
6107
|
-
*/
|
|
6108
|
-
static randomInt(min, max) {
|
|
6109
|
-
return Math.floor(this.random(min, max + 0.999));
|
|
6110
|
-
}
|
|
6111
|
-
/**
|
|
6112
|
-
* 随机唯一稳定值
|
|
6113
|
-
* @param min
|
|
6114
|
-
* @param max
|
|
6115
|
-
* @param clear 是否清除唯一记录
|
|
6116
|
-
*/
|
|
6117
|
-
static randomOnlyInt(min, max, clear) {
|
|
6118
|
-
if (clear) {
|
|
6119
|
-
this.onlyMap.clear();
|
|
6120
|
-
}
|
|
6121
|
-
let value = this.randomInt(min, max);
|
|
6122
|
-
let tryCount = (max - min) * 2;
|
|
6123
|
-
let tryIndex = 0;
|
|
6124
|
-
while (this.onlyMap.has(value)) {
|
|
6125
|
-
value = this.randomInt(min, max);
|
|
6126
|
-
if (tryIndex > tryCount) {
|
|
6127
|
-
console.error("所有唯一值已取完");
|
|
6128
|
-
return 0;
|
|
6217
|
+
send(eventName, data) {
|
|
6218
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
6219
|
+
const payload = data && typeof data === "object" && !Array.isArray(data)
|
|
6220
|
+
? Object.assign({}, data) : { value: data };
|
|
6221
|
+
let msg_data = {
|
|
6222
|
+
appid: publisher.appId,
|
|
6223
|
+
openid: String(storage.userId || publisher.login.openID || ""),
|
|
6224
|
+
deviceid: App.deviceId,
|
|
6225
|
+
sessionid: App.sessionId,
|
|
6226
|
+
level: ++this._index,
|
|
6227
|
+
data: payload,
|
|
6228
|
+
event: eventName,
|
|
6229
|
+
timestamp: this._config.timestampCall() || Date.now()
|
|
6230
|
+
};
|
|
6231
|
+
this._eventCache.push(msg_data);
|
|
6232
|
+
const countLimit = this._config.count || 20;
|
|
6233
|
+
const byteLimit = this._config.maxBufferedBytes || 512 * 1024;
|
|
6234
|
+
if (this._eventCache.length >= countLimit || this._currentBufferedBytes() >= byteLimit) {
|
|
6235
|
+
this._scheduleFlush(0);
|
|
6129
6236
|
}
|
|
6130
|
-
|
|
6131
|
-
}
|
|
6132
|
-
this.onlyMap.set(value, true);
|
|
6133
|
-
return value;
|
|
6134
|
-
}
|
|
6135
|
-
/**
|
|
6136
|
-
* 获取两点间的角度
|
|
6137
|
-
* @param p1 点1
|
|
6138
|
-
* @param p2 点2
|
|
6139
|
-
*/
|
|
6140
|
-
static getAngle(p1, p2) {
|
|
6141
|
-
return Math.atan((p2.y - p1.y) / (p2.x - p1.x));
|
|
6142
|
-
}
|
|
6143
|
-
/**
|
|
6144
|
-
* 获取两点间的距离
|
|
6145
|
-
* @param p1 点1
|
|
6146
|
-
* @param p2 点2
|
|
6147
|
-
*/
|
|
6148
|
-
static getDistance(p1, p2) {
|
|
6149
|
-
return Math.sqrt(Math.pow(p2.x - p1.x, 2) + Math.pow(p2.y - p1.y, 2));
|
|
6150
|
-
}
|
|
6151
|
-
/**
|
|
6152
|
-
* 将角度转为弧度
|
|
6153
|
-
* @param angle 角度
|
|
6154
|
-
*/
|
|
6155
|
-
static angleToRadian(angle) {
|
|
6156
|
-
return angle * Math.PI / 180;
|
|
6157
|
-
}
|
|
6158
|
-
/**
|
|
6159
|
-
* 求旋转后的点坐标
|
|
6160
|
-
* @param angle 角度
|
|
6161
|
-
* @param point 旋转前的坐标点
|
|
6162
|
-
* @param result
|
|
6163
|
-
*/
|
|
6164
|
-
static getRotationPoint(angle, pointOrDir, result) {
|
|
6165
|
-
if (result == null) {
|
|
6166
|
-
result = { x: 0, y: 0 };
|
|
6167
|
-
}
|
|
6168
|
-
//角度转弧度
|
|
6169
|
-
angle = angle * (Math.PI / 180);
|
|
6170
|
-
result.x = Math.cos(angle) * pointOrDir.x - Math.sin(angle) * pointOrDir.y;
|
|
6171
|
-
result.y = Math.cos(angle) * pointOrDir.y + Math.sin(angle) * pointOrDir.x;
|
|
6172
|
-
return result;
|
|
6237
|
+
});
|
|
6173
6238
|
}
|
|
6174
6239
|
}
|
|
6175
|
-
/**
|
|
6176
|
-
* 随机种子
|
|
6177
|
-
*/
|
|
6178
|
-
MathUtils.seed = 10;
|
|
6179
|
-
/**
|
|
6180
|
-
* 唯一记录
|
|
6181
|
-
*/
|
|
6182
|
-
MathUtils.onlyMap = new Map();
|
|
6183
6240
|
|
|
6184
|
-
class
|
|
6185
|
-
|
|
6186
|
-
|
|
6187
|
-
|
|
6188
|
-
|
|
6241
|
+
class AnalyticsManager {
|
|
6242
|
+
constructor() {
|
|
6243
|
+
this._base = null;
|
|
6244
|
+
this._level = null;
|
|
6245
|
+
this._ad = null;
|
|
6246
|
+
this._session = null;
|
|
6247
|
+
this._launch = null;
|
|
6248
|
+
this._social = null;
|
|
6249
|
+
this._assignmentSender = null;
|
|
6250
|
+
}
|
|
6251
|
+
hasRelayUrl(config) {
|
|
6252
|
+
return !!String(config && config.url || "").trim();
|
|
6253
|
+
}
|
|
6254
|
+
hasAliDirectConfig(config) {
|
|
6255
|
+
return !!String(config && config.project || "").trim() && !!String(config && config.logstore || "").trim();
|
|
6256
|
+
}
|
|
6257
|
+
createBehaviorSenders() {
|
|
6258
|
+
const analyticsConfig = remoteConfigService.config(SystemOnlineConfig).getAnalyticsConfig();
|
|
6259
|
+
if (this.hasRelayUrl(analyticsConfig)) {
|
|
6260
|
+
return [new KeytopsAnalyticsSender(analyticsConfig)];
|
|
6189
6261
|
}
|
|
6190
|
-
|
|
6262
|
+
if (this.hasAliDirectConfig(analyticsConfig)) {
|
|
6263
|
+
return [new ALiAnalyticsSender(analyticsConfig)];
|
|
6264
|
+
}
|
|
6265
|
+
return [];
|
|
6191
6266
|
}
|
|
6192
6267
|
/**
|
|
6193
|
-
*
|
|
6194
|
-
* @param url
|
|
6195
|
-
* @returns
|
|
6268
|
+
* 基础统计能力
|
|
6196
6269
|
*/
|
|
6197
|
-
|
|
6198
|
-
if (
|
|
6199
|
-
|
|
6270
|
+
get base() {
|
|
6271
|
+
if (!this._base) {
|
|
6272
|
+
this._base = new AnalyticsAble();
|
|
6200
6273
|
}
|
|
6201
|
-
return
|
|
6274
|
+
return this._base;
|
|
6202
6275
|
}
|
|
6203
6276
|
/**
|
|
6204
|
-
*
|
|
6205
|
-
* @param str
|
|
6277
|
+
* 关卡统计能力,用于关卡分析
|
|
6206
6278
|
*/
|
|
6207
|
-
|
|
6208
|
-
if (
|
|
6209
|
-
|
|
6279
|
+
get level() {
|
|
6280
|
+
if (!this.base.inited) {
|
|
6281
|
+
throw new Error("请先通过enable初始化基础统计能力");
|
|
6210
6282
|
}
|
|
6211
|
-
|
|
6283
|
+
if (!this._level) {
|
|
6284
|
+
this._level = new LevelAnalyticsAble(this.base);
|
|
6285
|
+
}
|
|
6286
|
+
return this._level;
|
|
6212
6287
|
}
|
|
6213
6288
|
/**
|
|
6214
|
-
|
|
6215
|
-
|
|
6216
|
-
|
|
6217
|
-
|
|
6218
|
-
|
|
6219
|
-
|
|
6220
|
-
|
|
6221
|
-
|
|
6222
|
-
|
|
6223
|
-
return
|
|
6289
|
+
* 广告统计能力,用于广告分析
|
|
6290
|
+
*/
|
|
6291
|
+
get ad() {
|
|
6292
|
+
if (!this.base.inited) {
|
|
6293
|
+
throw new Error("请先通过enable初始化基础统计能力");
|
|
6294
|
+
}
|
|
6295
|
+
if (!this._ad) {
|
|
6296
|
+
this._ad = new ADAnalyticsAble(this.base);
|
|
6297
|
+
}
|
|
6298
|
+
return this._ad;
|
|
6224
6299
|
}
|
|
6225
6300
|
/**
|
|
6226
|
-
*
|
|
6227
|
-
* @param str
|
|
6228
|
-
* @param rest
|
|
6229
|
-
*
|
|
6230
|
-
* @example
|
|
6231
|
-
*
|
|
6232
|
-
* var str:string = "here is some info '{0}' and {1}";
|
|
6233
|
-
* trace(StringUtil.substitute(str, 15.4, true));
|
|
6234
|
-
*
|
|
6235
|
-
* // this will output the following string:
|
|
6236
|
-
* // "here is some info '15.4' and true"
|
|
6301
|
+
* 会话统计能力,用于会话分析
|
|
6237
6302
|
*/
|
|
6238
|
-
|
|
6239
|
-
if (
|
|
6240
|
-
|
|
6241
|
-
// Replace all of the parameters in the msg string.
|
|
6242
|
-
var len = rest.length;
|
|
6243
|
-
var args;
|
|
6244
|
-
if (len == 1 && rest[0] instanceof Array) {
|
|
6245
|
-
args = rest[0];
|
|
6246
|
-
len = args.length;
|
|
6247
|
-
}
|
|
6248
|
-
else {
|
|
6249
|
-
args = rest;
|
|
6303
|
+
get session() {
|
|
6304
|
+
if (!this.base.inited) {
|
|
6305
|
+
throw new Error("请先通过enable初始化基础统计能力");
|
|
6250
6306
|
}
|
|
6251
|
-
|
|
6252
|
-
|
|
6307
|
+
if (!this._session) {
|
|
6308
|
+
this._session = new SessionAnalyticsAble(this.base);
|
|
6253
6309
|
}
|
|
6254
|
-
return
|
|
6310
|
+
return this._session;
|
|
6255
6311
|
}
|
|
6256
6312
|
/**
|
|
6257
|
-
*
|
|
6258
|
-
* 格式化数字
|
|
6259
|
-
* @param amount 目标数字
|
|
6260
|
-
* @param format 支持{1:"", 100:"百", 1000:"千", 10000:"万", 100000:"十万", 1000000:"百万", 10000000:"千万", 100000000:"亿", 1000000000000:"兆"}等
|
|
6261
|
-
* @returns
|
|
6313
|
+
* 启动统计能力,用于启动性能分析
|
|
6262
6314
|
*/
|
|
6263
|
-
|
|
6264
|
-
if (!
|
|
6265
|
-
|
|
6315
|
+
get launch() {
|
|
6316
|
+
if (!this.base.inited) {
|
|
6317
|
+
throw new Error("请先通过enable初始化基础统计能力");
|
|
6266
6318
|
}
|
|
6267
|
-
|
|
6268
|
-
|
|
6269
|
-
if (format.hasOwnProperty(key)) {
|
|
6270
|
-
formatKey.push(Number(key));
|
|
6271
|
-
}
|
|
6319
|
+
if (!this._launch) {
|
|
6320
|
+
this._launch = new LaunchAnalyticsAble(this.base);
|
|
6272
6321
|
}
|
|
6273
|
-
|
|
6274
|
-
|
|
6322
|
+
return this._launch;
|
|
6323
|
+
}
|
|
6324
|
+
/**
|
|
6325
|
+
* 社交统计能力
|
|
6326
|
+
*/
|
|
6327
|
+
get social() {
|
|
6328
|
+
if (!this.base.inited) {
|
|
6329
|
+
throw new Error("请先通过enable初始化基础统计能力");
|
|
6275
6330
|
}
|
|
6276
|
-
|
|
6277
|
-
|
|
6278
|
-
let total = amount;
|
|
6279
|
-
for (let i = 0; i < formatKey.length; i++) {
|
|
6280
|
-
const key = formatKey[i];
|
|
6281
|
-
let count = Math.floor(Math.abs(total / key)) * (total / Math.abs(total));
|
|
6282
|
-
if (count == 0) {
|
|
6283
|
-
continue;
|
|
6284
|
-
}
|
|
6285
|
-
total = total % key;
|
|
6286
|
-
result.push(`${count}${format[key]}`);
|
|
6331
|
+
if (!this._social) {
|
|
6332
|
+
this._social = new SocialAnalyticsAble(this.base);
|
|
6287
6333
|
}
|
|
6288
|
-
return
|
|
6334
|
+
return this._social;
|
|
6289
6335
|
}
|
|
6290
6336
|
/**
|
|
6291
|
-
*
|
|
6292
|
-
* @param
|
|
6293
|
-
* @param
|
|
6294
|
-
* @param pad 填充文本,默认为“0”
|
|
6295
|
-
* @example
|
|
6296
|
-
* formatSecond(86400, "ddd天hh小时mm分ss秒")=>"001天00小时00分00秒"
|
|
6297
|
-
* formatSecond(86400, "dd天hh小时mm分ss秒")=>"01天00小时00分00秒"
|
|
6298
|
-
* formatSecond(86400, "hh小时mm分ss秒")=>"24小时00分00秒"
|
|
6299
|
-
* formatSecond(86400, "mm分ss秒")=>"1440分00秒"
|
|
6337
|
+
* 启用统计(基础统计能力)
|
|
6338
|
+
* @param debugMode 调试模式,是否打印日志信息,默认为false
|
|
6339
|
+
* @param senders 上报器列表,允许多个 默认为ALiAnalyticsSender
|
|
6300
6340
|
* @returns
|
|
6301
6341
|
*/
|
|
6302
|
-
|
|
6303
|
-
|
|
6304
|
-
|
|
6305
|
-
{ unit: 3600, regExp: /H{1,2}/i },
|
|
6306
|
-
{ unit: 60, regExp: /m{1,2}/i },
|
|
6307
|
-
{ unit: 1, regExp: /s{1,2}/i },
|
|
6308
|
-
];
|
|
6309
|
-
for (let i = 0; i < formatArr.length; i++) {
|
|
6310
|
-
const element = formatArr[i];
|
|
6311
|
-
let match = element.regExp.exec(format);
|
|
6312
|
-
if (match) {
|
|
6313
|
-
format = format.replace(element.regExp, Math.floor(second / element.unit).toFixed(0).padStart(match[0].length, pad));
|
|
6314
|
-
second = second % element.unit;
|
|
6315
|
-
}
|
|
6342
|
+
enable(debugMode = false, senders) {
|
|
6343
|
+
if (this.base.inited) {
|
|
6344
|
+
return;
|
|
6316
6345
|
}
|
|
6317
|
-
|
|
6346
|
+
senders = senders || this.createBehaviorSenders();
|
|
6347
|
+
this.base.init(senders, debugMode);
|
|
6348
|
+
this.launch.report("enabled");
|
|
6349
|
+
}
|
|
6350
|
+
get version() {
|
|
6351
|
+
return "v1";
|
|
6318
6352
|
}
|
|
6319
6353
|
/**
|
|
6320
|
-
*
|
|
6321
|
-
* @
|
|
6322
|
-
* @param format
|
|
6323
|
-
* @param pad 填充文本,默认为“0”
|
|
6324
|
-
* @returns
|
|
6354
|
+
* 获取所有维度的数据
|
|
6355
|
+
* @returns 所有维度的数据
|
|
6325
6356
|
*/
|
|
6326
|
-
|
|
6327
|
-
|
|
6328
|
-
|
|
6329
|
-
|
|
6330
|
-
|
|
6331
|
-
|
|
6332
|
-
|
|
6333
|
-
|
|
6334
|
-
|
|
6335
|
-
|
|
6336
|
-
|
|
6337
|
-
|
|
6338
|
-
|
|
6339
|
-
let match = element.r.exec(format);
|
|
6340
|
-
if (match) {
|
|
6341
|
-
format = format.replace(element.r, element.to.toString().padStart(match[0].length, "0"));
|
|
6342
|
-
}
|
|
6357
|
+
getMetrics() {
|
|
6358
|
+
const result = {};
|
|
6359
|
+
result[DimensionType[DimensionType.ACTIVITY]] = this.session.getMetrics();
|
|
6360
|
+
result[DimensionType[DimensionType.AD]] = this.ad.getMetrics();
|
|
6361
|
+
result[DimensionType[DimensionType.SOCIAL]] = this.social.getMetrics();
|
|
6362
|
+
return result;
|
|
6363
|
+
}
|
|
6364
|
+
get assignmentSender() {
|
|
6365
|
+
if (!this._assignmentSender) {
|
|
6366
|
+
const analyticsConfig = remoteConfigService.config(SystemOnlineConfig).getAssignmentConfig();
|
|
6367
|
+
this._assignmentSender = this.hasRelayUrl(analyticsConfig)
|
|
6368
|
+
? new KeytopsAnalyticsSender(analyticsConfig, analyticsConfig.exclude || ["view"])
|
|
6369
|
+
: new EmptyAnalyticsSender();
|
|
6343
6370
|
}
|
|
6344
|
-
return
|
|
6371
|
+
return this._assignmentSender;
|
|
6372
|
+
}
|
|
6373
|
+
reportAssignments(resolveMeta, levelid) {
|
|
6374
|
+
if (!resolveMeta) {
|
|
6375
|
+
return;
|
|
6376
|
+
}
|
|
6377
|
+
const method = resolveMeta.method;
|
|
6378
|
+
if (!method) {
|
|
6379
|
+
throw new Error("method is required");
|
|
6380
|
+
}
|
|
6381
|
+
const eventName = `${method}_assignment`;
|
|
6382
|
+
(resolveMeta.matchedUnits || []).forEach((unit) => {
|
|
6383
|
+
const payload = {
|
|
6384
|
+
method: method,
|
|
6385
|
+
resolve_key: resolveMeta.resolveKey,
|
|
6386
|
+
config_name: unit.configName,
|
|
6387
|
+
fingerprint: unit.fingerprint,
|
|
6388
|
+
};
|
|
6389
|
+
if (unit.publishId) {
|
|
6390
|
+
payload.publish_id = unit.publishId;
|
|
6391
|
+
}
|
|
6392
|
+
if (unit.variantId) {
|
|
6393
|
+
payload.variant_id = unit.variantId;
|
|
6394
|
+
}
|
|
6395
|
+
if (unit.variantFingerprint) {
|
|
6396
|
+
payload.variant_fingerprint = unit.variantFingerprint;
|
|
6397
|
+
}
|
|
6398
|
+
if (levelid !== undefined) {
|
|
6399
|
+
payload.level_id = levelid;
|
|
6400
|
+
}
|
|
6401
|
+
this.assignmentSender.send(eventName, payload);
|
|
6402
|
+
});
|
|
6345
6403
|
}
|
|
6346
6404
|
}
|
|
6405
|
+
/**
|
|
6406
|
+
* 埋点统计能力
|
|
6407
|
+
*/
|
|
6408
|
+
const analytics = new AnalyticsManager();
|
|
6347
6409
|
|
|
6348
6410
|
class KKTServer {
|
|
6349
6411
|
requestConfig(context) {
|
|
@@ -6462,7 +6524,6 @@ class Application {
|
|
|
6462
6524
|
this.impl.launch();
|
|
6463
6525
|
this._resetLaunchTimes();
|
|
6464
6526
|
timer.enable();
|
|
6465
|
-
analytics.enable(debug);
|
|
6466
6527
|
let isFirstLaunch = this.isFirstLaunch;
|
|
6467
6528
|
console.log(`App Launch Done. isFirstLaunch:${isFirstLaunch}`);
|
|
6468
6529
|
});
|