您的位置:首页 > 其它

开源Countly

2015-08-19 17:47 351 查看
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<Str
4000
ing>(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 coun
a36d
t) {
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,如果获取不了,会随机生成一个。

文章为原创,转载请注明出处。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: