Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Catch Out of Memory errors. #4

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified MPMetrics.jar
Binary file not shown.
Binary file removed bin/MPMetrics.apk
Binary file not shown.
Binary file removed bin/classes.dex
Binary file not shown.
Binary file not shown.
Binary file removed bin/com/mixpanel/android/dbadapter/MPDbAdapter.class
Binary file not shown.
Binary file removed bin/com/mixpanel/android/mpmetrics/Global.class
Binary file not shown.
Binary file not shown.
Binary file removed bin/com/mixpanel/android/mpmetrics/MPMetrics$2.class
Binary file not shown.
Binary file removed bin/com/mixpanel/android/mpmetrics/MPMetrics$3.class
Binary file not shown.
Binary file removed bin/com/mixpanel/android/mpmetrics/MPMetrics.class
Binary file not shown.
Binary file removed bin/com/mixpanel/android/mpmetrics/R$attr.class
Binary file not shown.
Binary file removed bin/com/mixpanel/android/mpmetrics/R$drawable.class
Binary file not shown.
Binary file removed bin/com/mixpanel/android/mpmetrics/R$id.class
Binary file not shown.
Binary file removed bin/com/mixpanel/android/mpmetrics/R$layout.class
Binary file not shown.
Binary file removed bin/com/mixpanel/android/mpmetrics/R.class
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed bin/com/mixpanel/android/network/Base64Coder.class
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file removed bin/com/mixpanel/android/util/StringUtils.class
Binary file not shown.
Binary file removed bin/resources.ap_
Binary file not shown.
7 changes: 6 additions & 1 deletion src/com/mixpanel/android/dbadapter/MPDbAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
Expand Down Expand Up @@ -130,7 +131,11 @@ public void cleanupEvents(Date date) {
SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_TIME_FORMAT);
String strDate = dateFormat.format(date);

mDb.delete(DATABASE_TABLE, KEY_CREATED_AT + " < \"" + strDate + "\"", null);
try {
mDb.delete(DATABASE_TABLE, KEY_CREATED_AT + " < \"" + strDate + "\"", null);
} catch (SQLiteException e) {
Log.e(LOGTAG, "Could not DELETE events", e);
}
}

/**
Expand Down
148 changes: 85 additions & 63 deletions src/com/mixpanel/android/mpmetrics/MPMetrics.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,18 +12,19 @@
import org.json.JSONException;
import org.json.JSONObject;

import com.mixpanel.android.dbadapter.MPDbAdapter;
import com.mixpanel.android.network.Base64Coder;
import com.mixpanel.android.network.HTTPRequestHelper;

import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Handler;
import android.os.Message;
import android.provider.Settings;
import android.util.Log;

import com.mixpanel.android.dbadapter.MPDbAdapter;
import com.mixpanel.android.network.Base64Coder;
import com.mixpanel.android.network.HTTPRequestHelper;
import com.mixpanel.android.util.Installation;

public class MPMetrics {

private static final String LOGTAG = "MPMetrics";
Expand Down Expand Up @@ -57,7 +58,7 @@ public class MPMetrics {
private String mCarrier;
private String mModel;
private String mVersion;
private String mDeviceId;
private String mInstallationId;

public MPMetrics(Context context, String token) {
mContext = context;
Expand Down Expand Up @@ -93,7 +94,7 @@ public void handleMessage(final Message msg) {
mCarrier = getCarrier();
mModel = getModel();
mVersion = getVersion();
mDeviceId = getDeviceId();
mInstallationId = getInstallationId();

mTimer = new Timer();
mTimer.schedule(new TimerTask() {
Expand Down Expand Up @@ -130,19 +131,13 @@ private String getVersion() {
}

/**
* Return the unique device identifier of the phone
* Return the unique installation identifier of the host application
* @param context the context
* @return A String containing the device's unique identifier
* @return A String containing the installation UUID
*/
private String getDeviceId() {
String product = Build.PRODUCT;
String androidId = Settings.Secure.getString(mContext.getContentResolver(), Settings.Secure.ANDROID_ID);
if (product == null || androidId == null) {
return null;
} else {
return product + "_" + androidId;
}

private String getInstallationId() {
if (Global.DEBUG) Log.d(LOGTAG, "getInstallationId: " + Installation.id(mContext));
return Installation.id(mContext);
}


Expand All @@ -152,7 +147,7 @@ private String getDeviceId() {
* @return the rowId of the stored data, or -1 if failed
*/
private long persistEventData(String data) {
if (Global.DEBUG) Log.d(LOGTAG, "persistEventData");
if (Global.DEBUG) Log.d(LOGTAG, "persistEventData: \"" + data + '"');
long id = -1;
try {
mDbAdapter.open();
Expand Down Expand Up @@ -189,40 +184,31 @@ private boolean removeEventData(long id) {
* @param url Mixpanel API endpoint
* @param data the base64 encoded value for the "data" param
*/
private void sendRequest(final String url) {
private void sendRequest(String url) {
if (Global.DEBUG) Log.d(LOGTAG, "sendRequest");

try {
mDbAdapter.open();
Cursor cursor = mDbAdapter.fetchEvents();
int idColumnIndex = cursor.getColumnIndexOrThrow(MPDbAdapter.KEY_ROWID);
int dataColumnIndex = cursor.getColumnIndexOrThrow(MPDbAdapter.KEY_DATA);

if (cursor.moveToFirst()) {
do {
final long id = cursor.getLong(idColumnIndex);
final String data = cursor.getString(dataColumnIndex);

if (!mDispatchedEvents.contains(id)) {
mDispatchedEvents.add(id);

final ResponseHandler<String> responseHandler =
HTTPRequestHelper.getResponseHandlerInstance(mHandler, id);

new Thread() {
@Override
public void run() {
if (Global.DEBUG) Log.d(LOGTAG, "Sending data id: " + id);
Map<String, String> params = new HashMap<String, String>();
params.put("data", data);
HTTPRequestHelper helper = new HTTPRequestHelper(responseHandler, mHandler);
helper.performPost(url, null, null, null, params);

}
}.start();
}
} while (cursor.moveToNext());
if (cursor != null) {
int idColumnIndex = cursor.getColumnIndexOrThrow(MPDbAdapter.KEY_ROWID);
int dataColumnIndex = cursor.getColumnIndexOrThrow(MPDbAdapter.KEY_DATA);

if (cursor.moveToFirst()) {
do {
long id = cursor.getLong(idColumnIndex);

if (!mDispatchedEvents.contains(id)) {
mDispatchedEvents.add(id);
String data = cursor.getString(dataColumnIndex);
new POSTTask(id).execute(url, data);
}
} while (cursor.moveToNext());
}

cursor.close();
}
cursor.close();
} catch (Exception e) {

} finally {
Expand All @@ -238,21 +224,25 @@ public void run() {
* service is destroyed to ensure that any remaining events are sent.
*/
public synchronized void flush() {
if (Global.DEBUG) Log.d(LOGTAG, "flush");
if (Global.DEBUG) {
try {
Log.d(LOGTAG, mEvents.toString(2));
} catch (JSONException e) {
int eventCount = mEvents.length();
if (Global.DEBUG) Log.d(LOGTAG, "flush: " + eventCount + " events");

if (eventCount > 0) {
if (Global.DEBUG) {
try {
Log.d(LOGTAG, "mEvents = " + mEvents.toString(2));
} catch (JSONException e) {
}
}

String data = Base64Coder.encodeString(mEvents.toString());

persistEventData(data);

sendRequest(ENDPOINT_TRACK);

mEvents = new JSONArray();
}

String data = Base64Coder.encodeString(mEvents.toString());

persistEventData(data);

sendRequest(ENDPOINT_TRACK);

mEvents = new JSONArray();
}

/**
Expand Down Expand Up @@ -341,11 +331,12 @@ public void event(String eventName, Map<String, String> properties) {
JSONObject propertiesObj = new JSONObject();
propertiesObj.put("token", mToken);
propertiesObj.put("time", time);
propertiesObj.put("distinct_id", mDeviceId == null ? "UNKNOWN" : mDeviceId);
propertiesObj.put("distinct_id", mInstallationId == null ? "UNKNOWN" : mInstallationId);
propertiesObj.put("carrier", mCarrier == null ? "UNKNOWN" : mCarrier);
propertiesObj.put("model", mModel == null ? "UNKNOWN" : mModel);
propertiesObj.put("version", mVersion == null ? "UNKNOWN" : mVersion);
propertiesObj.put("mp_lib", "android");
propertiesObj.put("os", "android");

if (mSuperProperties != null) {
if (mSuperPropertiesType == SUPER_PROPERTY_TYPE_ALL || mSuperPropertiesType == SUPER_PROPERTY_TYPE_EVENTS) {
Expand Down Expand Up @@ -411,7 +402,7 @@ public void funnel(String funnelName, int step, String goal, Map<String, String
propertiesObj.put("funnel", funnelName);
propertiesObj.put("step", step);
propertiesObj.put("goal", goal);
propertiesObj.put("distinct_id", mDeviceId == null ? "UNKNOWN" : mDeviceId);
propertiesObj.put("distinct_id", mInstallationId == null ? "UNKNOWN" : mInstallationId);
propertiesObj.put("carrier", mCarrier == null ? "UNKNOWN" : mCarrier);
propertiesObj.put("model", mModel == null ? "UNKNOWN" : mModel);
propertiesObj.put("version", mVersion == null ? "UNKNOWN" : mVersion);
Expand Down Expand Up @@ -463,5 +454,36 @@ private synchronized void parseResults(String resultString, long id, boolean suc
mDispatchedEvents.remove(id);

}


private class POSTTask extends AsyncTask<String, Void, Void> {
private long mId;

public POSTTask(long id) {
super();
mId = id;
}

@Override
protected Void doInBackground(String... params) {
if (Global.DEBUG) {
Log.d(LOGTAG, "Sending data id " + mId + ": \"" + params[1] + '"');
}

try {
String url = params[0];
String data = params[1];
ResponseHandler<String> responseHandler = HTTPRequestHelper.getResponseHandlerInstance(mHandler, mId);
Map<String, String> postParams = new HashMap<String, String>();
postParams.put("data", data);
HTTPRequestHelper helper = new HTTPRequestHelper(responseHandler, mHandler);
helper.performPost(url, null, null, null, postParams);
} catch (Exception e) {
Log.e(LOGTAG, "Exception on POST", e);
} catch (OutOfMemoryError e) {
Log.e(LOGTAG, "OutOfMemory on POST", e);
}

return null;
}
}
}
57 changes: 57 additions & 0 deletions src/com/mixpanel/android/util/Installation.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.mixpanel.android.util;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.util.UUID;

import android.content.Context;

// class that maintains a UUID to track this particular installation of the host app
//
// adapted from http://android-developers.blogspot.com/2011/03/identifying-app-installations.html
//
public class Installation {
private static String sID = null;
private static final String INSTALLATION = "INSTALLATION";

public synchronized static String id(Context context) {
if (context != null && sID == null) {
File installation = new File(context.getFilesDir(), INSTALLATION);

try {
if (!installation.exists()) {
writeInstallationFile(installation);
}

sID = readInstallationFile(installation);
} catch (Exception e) {
throw new RuntimeException(e);
}
}

return sID;
}

private static String readInstallationFile(File installation) throws IOException {
if (installation != null) {
RandomAccessFile f = new RandomAccessFile(installation, "r");
byte[] bytes = new byte[(int) f.length()];
f.readFully(bytes);
f.close();
return new String(bytes);
}

return null;
}

private static void writeInstallationFile(File installation) throws IOException {
if (installation != null) {
FileOutputStream out = new FileOutputStream(installation);
String id = UUID.randomUUID().toString();
out.write(id.getBytes());
out.close();
}
}
}