zoukankan      html  css  js  c++  java
  • Countly在andoid和vps集成使用,开源的统计分析sdk

    这几天项目需要,简单研究了一下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() {
    			@Override
    			public 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() {
    			@Override
    			public 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);
    	}
    
    	@Override
    	public void onCreate(SQLiteDatabase db) {
    		db.execSQL(CONNECTIONS_TABLE_CREATE);
    		db.execSQL(EVENTS_TABLE_CREATE);
    	}
    
    	@Override
    	public 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 functions
    
    	public 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


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



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

  • 相关阅读:
    C语言中的复合类型
    C语言中的函数与指针
    C语言中的循环结构与选择结构
    C语言中的运算符
    C语言中的变量
    毕业论文查重网站
    Protocol and Delegate协议和代理
    NSArray与NSMutableArray 数组与可变数组
    UI复习
    NSString方法与NSMutableString方法
  • 原文地址:https://www.cnblogs.com/keanuyaoo/p/3366063.html
Copyright © 2011-2022 走看看