Countly在andoid和vps集成使用,开源的统计分析sdk

来源:互联网 发布:销售软件心得 编辑:程序博客网 时间:2024/05/24 01:43

这几天项目需要,简单研究了一下countly,说实话完全满足项目需要,比umeng这类产品干净多了。

Countly Mobile Analytics






下面就讲一下android 集成的过程,以及 服务器端报表部分的使用。


1、android 集成。

下载完后,一个工程4个文件,很整洁,赞一个。



这个工程,运行之前把libary去掉勾选,默认这个工程被设置成一个库了。


然后,直接打开CounlyActivity.java这个文件,

Countly.sharedInstance().init(this,你的服务器地址,你的app_key);

上面的你的服务器地址和你的app_key都是先要安装好服务端的程序才能设置的。


下面讲一下服务器端程序的安装和使用。

1、下载完程序后,直接进入bin目录执行sh counly.install.sh ,等5分钟自动全部安装完毕,这方便多了,发现采用node.js代码写的,效率就有些打折扣了。


对了,我的系统是unbuntu  server.


2、它会给你自动装上nginx ,如果你服务器上已经安装apache了,会提示你80端口被占,这个需要你去设置以下nginx.conf里的listen port,随便改个端口就行了

3、服务器网站访问成功后,就在 “管理”-“应用”里添加一个新的应用:




应用密钥就是在android里需要的app_key。


这样子就连通了。



下面简单讲一下会出现的问题:

10-12 15:00:52.570: E/SQLiteDatabase(15701): close() was never explicitly called on database '/data/data/ly.count.android.api/databases/countly' 

如果你发现出现这个异常,基本上就是CountlyDB的全部操作没有执行db.close()方法,然后在activiy的stop方法也没有关闭sqlite 数据库导致的,下面就是我简单修改的CountlyDB.java

package ly.count.android.api;import java.io.InputStream;import java.io.UnsupportedEncodingException;import java.net.URI;import java.util.ArrayList;import java.util.HashMap;import java.util.Iterator;import java.util.Locale;import java.util.Map;import java.util.Timer;import java.util.TimerTask;import org.OpenUDID.OpenUDID_manager;import org.apache.http.HttpResponse;import org.apache.http.client.methods.HttpGet;import org.apache.http.impl.client.DefaultHttpClient;import org.json.JSONArray;import org.json.JSONException;import org.json.JSONObject;import android.content.Context;import android.content.pm.PackageManager.NameNotFoundException;import android.database.Cursor;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import android.telephony.TelephonyManager;import android.util.DisplayMetrics;import android.util.Log;import android.view.Display;import android.view.WindowManager;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 CountlyDB countlyDB_;static public Countly sharedInstance() {if (sharedInstance_ == null)sharedInstance_ = new Countly();return sharedInstance_;}private Countly() {queue_ = new ConnectionQueue();timer_ = new Timer();timer_.schedule(new TimerTask() {@Overridepublic void run() {onTimer();}}, 60 * 1000, 60 * 1000);isVisible_ = false;unsentSessionLength_ = 0;activityCount_ = 0;}public void init(Context context, String serverURL, String appKey) {OpenUDID_manager.sync(context);countlyDB_ = new CountlyDB(context);queue_.setContext(context);queue_.setServerURL(serverURL);queue_.setAppKey(appKey);queue_.setCountlyDB(countlyDB_);eventQueue_ = new EventQueue(countlyDB_);}public void onStart() {activityCount_++;if (activityCount_ == 1)onStartHelper();}public void onStop() {activityCount_--;if (activityCount_ == 0)onStopHelper();if(countlyDB_!=null){countlyDB_.close();}}public void onStartHelper() {lastTime_ = System.currentTimeMillis() / 1000.0;queue_.beginSession();isVisible_ = true;}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;}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());}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());}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());}}class ConnectionQueue {private CountlyDB queue_;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 setCountlyDB(CountlyDB countlyDB) {queue_ = countlyDB;}public void beginSession() {String data;data = "app_key=" + appKey_;data += "&" + "device_id=" + DeviceInfo.getUDID();data += "&" + "timestamp=" + (long) (System.currentTimeMillis() / 1000.0);data += "&" + "sdk_version=" + "2.0";data += "&" + "begin_session=" + "1";data += "&" + "metrics=" + DeviceInfo.getMetrics(context_);queue_.offer(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;queue_.offer(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;queue_.offer(data);tick();}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;queue_.offer(data);tick();}private void tick() {if (thread_ != null && thread_.isAlive())return;if (queue_.isEmpty())return;thread_ = new Thread() {@Overridepublic void run() {while (true) {String data = queue_.peek();if (data == null)break;int index = data.indexOf("REPLACE_UDID");if (index != -1) {if (OpenUDID_manager.isInitialized() == false)break;data = data.replaceFirst("REPLACE_UDID", OpenUDID_manager.getOpenUDID());}try {DefaultHttpClient httpClient = new DefaultHttpClient();HttpGet method = new HttpGet(new URI(serverURL_ + "/i?" + data));HttpResponse response = httpClient.execute(method);InputStream input = response.getEntity().getContent();while (input.read() != -1);httpClient.getConnectionManager().shutdown();Log.d("Countly", "ok ->" + data);queue_.poll();} catch (Exception e) {Log.d("Countly", e.toString());Log.d("Countly", "error ->" + data);break;}}}};thread_.start();}}class DeviceInfo {public static String getUDID() {return OpenUDID_manager.isInitialized() == false ? "REPLACE_UDID" : OpenUDID_manager.getOpenUDID();}public static String getOS() {return "Android";}public static String getOSVersion() {return android.os.Build.VERSION.RELEASE;}public static String getDevice() {return android.os.Build.MODEL;}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;}public static String getCarrier(Context context) {TelephonyManager manager = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);return manager.getNetworkOperatorName();}public static String getLocale() {Locale locale = Locale.getDefault();return locale.getLanguage() + "_" + locale.getCountry();}public static String appVersion(Context context) {String result = "1.0";try {result = context.getPackageManager().getPackageInfo(context.getPackageName(), 0).versionName;} catch (NameNotFoundException e) {}return result;}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("_locale", getLocale());json.put("_app_version", appVersion(context));} catch (JSONException e) {e.printStackTrace();}result = json.toString();try {result = java.net.URLEncoder.encode(result, "UTF-8");} catch (UnsupportedEncodingException e) {}return result;}}class Event {public String key = null;public Map<String, String> segmentation = null;public int count = 0;public double sum = 0;public int timestamp = 0;}class EventQueue {private ArrayList<Event> events_;private CountlyDB countlyDB_;public EventQueue(CountlyDB countlyDB) {countlyDB_ = countlyDB;events_ = countlyDB_.getEvents();}public int size() {synchronized (this) {return events_.size();}}public String events() {String result = "";synchronized (this) {JSONArray eventArray = new JSONArray();for (int i = 0; i < events_.size(); ++i) {JSONObject json = new JSONObject();Event currEvent = events_.get(i);try {json.put("key", currEvent.key);json.put("count", currEvent.count);json.put("sum", currEvent.sum);json.put("timestamp", currEvent.timestamp);if (currEvent.segmentation != null) {json.put("segmentation", new JSONObject(currEvent.segmentation));}} catch (JSONException e) {e.printStackTrace();}eventArray.put(json);}result = eventArray.toString();events_.clear();countlyDB_.clearEvents();}try {result = java.net.URLEncoder.encode(result, "UTF-8");} catch (UnsupportedEncodingException e) {}return result;}public void recordEvent(String key, int count) {synchronized (this) {for (int i = 0; i < events_.size(); ++i) {Event event = events_.get(i);if (event.key.equals(key)) {event.count += count;event.timestamp = Math.round((event.timestamp + (System.currentTimeMillis() / 1000)) / 2);countlyDB_.saveEvents(events_);return;}}Event event = new Event();event.key = key;event.count = count;event.timestamp = Math.round(System.currentTimeMillis() / 1000);events_.add(event);countlyDB_.saveEvents(events_);}}public void recordEvent(String key, int count, double sum) {synchronized (this) {for (int i = 0; i < events_.size(); ++i) {Event event = events_.get(i);if (event.key.equals(key)) {event.count += count;event.sum += sum;event.timestamp = Math.round((event.timestamp + (System.currentTimeMillis() / 1000)) / 2);countlyDB_.saveEvents(events_);return;}}Event event = new Event();event.key = key;event.count = count;event.sum = sum;event.timestamp = Math.round(System.currentTimeMillis() / 1000);events_.add(event);countlyDB_.saveEvents(events_);}}public void recordEvent(String key, Map<String, String> segmentation, int count) {synchronized (this) {for (int i = 0; i < events_.size(); ++i) {Event event = events_.get(i);if (event.key.equals(key) && event.segmentation != null && event.segmentation.equals(segmentation)) {event.count += count;event.timestamp = Math.round((event.timestamp + (System.currentTimeMillis() / 1000)) / 2);countlyDB_.saveEvents(events_);return;}}Event event = new Event();event.key = key;event.segmentation = segmentation;event.count = count;event.timestamp = Math.round(System.currentTimeMillis() / 1000);events_.add(event);countlyDB_.saveEvents(events_);}}public void recordEvent(String key, Map<String, String> segmentation, int count, double sum) {synchronized (this) {for (int i = 0; i < events_.size(); ++i) {Event event = events_.get(i);if (event.key.equals(key) && event.segmentation != null && event.segmentation.equals(segmentation)) {event.count += count;event.sum += sum;event.timestamp = Math.round((event.timestamp + (System.currentTimeMillis() / 1000)) / 2);countlyDB_.saveEvents(events_);return;}}Event event = new Event();event.key = key;event.segmentation = segmentation;event.count = count;event.sum = sum;event.timestamp = Math.round(System.currentTimeMillis() / 1000);events_.add(event);countlyDB_.saveEvents(events_);}}}class CountlyDB extends SQLiteOpenHelper {private static final int DATABASE_VERSION = 1;private static final String DATABASE_NAME = "countly";private static final String CONNECTIONS_TABLE_NAME = "CONNECTIONS";private static final String EVENTS_TABLE_NAME = "EVENTS";private static final String CONNECTIONS_TABLE_CREATE = "CREATE TABLE " + CONNECTIONS_TABLE_NAME + " (ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, CONNECTION TEXT NOT NULL);";private static final String EVENTS_TABLE_CREATE = "CREATE TABLE " + EVENTS_TABLE_NAME + " (ID INTEGER UNIQUE NOT NULL, EVENT TEXT NOT NULL);";CountlyDB(Context context) {super(context, DATABASE_NAME, null, DATABASE_VERSION);}@Overridepublic void onCreate(SQLiteDatabase db) {db.execSQL(CONNECTIONS_TABLE_CREATE);db.execSQL(EVENTS_TABLE_CREATE);}@Overridepublic void onUpgrade(SQLiteDatabase db, int arg1, int arg2) {}public String peek() {synchronized (this) {SQLiteDatabase db = this.getReadableDatabase();Cursor cursor = db.query(CONNECTIONS_TABLE_NAME, null, null, null, null, null, "ID DESC", "1");String connection = null;if (cursor != null && cursor.getCount() > 0) {cursor.moveToFirst();connection = cursor.getString(1);Log.d("Countly", "Fetched: " + connection);}db.close();return connection;}}public String poll() {synchronized (this) {SQLiteDatabase db = this.getReadableDatabase();Cursor cursor = db.query(CONNECTIONS_TABLE_NAME, null, null, null, null, null, "ID DESC", "1");String connection = null;if (cursor != null && cursor.getCount() > 0) {cursor.moveToFirst();connection = cursor.getString(1);int rawId = Integer.parseInt(cursor.getString(0));SQLiteDatabase writeDb = this.getWritableDatabase();writeDb.execSQL("DELETE FROM " + CONNECTIONS_TABLE_NAME + " WHERE ID = " + rawId + ";");Log.d("Countly", "Fetched and deleted: " + connection);}db.close();return connection;}}public void offer(String data) {SQLiteDatabase db = this.getWritableDatabase();db.execSQL("INSERT INTO " + CONNECTIONS_TABLE_NAME + "(CONNECTION) VALUES('" + data + "');");Log.d("Countly", "Insert into " + CONNECTIONS_TABLE_NAME + ": " + data);db.close();}public boolean isEmpty() {SQLiteDatabase db = this.getReadableDatabase();Cursor cursor = db.query(CONNECTIONS_TABLE_NAME, null, null, null, null, null, "ID DESC", "1");boolean rtn= !(cursor != null && cursor.getCount() > 0);db.close();return rtn;}// Event related functionspublic ArrayList<Event> getEvents() {SQLiteDatabase db = this.getReadableDatabase();Cursor cursor = db.query(EVENTS_TABLE_NAME, null, null, null, null, null, "ID = 1", "1");ArrayList<Event> eventsArray = new ArrayList<Event>();if (cursor != null && cursor.getCount() > 0) {cursor.moveToFirst();String events = cursor.getString(1);JSONObject json = new JSONObject();try {json = new JSONObject(events);} catch (JSONException e) {e.printStackTrace();}JSONArray jArray = json.optJSONArray("events");if (jArray != null) {for (int i = 0; i < jArray.length(); i++) {try {eventsArray.add(jsonToEvent(new JSONObject(jArray.get(i).toString())));} catch (JSONException e) {e.printStackTrace();}}}}db.close();return eventsArray;}public void saveEvents(ArrayList<Event> events) {JSONArray eventArray = new JSONArray();JSONObject json = new JSONObject();for (int i = 0; i < events.size(); ++i) {eventArray.put(eventToJSON(events.get(i)));}try {json.put("events", eventArray);} catch (JSONException e) {e.printStackTrace();}SQLiteDatabase db = this.getWritableDatabase();db.execSQL("INSERT OR REPLACE INTO " + EVENTS_TABLE_NAME + "(ID, EVENT) VALUES(1, '" + json.toString() + "');");db.close();}public void clearEvents() {SQLiteDatabase writeDb = this.getWritableDatabase();writeDb.execSQL("DELETE FROM " + EVENTS_TABLE_NAME + ";");writeDb.close();}private 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;}private 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());HashMap<String, String> segmentation = new HashMap<String, String>();@SuppressWarnings("unchecked")Iterator<String> nameItr = ((JSONObject) json.get("segmentation")).keys();while (nameItr.hasNext()) {String key = nameItr.next();segmentation.put(key, ((JSONObject) json.get("segmentation")).getString(key));}event.segmentation = segmentation;} catch (JSONException e) {e.printStackTrace();}return event;}}


最后讲一下,自定义事件的使用。

        Countly.sharedInstance().recordEvent("open acivity", count++);                Map<String, String> music = new HashMap<String, String>();        music.put("type", "popular");        music.put("artist", "JJLin");        music.put("User_status", "registered");        Countly.sharedInstance().recordEvent( "music",music,count++);

你看Countly提供了好几种方式:


第一种方式,就是一个tag和一个计数,足够我们使用了。

对于第三种,我觉得有更高需要采用的到,举个例子:

http://dev.umeng.com/analytics/android/%E8%87%AA%E5%AE%9A%E4%B9%89%E4%BA%8B%E4%BB%B6%E6%A1%88%E4%BE%8B


附上网站自定义事件的报表:



如果大家有啥问题可以找我讨论。

原创粉丝点击