Countly下载的原工程中,如博客http://blog.csdn.net/changemyself/article/details/12653151,所说的,一共只有两个包,一个管理UDID的,一个是Countly的核心。
首先说一个巨大的变化,不同于上面博客的是,我与2014年7月下载Countly Android SDK,其记录缓存机制已经不使用数据库,而全部改用SharedPreference,使得容错能力获得极大提高。
下面先贴出我修改后的工程结构:
OpenUDID包我没有做修改,还是沿用原来的源码,
countly包:
CountlyStore.java,DeviceInfo.java,UploadUtils.java三个文件都是辅助类,Countly是核心类,首先简要了解三个辅助类的源码
UploadUtils.java 是我自己添加了post上传的封装方法,就是post上传,就占用篇幅了。
DeviceInfo : 主要是获取手机中的各类信息,其中的channel表示市场渠道号,是自己在manifest的metadata里定义的
而getMetrics是对外的主要方法,可以把信息收集起来,并做成json字符串的格式
class DeviceInfo {
//---------------自定义meta-------------------
private static String DEFAULT_CHANNEL ="1000";
/**
* 获取发布渠道信息
* @param context
* @return
*/
public static String getChannel(Context context){
String msg = DEFAULT_CHANNEL;
ApplicationInfo appInfo;
try {
appInfo = context.getPackageManager()
.getApplicationInfo(context.getPackageName(),
PackageManager.GET_META_DATA);
msg=appInfo.metaData.getInt("channel")+"";
} catch (Exception e) {
// TODO Auto-generated catch block
//e.printStackTrace();
msg="0000";
}
return msg;
}
//---------------唯一标识UDID-------------------
/**
* 设备通用统一标识符(注意是从OpenUDID_manager里取,不是直接获得)
* @return
*/
public static String getUDID() {
return OpenUDID_manager.isInitialized() == false ? "REPLACE_UDID" : OpenUDID_manager.getOpenUDID();
}
//---------------系统固有信息-------------------
/**
* get current connected network type
*
* @return
*/
public static String getNetType(Context context) {
String type = null;
ConnectivityManager conMan = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
NetworkInfo info = conMan.getActiveNetworkInfo();
if (info != null) // TYPE_MOBILE
{
switch (info.getType()) {
case ConnectivityManager.TYPE_MOBILE:
switch (info.getSubtype()) {
case TelephonyManager.NETWORK_TYPE_EDGE:
type = "EDGE";
break;
case TelephonyManager.NETWORK_TYPE_CDMA:
type = "CDMA";
break;
case TelephonyManager.NETWORK_TYPE_GPRS:
type = "GPRS";
break;
case TelephonyManager.NETWORK_TYPE_EVDO_0:
type = "EVDO_0";
break;
case TelephonyManager.NETWORK_TYPE_UNKNOWN:
type = "UNKOWN";
break;
}
break;
case ConnectivityManager.TYPE_WIFI:
type = "wifi";
break;
}
} else
type = "outofnetwork";
return type;
}
/**
* 系统类型
* @return
*/
public static String getOS() {
return "Android";
}
/**
* 系统版本号
* @return
*/
public static String getOSVersion() {
return android.os.Build.VERSION.RELEASE;
}
/**
* 手机型号
* @return
*/
public static String getDevice() {
return android.os.Build.MODEL;
}
/**
* 分辨率
* @param context
* @return like “480x800”
*/
public static String getResolution(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
return metrics.widthPixels + "x" + metrics.heightPixels;
}
/**
* 获取屏幕密度分级
* @param context
* @return
*/
public static String getDensity(Context context) {
int density = context.getResources().getDisplayMetrics().densityDpi;
switch (density) {
case DisplayMetrics.DENSITY_LOW:
return "LDPI";
case DisplayMetrics.DENSITY_MEDIUM:
return "MDPI";
case DisplayMetrics.DENSITY_TV:
return "TVDPI";
case DisplayMetrics.DENSITY_HIGH:
return "HDPI";
case DisplayMetrics.DENSITY_XHIGH:
return "XHDPI";
case DisplayMetrics.DENSITY_XXHIGH:
return "XXHDPI";
// not support on android 4.1.2
// case DisplayMetrics.DENSITY_XXXHIGH:
// return "XXXHDPI";
default:
return "";
}
}
/**
* 运营商名
* @param context
* @return
*/
public static String getCarrier(Context context) {
try {
TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
return manager.getNetworkOperatorName();
} catch (NullPointerException npe) {
npe.printStackTrace();
Log.e("Countly", "No carrier found");
}
return "";
}
/**
* 获得本地化信息
* @return “语言_国家”
*/
public static String getLocale() {
Locale locale = Locale.getDefault();
return locale.getLanguage() + "_" + locale.getCountry();
}
/**
* app 版本
* @param context
* @return
*/
public static String appVersion(Context context) {
String result = "1.0";
try {
result = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;
} catch (NameNotFoundException e) {
}
return result;
}
/**
* 把设备和app信息组装成json
* @param context
* @return
*/
public static String getMetrics(Context context) {
String result = "";
JSONObject json = new JSONObject();
try {
json.put("_device", getDevice());
json.put("_os", getOS());
json.put("_os_version", getOSVersion());
json.put("_carrier", getCarrier(context));
json.put("_resolution", getResolution(context));
json.put("_density", getDensity(context));
json.put("_locale", getLocale());
json.put("_app_version", appVersion(context));
json.put("_channel",getChannel(context));
} catch (JSONException e) {
e.printStackTrace();
}
result = json.toString();
//Log.d("metric origin",result);
try {
//确认编码为utf-8字符
result = java.net.URLEncoder.encode(result, "UTF-8");
} catch (UnsupportedEncodingException e) {
}
return result;
}
}
CountlyStore:
注意其数据存储的方法,都是每次把全部数据从pref里取出,整体修改,然后再整体替换的。由于数据其实不大,实际占用的data控件也就上百k,这样简化了操作,规避了数据库的适配危险,程序也比以前的版本好读多了。
/**
* 负责向手机本地存储数据
* connection :表示activity的启动和关闭等,在beginSession,Updatesesstion等里边改变connections
* event: 自定义的事件,用法参见样例。
* @author Jackland_zgl
*
*/
class CountlyStore {
private static final String TAG = "COUNTLY_STORE";
private static final String PREFERENCES = "COUNTLY_STORE";
private static final String DELIMITER = ";";
private static final String CONNECTIONS_PREFERENCE = "CONNECTIONS";
private static final String EVENTS_PREFERENCE = "EVENTS";
private SharedPreferences preferences;
/**
* 初始化获取SharedPreference
* @param ctx
*/
protected CountlyStore(Context ctx) {
preferences = ctx.getSharedPreferences(PREFERENCES, Context.MODE_PRIVATE);
}
public String[] connections() {
String array = preferences.getString(CONNECTIONS_PREFERENCE, null);
return array == null || "".equals(array) ? new String[0] : array.split(DELIMITER);
}
public String connectionsString() {
String array = preferences.getString(CONNECTIONS_PREFERENCE, null);
//if (array!=null) Log.d("connections",array);
return array;
}
public String[] events() {
String array = preferences.getString(EVENTS_PREFERENCE, null);
return array == null || "".equals(array) ? new String[0] : array.split(DELIMITER);
}
/**
* 返回按时间戳排序的事件
* @return
*/
public List<Event> eventsList() {
String[] array = events();
if (array.length == 0) return new ArrayList<Event>();
else {
List<Event> events = new ArrayList<Event>();
for (String s : array) {
try {
events.add(jsonToEvent(new JSONObject(s)));
} catch (JSONException e) {
Log.e(TAG, "Cannot parse Event json", e);
}
}
Collections.sort(events, new Comparator<Event>() {
@Override
public int compare(Event e1, Event e2) {
return e2.timestamp - e1.timestamp;
}
});
return events;
}
}
public boolean isEmptyConnections() {
return connections().length == 0;
}
public boolean isEmptyEvents() {
return events().length == 0;
}
public void addConnection(String str) {
List<String> connections = new ArrayList<String>(Arrays.asList(connections()));
connections.add(str);
preferences.edit().putString(CONNECTIONS_PREFERENCE, join(connections, DELIMITER)).commit();
}
public void removeAllConnection() {
List<String> connections = new ArrayList<String>(Arrays.asList(connections()));
connections.clear();
preferences.edit().putString(CONNECTIONS_PREFERENCE, join(connections, DELIMITER)).commit();
}
public void removeConnection(String str) {
List<String> connections = new ArrayList<String>(Arrays.asList(connections()));
connections.remove(str);
preferences.edit().putString(CONNECTIONS_PREFERENCE, join(connections, DELIMITER)).commit();
}
public void addEvent(Event event) {
List<Event> events = eventsList();
if (!events.contains(event)) events.add(event);
preferences.edit().putString(EVENTS_PREFERENCE, joinEvents(events, DELIMITER)).commit();
}
public void addEvent(String key, Map<String, String> segmentation, int count, double sum) {
List<Event> events = eventsList();
Event event = null;
//判重
for (Event e : events) if (e.key != null && e.key.equals(key)) event = e;
//如果是新事件则新建,否则只累加 count 和 sum值
if (event == null) {
event = new Event();
event.key = key;
event.segmentation = segmentation;
event.count = 0;
event.sum = 0;
event.timestamp = (int) (System.currentTimeMillis() / 1000);
} else {
removeEvent(event);
event.timestamp = Math.round((event.timestamp + (System.currentTimeMillis() / 1000)) / 2);
}
event.count += count;
event.sum += sum;
addEvent(event);
}
public void removeEvent(Event event) {
List<Event> events = eventsList();
events.remove(event);
preferences.edit().putString(EVENTS_PREFERENCE, joinEvents(events, DELIMITER)).commit();
}
public void removeEvents(Collection<Event> eventsToRemove) {
List<Event> events = eventsList();
for (Event e : eventsToRemove) events.remove(e);
preferences.edit().putString(EVENTS_PREFERENCE, joinEvents(events, DELIMITER)).commit();
}
protected static JSONObject eventToJSON(Event event) {
JSONObject json = new JSONObject();
try {
json.put("key", event.key);
json.put("count", event.count);
json.put("sum", event.sum);
json.put("timestamp", event.timestamp);
if (event.segmentation != null) {
json.put("segmentation", new JSONObject(event.segmentation));
}
} catch (JSONException e) {
e.printStackTrace();
}
return json;
}
/**
* json对象组装成event
* @param json
* @return
*/
protected static Event jsonToEvent(JSONObject json) {
Event event = new Event();
try {
event.key = json.get("key").toString();
event.count = Integer.valueOf(json.get("count").toString());
event.sum = Double.valueOf(json.get("sum").toString());
event.timestamp = Integer.valueOf(json.get("timestamp").toString());
if (json.has("segmentation")) {
JSONObject segm = json.getJSONObject("segmentation");
HashMap<String, String> segmentation = new HashMap<String, String>();
Iterator nameItr = segm.keys();
while (nameItr.hasNext()) {
Object obj = nameItr.next();
if (obj instanceof String) {
segmentation.put((String) obj, ((JSONObject) json.get("segmentation")).getString((String) obj));
}
}
event.segmentation = segmentation;
}
} catch (JSONException e) {
e.printStackTrace();
}
return event;
}
/**
*
* 把所有event转化成一个String,这个方法是将event转换成list然后调用join
* @param collection
* @param delimiter
* @return
*/
private static String joinEvents(Collection<Event> collection, String delimiter) {
List<String> strings = new ArrayList<String>();
for (Event e : collection) strings.add(eventToJSON(e).toString());
return join(strings, delimiter);
}
/**
* 用分隔符连接collection里的String
* @param collection
* @param delimiter
* @return
*/
private static String join(Collection<String> collection, String delimiter) {
StringBuilder builder = new StringBuilder();
int i = 0;
for (String s : collection) {
builder.append(s);
if (++i < collection.size()) builder.append(delimiter);
}
return builder.toString();
}
}
最后是Countly:其中的recordCrashEvent是我为了区别崩溃事件和一般事件加的,因为设计服务器时,崩溃信息可能要发送到另一处,所以崩溃日志要带上全部metric
/**
* 周期性维护,事件队列,网络发送队列,本地信息存储 三个事务
* @author Jackland_zgl
*
*/
public class Countly {
private static Countly sharedInstance_;
private Timer timer_;
private ConnectionQueue queue_;
private EventQueue eventQueue_;
private boolean isVisible_;
private double unsentSessionLength_;
private double lastTime_;
private int activityCount_;
private CountlyStore countlyStore_;
protected static final String SDK_VERSION = "2.0.1.1";
protected static final int SESSION_DURATION_WHEN_TIME_ADJUSTED = 15;
protected static final int MAX_CONNECTIONS_ALLOWED = 100; //最多缓存记录的条数
protected static final int HALF_CONNECTIONS_CLEANED = 35; //最多
protected static final int TIMER_DURATION_DELAY = 20;
protected static final int TIMER_DURATION_LONG = 80;
protected static final int TIMER_DURATION_SHORT = 20;
protected static final int TIMER_DURATION = TIMER_DURATION_LONG;
public final static boolean LOG = false; //Display or not debug message
/**
Countly实例,在调用的时候只使用 sharedInstance 这个名字
使用时只有一个实例存在
*/
static public Countly sharedInstance() {
if (sharedInstance_ == null)
sharedInstance_ = new Countly();
return sharedInstance_;
}
private Countly() {
queue_ = new ConnectionQueue();
timer_ = new Timer();
timer_.schedule(new TimerTask() {
@Override
public void run() {
onTimer();
}
}, TIMER_DURATION_DELAY * 1000, TIMER_DURATION * 1000);
isVisible_ = false;
unsentSessionLength_ = 0;
activityCount_ = 0;
}
public void init(Context context, String serverURL, String appKey) {
OpenUDID_manager.sync(context);
countlyStore_ = new CountlyStore(context);
queue_.setContext(context);
queue_.setServerURL(serverURL);
queue_.setAppKey(appKey);
queue_.setCountlyStore(countlyStore_);
UploadUtils.UPLOAD_URL = serverURL;
eventQueue_ = new EventQueue(countlyStore_);
}
public void onStart() {
activityCount_++;
if (activityCount_ == 1)
onStartHelper();
}
public void onStop() {
activityCount_--;
if (activityCount_ == 0)
onStopHelper();
}
/**
* 第一次启动跟踪时,队列和时间初始化
*/
public void onStartHelper() {
lastTime_ = System.currentTimeMillis() / 1000.0;
queue_.beginSession();
isVisible_ = true;
}
/**
* 所有activity完成跟踪,则记录所有时间,计算完成时间
*/
public void onStopHelper() {
//将自定义事件中所有事件推入总的队列里
if (eventQueue_.size() > 0)
queue_.recordEvents(eventQueue_.events());
double currTime = System.currentTimeMillis() / 1000.0;
unsentSessionLength_ += currTime - lastTime_;
int duration = (int) unsentSessionLength_;
queue_.endSession(duration);
unsentSessionLength_ -= duration;
isVisible_ = false;
}
/**
* 记录崩溃事件,传入错误信息string即可
* @param errorMessage
* 关键词:ErrorMessage , key:crash
*/
public void recordCrashEvent(String errorMessage){
HashMap<String,String> map = new HashMap<String,String>();
errorMessage = errorMessage.replaceAll(";", "_");
map.put("ErrorMessage", errorMessage);
recordEventWithMetrics("crash",map, 1);
}
/**
* 记录意见反馈行为,传入意见反馈的string即可
* @param fbMessage
* 关键词:FeedBackMessage , key:feedback
*/
public void recordFeedbackEvent(String fbMessage){
HashMap<String,String> map = new HashMap<String,String>();
fbMessage=fbMessage.replaceAll(";","_");
map.put("FeedbackMessage", fbMessage);
recordEventWithMetrics("feedback",map, 1);
}
/**
* 记录事件,如果事件队列里有超过10个事件则发出去
* @param key
*/
public void recordEvent(String key) {
eventQueue_.recordEvent(key);
if (eventQueue_.size() >= 10)
queue_.recordEvents(eventQueue_.events());
}
public void recordEvent(String key, int count) {
eventQueue_.recordEvent(key, count);
if (eventQueue_.size() >= 10)
queue_.recordEvents(eventQueue_.events());
}
public void recordEvent(String key, int count, double sum) {
eventQueue_.recordEvent(key, count, sum);
if (eventQueue_.size() >= 10)
queue_.recordEvents(eventQueue_.events());
}
public void recordEvent(String key, Map<String, String> segmentation, int count) {
eventQueue_.recordEvent(key, segmentation, count);
if (eventQueue_.size() >= 10)
queue_.recordEvents(eventQueue_.events());
}
/**
* 让需要发送的自定义时间内加入metrics信息
* @param key
* @param segmentation
* @param count
*/
public void recordEventWithMetrics(String key, Map<String, String> segmentation, int count) {
eventQueue_.recordEvent(key, segmentation, count);
if (eventQueue_.size() >= 10)
queue_.recordEventsWithMetrics(eventQueue_.events());
}
public void recordEvent(String key, Map<String, String> segmentation, int count, double sum) {
eventQueue_.recordEvent(key, segmentation, count, sum);
if (eventQueue_.size() >= 10)
queue_.recordEvents(eventQueue_.events());
}
/**
* 定时计算时间更新session信息,并将发送到服务器
*/
private void onTimer() {
if (isVisible_ == false)
return;
double currTime = System.currentTimeMillis() / 1000.0;
unsentSessionLength_ += currTime - lastTime_;
lastTime_ = currTime;
int duration = (int) unsentSessionLength_;
queue_.updateSession(duration);
unsentSessionLength_ -= duration;
if (eventQueue_.size() > 0)
queue_.recordEvents(eventQueue_.events());
}
}
/**
* 连接队列,主要控制网络发送,
* @author Jackland_zgl
*
*/
class ConnectionQueue {
private CountlyStore store_;
private Thread thread_ = null;
private String appKey_;
private Context context_;
private String serverURL_;
public void setAppKey(String appKey) {
appKey_ = appKey;
}
public void setContext(Context context) {
context_ = context;
}
public void setServerURL(String serverURL) {
serverURL_ = serverURL;
}
public void setCountlyStore(CountlyStore countlyStore) {
store_ = countlyStore;
}
public void beginSession() {
String data;
data = "app_key=" + appKey_;
data += "&" + "device_id=" + DeviceInfo.getUDID();
data += "&" + "timestamp=" + (long) (System.currentTimeMillis() / 1000.0);
data += "&" + "sdk_version=" + Countly.SDK_VERSION;
data += "&" + "begin_session=" + "1";
data += "&" + "metrics=" + DeviceInfo.getMetrics(context_);
store_.addConnection(data);
tick();
}
public void updateSession(int duration) {
String data;
data = "app_key=" + appKey_;
data += "&" + "device_id=" + DeviceInfo.getUDID();
data += "&" + "timestamp=" + (long) (System.currentTimeMillis() / 1000.0);
data += "&" + "session_duration=" + (duration > 0 ? duration : Countly.SESSION_DURATION_WHEN_TIME_ADJUSTED);
store_.addConnection(data);
tick();
}
public void endSession(int duration) {
String data;
data = "app_key=" + appKey_;
data += "&" + "device_id=" + DeviceInfo.getUDID();
data += "&" + "timestamp=" + (long) (System.currentTimeMillis() / 1000.0);
data += "&" + "end_session=" + "1";
data += "&" + "session_duration=" + (duration > 0 ? duration : Countly.SESSION_DURATION_WHEN_TIME_ADJUSTED);
store_.addConnection(data);
tick();
}
/**
* 注意这个方法,是唯一能导致自定义的event(即不是connection)上传到服务器的
* @param events
*/
public void recordEvents(String events) {
String data;
data = "app_key=" + appKey_;
data += "&" + "device_id=" + DeviceInfo.getUDID();
data += "&" + "timestamp=" + (long) (System.currentTimeMillis() / 1000.0);
data += "&" + "events=" + events;
if ((events.indexOf("crash")>0) || (events.indexOf("feedback")>0))
data += "&" + "metrics=" + DeviceInfo.getMetrics(context_);
store_.addConnection(data);
tick();
}
/**
* 注意这个方法,是唯一能导致自定义的event(即不是connection)上传到服务器的
* @param events
*/
public void recordEventsWithMetrics(String events) {
String data;
data = "app_key=" + appKey_;
data += "&" + "device_id=" + DeviceInfo.getUDID();
data += "&" + "timestamp=" + (long) (System.currentTimeMillis() / 1000.0);
data += "&" + "events=" + events;
data += "&" + "metrics=" + DeviceInfo.getMetrics(context_);
store_.addConnection(data);
tick();
}
/**
* 记录事件的心跳
* 能够上传的永远只有connection 上边的函数recordEvents 也是把所有的event信息加入到connection里。
*/
private void tick() {
if (thread_ != null && thread_.isAlive())
return;
if (store_.isEmptyConnections())
return;
thread_ = new Thread() {
@Override
public void run() {
//uploadByGetOneByOne();
uploadByPostAll();
}
};
thread_.start();
}
/**
* 用post方法一个连接上传全部数据,如果发送不成功则考虑清除数据
*/
private synchronized void uploadByPostAll(){
String content = store_.connectionsString();
if (content!=null) Log.d("post",content);
int success = UploadUtils.doUploadString(context_ ,content);
if (success==1){
store_.removeAllConnection();
}else{
//如果发送不成功而且留存的数量较大则清除掉前一半
String[] sessions = store_.connections();
if (sessions.length>Countly.MAX_CONNECTIONS_ALLOWED){
if (Countly.LOG) Log.d("Clean","####################");
for(int i=0;i<Countly.HALF_CONNECTIONS_CLEANED;i++){
store_.removeConnection(sessions[i]); //发送完成后删除
}
}
}
}
/**
* 用get方法每次上传一个,多次连接并全部上传完
*/
private synchronized void uploadByGetOneByOne(){
while (true) {
String[] sessions = store_.connections();
if (sessions.length == 0)
break;
String initial = sessions[0], replaced = initial;
int index = replaced.indexOf("REPLACE_UDID");
if (index != -1) {
if (OpenUDID_manager.isInitialized() == false)
break;
replaced = replaced.replaceFirst("REPLACE_UDID", OpenUDID_manager.getOpenUDID());
}
/**
* 发送事件
*/
try {
//if (Countly.LOG) Log.d("Countly try upload ->", serverURL_ + "/i?" + replaced);
DefaultHttpClient httpClient = new DefaultHttpClient();
HttpGet method = new HttpGet(new URI(serverURL_ + "/i?" + replaced));
HttpResponse response = httpClient.execute(method);
InputStream input = response.getEntity().getContent();
while (input.read() != -1)
;
httpClient.getConnectionManager().shutdown();
store_.removeConnection(initial); //发送完成后删除
} catch (Exception e) {
Log.d("Countly", "error ->" + initial);
break;
}
}
}
}
/**
* 事件实体
* @author Jackland_zgl
*
*/
class Event {
public String key = null;
public Map<String, String> segmentation = null;
public int count = 0;
public double sum = 0;
public int timestamp = 0;
public boolean equals(Object o) {
if (o == null || !(o instanceof Event)) return false;
Event e = (Event) o;
return (key == null ? e.key == null : key.equals(e.key)) &&
timestamp == e.timestamp && (segmentation == null ? e.segmentation == null : segmentation.equals(e.segmentation));
}
}
/**
* 事件队列
* @author Jackland_zgl
*
*/
class EventQueue {
private CountlyStore countlyStore_;
public EventQueue(CountlyStore countlyStore) {
countlyStore_ = countlyStore;
}
public int size() {
synchronized (this) {
return countlyStore_.events().length;
}
}
/**
* 获取所有事件,JSON格式 String
* @return String for all events
*/
public String events() {
String result = "";
synchronized (this) {
List<Event> events = countlyStore_.eventsList();
JSONArray eventArray = new JSONArray();
for (Event e : events) eventArray.put(CountlyStore.eventToJSON(e));
result = eventArray.toString();
//!!注意这里
countlyStore_.removeEvents(events);
}
try {
result = java.net.URLEncoder.encode(result, "UTF-8");
} catch (UnsupportedEncodingException e) {
}
return result;
}
public void recordEvent(String key) {
recordEvent(key, null, 1, 0);
}
public void recordEvent(String key, int count) {
recordEvent(key, null, count, 0);
}
public void recordEvent(String key, int count, double sum) {
recordEvent(key, null, count, sum);
}
public void recordEvent(String key, Map<String, String> segmentation, int count) {
recordEvent(key, segmentation, count, 0);
}
/**
* 将事件加入到countlyStore里
* @param key
* @param segmentation
* @param count
* @param sum
*/
public void recordEvent(String key, Map<String, String> segmentation, int count, double sum) {
synchronized (this) {
countlyStore_.addEvent(key, segmentation, count, sum);
}
}
}
注意源码中的几点:
1 connection对象和event对象分别对应app自己的生命和用户记录的event,两者在记录的时候,是使用两个不同的queue,但是在上传的时候,统一使用connection队列,recordEvent的时候会把event加入的connection中,onTick里是唯一的上传通道。
2 里面的Crash和feedback是我自己额外加的,原来只有通用的对象
3 为了防止溢出,我加入了规避的情况,即多次发送不成功后,回将最早的部分记录删除掉,避免一直不联网的情况下,pref的数据无限变大。
第二个包openUDID也是个开源包,目的是为了获得设备唯一的一个UDID,如果获取不了,会随机生成一个。
文章为原创,转载请注明出处。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。