Skip to content
Open
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
1 change: 1 addition & 0 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
apply plugin: 'java'
apply plugin: 'maven-publish'
apply from: 'gradle-mvn-push.gradle'

targetCompatibility = '1.6'
Expand Down
11 changes: 10 additions & 1 deletion src/com/activeandroid/ActiveAndroid.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
import android.database.sqlite.SQLiteDatabase;

import com.activeandroid.util.Log;
import android.os.Build;

public final class ActiveAndroid {
//////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -60,8 +61,16 @@ public static SQLiteDatabase getDatabase() {
return Cache.openDatabase();
}

/**
* Non-exclusive transactions allows BEGIN IMMEDIATE
* blocks, allowing better read concurrency.
*/
public static void beginTransaction() {
Cache.openDatabase().beginTransaction();
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB) {
Cache.openDatabase().beginTransaction();
} else {
Cache.openDatabase().beginTransactionNonExclusive();
}
}

public static void endTransaction() {
Expand Down
20 changes: 20 additions & 0 deletions src/com/activeandroid/DatabaseHelper.java
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import com.activeandroid.util.NaturalOrderComparator;
import com.activeandroid.util.SQLiteUtils;
import com.activeandroid.util.SqlParser;
import android.os.Build;

public final class DatabaseHelper extends SQLiteOpenHelper {
//////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -66,6 +67,25 @@ public DatabaseHelper(Configuration configuration) {
// OVERRIDEN METHODS
//////////////////////////////////////////////////////////////////////////////////////

/**
* onConfigure is called when the db connection
* is being configured. It's the right place
* to enable write-ahead logging or foreign
* key support.
*
* Available for API level 16 (JellyBean) and above.
*/
@Override
public void onConfigure(SQLiteDatabase db) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
db.enableWriteAheadLogging();
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
db.setForeignKeyConstraintsEnabled(true);
}
executePragmas(db);
}

@Override
public void onOpen(SQLiteDatabase db) {
executePragmas(db);
Expand Down
4 changes: 0 additions & 4 deletions src/com/activeandroid/Model.java
Original file line number Diff line number Diff line change
Expand Up @@ -293,10 +293,6 @@ else if (ReflectionUtils.isSubclassOf(fieldType, Enum.class)) {
Log.e(e.getClass().getName(), e);
}
}

if (mId != null) {
Cache.addEntity(this);
}
}


Expand Down
81 changes: 65 additions & 16 deletions src/com/activeandroid/ModelInfo.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
import java.util.Map;

import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ApplicationInfo;

import com.activeandroid.serializer.CalendarSerializer;
import com.activeandroid.serializer.FileSerializer;
Expand All @@ -41,6 +43,12 @@
import dalvik.system.DexFile;

final class ModelInfo {
private static final String PREFS_FILE = "multidex.version";
private static final String SECONDARY_FOLDER_NAME = "code_cache" + File.separator + "secondary-dexes";
private static final String EXTRACTED_NAME_EXT = ".classes";
private static final String KEY_DEX_NUMBER = "dex.number";
private static final String EXTRACTED_SUFFIX = ".zip";

//////////////////////////////////////////////////////////////////////////////////////
// PRIVATE METHODS
//////////////////////////////////////////////////////////////////////////////////////
Expand Down Expand Up @@ -125,28 +133,41 @@ private boolean loadModelFromMetaData(Configuration configuration) {

private void scanForModel(Context context) throws IOException {
String packageName = context.getPackageName();
String sourcePath = context.getApplicationInfo().sourceDir;
List<String> paths = new ArrayList<String>();

if (sourcePath != null && !(new File(sourcePath).isDirectory())) {
DexFile dexfile = new DexFile(sourcePath);
Enumeration<String> entries = dexfile.entries();
try {
for (String sourcePath : getSourcePaths(context)) {
try {
if (sourcePath != null && !(new File(sourcePath).isDirectory())) {
DexFile dexfile;
if (sourcePath.endsWith(EXTRACTED_SUFFIX))
dexfile = DexFile.loadDex(sourcePath, sourcePath + ".tmp", 0);
else
dexfile = new DexFile(sourcePath);
Enumeration<String> entries = dexfile.entries();

while (entries.hasMoreElements()) {
paths.add(entries.nextElement());
}
}
// Robolectric fallback
else {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = classLoader.getResources("");
while (entries.hasMoreElements()) {
paths.add(entries.nextElement());
}
}
// Robolectric fallback
else {
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = classLoader.getResources("");

while (resources.hasMoreElements()) {
String path = resources.nextElement().getFile();
if (path.contains("bin") || path.contains("classes")) {
paths.add(path);
while (resources.hasMoreElements()) {
String path = resources.nextElement().getFile();
if (path.contains("bin") || path.contains("classes")) {
paths.add(path);
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}

for (String path : paths) {
Expand All @@ -155,6 +176,34 @@ private void scanForModel(Context context) throws IOException {
}
}

private static SharedPreferences getMultiDexPreferences(Context context) {
int mode = Context.MODE_PRIVATE | Context.MODE_MULTI_PROCESS;
return context.getSharedPreferences(PREFS_FILE, mode);
}

private static List<String> getSourcePaths(Context context) throws Exception {
ApplicationInfo applicationInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), 0);
File sourceApk = new File(applicationInfo.sourceDir);
File dexDir = new File(applicationInfo.dataDir, SECONDARY_FOLDER_NAME);

List<String> sourcePaths = new ArrayList<String>();
sourcePaths.add(applicationInfo.sourceDir);

String extractedFilePrefix = sourceApk.getName() + EXTRACTED_NAME_EXT;
int totalDexNumber = getMultiDexPreferences(context).getInt(KEY_DEX_NUMBER, 1);

for (int secondaryNumber = 2; secondaryNumber <= totalDexNumber; secondaryNumber++) {
String fileName = extractedFilePrefix + secondaryNumber + EXTRACTED_SUFFIX;
File extractedFile = new File(dexDir, fileName);
if (extractedFile.isFile())
sourcePaths.add(extractedFile.getAbsolutePath());
else
throw new Exception("Missing extracted secondary dex file '" + extractedFile.getPath() + "'");
}

return sourcePaths;
}

private void scanForModelClasses(File path, String packageName, ClassLoader classLoader) {
if (path.isDirectory()) {
for (File file : path.listFiles()) {
Expand Down
9 changes: 8 additions & 1 deletion src/com/activeandroid/automigration/TableDifference.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.activeandroid.automigration;

import android.util.Log;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
Expand Down Expand Up @@ -39,7 +41,12 @@ public TableDifference(TableInfo tableInfo, SQLTableInfo sqlTableInfo) {
if (existingColumnInfo.getType() == sqlColumnInfo.getType()) {
mDifferences.put(sqlColumnInfo, existingColumnInfo);
} else {
throw new IncompatibleColumnTypesException(tableInfo.getTableName(), existingColumnInfo.getName(), existingColumnInfo.getType(), sqlColumnInfo.getType());
// allow column type changes just to let SQLite attempt to cast these values
Log.w(TableDifference.class.getName(), "potentially incompatible column types (table='"
+ tableInfo.getTableName() + "' column='" + existingColumnInfo.getName()
+ "' current type='" + existingColumnInfo.getType() + "' new type='"
+ sqlColumnInfo.getType() + "')");
mDifferences.put(sqlColumnInfo, existingColumnInfo);
}
}
break;
Expand Down
69 changes: 45 additions & 24 deletions src/com/activeandroid/internal/AnnotationProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import javax.lang.model.element.TypeElement;
import javax.lang.model.element.VariableElement;
import javax.lang.model.type.DeclaredType;
import javax.lang.model.type.TypeKind;
import javax.lang.model.type.TypeMirror;
import javax.tools.Diagnostic.Kind;
import javax.tools.JavaFileObject;
Expand Down Expand Up @@ -134,7 +135,8 @@ private void generate(TypeElement tableElement, Set<VariableElement> columns) {

private String getLoadFromCursorCode(Set<VariableElement> columns) {
StringBuilder stringBuilder = new StringBuilder();

stringBuilder.append(" int i = -1; // column index \n");
final String nullCheck = CURSOR + ".isNull(i) ? null : ";
for (VariableElement column : columns) {
Column annotation = column.getAnnotation(Column.class);

Expand All @@ -147,40 +149,56 @@ private String getLoadFromCursorCode(Set<VariableElement> columns) {
boolean notPrimitiveType = typeMirror instanceof DeclaredType;
String type = typeMirror.toString() + ".class";
String getColumnIndex = COLUMNS_ORDERED + ".indexOf(\"" + fieldName + "\")";
String getColumnIndexAssignment = "i = " + getColumnIndex + "; \n";

stringBuilder.append(" " + getColumnIndexAssignment );
if (notPrimitiveType) {
stringBuilder.append(" if (ModelHelper.isSerializable(" + type + ")) {\n");
stringBuilder.append(" " + MODEL + "." + column.getSimpleName() + " = (" + typeMirror.toString() + ") ModelHelper.getSerializable(cursor, " + type + ", " + getColumnIndex + ");\n");
stringBuilder.append(" " + MODEL + "." + column.getSimpleName() + " = (" + typeMirror.toString() + ") ModelHelper.getSerializable(cursor, " + type + ", i);\n");
stringBuilder.append(" } else {\n");
stringBuilder.append(" " + MODEL + "." + column.getSimpleName() + " = ");
} else {
stringBuilder.append(" " + MODEL + "." + column.getSimpleName() + " = ");
}

if (isTypeOf(typeMirror, Integer.class) || isTypeOf(typeMirror, int.class))
stringBuilder.append(CURSOR + ".getInt(" + getColumnIndex + ");\n");
else if (isTypeOf(typeMirror, Byte.class) || isTypeOf(typeMirror, byte.class))
stringBuilder.append(CURSOR + ".getInt(" + getColumnIndex + ");\n");
else if (isTypeOf(typeMirror, Short.class) || isTypeOf(typeMirror, short.class))
stringBuilder.append(CURSOR + ".getInt(" + getColumnIndex + ");\n");
else if (isTypeOf(typeMirror, Long.class) || isTypeOf(typeMirror, long.class))
stringBuilder.append(CURSOR + ".getLong(" + getColumnIndex + ");\n");
else if (isTypeOf(typeMirror, Float.class) || isTypeOf(typeMirror, float.class))
stringBuilder.append(CURSOR + ".getFloat(" + getColumnIndex + ");\n");
else if (isTypeOf(typeMirror, Double.class) || isTypeOf(typeMirror, double.class))
stringBuilder.append(CURSOR + ".getDouble(" + getColumnIndex + ");\n");
else if (isTypeOf(typeMirror, Boolean.class) || isTypeOf(typeMirror, boolean.class))
stringBuilder.append(CURSOR + ".getInt(" + getColumnIndex + ") != 0;\n");
else if (isTypeOf(typeMirror, Character.class) || isTypeOf(typeMirror, char.class))
stringBuilder.append(CURSOR + ".getString(" + getColumnIndex + ");\n");
if (isTypeOf(typeMirror, Integer.class) || isTypeOf(typeMirror, Byte.class) || isTypeOf(typeMirror, Short.class) )
stringBuilder.append(nullCheck).append(CURSOR + ".getInt(i);\n");
else if (isTypeOf(typeMirror, Long.class))
stringBuilder.append(nullCheck).append(CURSOR + ".getLong(i);\n");
else if (isTypeOf(typeMirror, Float.class))
stringBuilder.append(nullCheck).append(CURSOR + ".getFloat(i);\n");
else if (isTypeOf(typeMirror, Double.class))
stringBuilder.append(nullCheck).append(CURSOR + ".getDouble(i);\n");
else if (isTypeOf(typeMirror, int.class))
stringBuilder.append(CURSOR + ".getInt(i);\n");
else if (isTypeOf(typeMirror, byte.class))
stringBuilder.append(CURSOR + ".getInt(i);\n");
else if (isTypeOf(typeMirror, short.class))
stringBuilder.append(CURSOR + ".getInt(i);\n");
else if (isTypeOf(typeMirror, long.class))
stringBuilder.append(CURSOR + ".getLong(i);\n");
else if (isTypeOf(typeMirror, float.class))
stringBuilder.append(CURSOR + ".getFloat(i);\n");
else if (isTypeOf(typeMirror, double.class))
stringBuilder.append(CURSOR + ".getDouble(i);\n");
else if (isTypeOf(typeMirror, Boolean.class))
stringBuilder.append(nullCheck).append(CURSOR + ".getInt(i) != 0;\n");
else if (isTypeOf(typeMirror, boolean.class))
stringBuilder.append(CURSOR + ".getInt(i) != 0;\n");
else if (isTypeOf(typeMirror, char.class))
stringBuilder.append(CURSOR + ".getString(i);\n");
else if (isTypeOf(typeMirror, Character.class))
stringBuilder.append(nullCheck).append(CURSOR + ".getString(i);\n");
else if (isTypeOf(typeMirror, String.class))
stringBuilder.append(CURSOR + ".getString(" + getColumnIndex + ");\n");
else if (isTypeOf(typeMirror, Byte[].class) || isTypeOf(typeMirror, byte[].class))
stringBuilder.append(CURSOR + ".getBlob(" + getColumnIndex + ");\n");
stringBuilder.append(nullCheck).append(CURSOR + ".getString(i);\n");
else if (isTypeOf(typeMirror, byte[].class))
stringBuilder.append(CURSOR + ".getBlob(i);\n");
else if (isTypeOf(typeMirror, Byte[].class))
stringBuilder.append(nullCheck).append(CURSOR + ".getBlob(i);\n");
else if (isTypeOf(typeMirror, Model.class))
stringBuilder.append("(" + typeMirror.toString() + ") ModelHelper.getModel(cursor, " + type + ", " + getColumnIndex + ");\n");
stringBuilder.append("(" + typeMirror.toString() + ") ModelHelper.getModel(cursor, " + type + ", i);\n");
else if (isTypeOf(typeMirror, Enum.class))
stringBuilder.append("(" + typeMirror.toString() + ") ModelHelper.getEnum(cursor, " + type + ", " + getColumnIndex + ");\n");
stringBuilder.append("(" + typeMirror.toString() + ") ModelHelper.getEnum(cursor, " + type + ", i);\n");
else
stringBuilder.append(" null;\n");
if (notPrimitiveType) {
Expand All @@ -205,7 +223,7 @@ private String getFillContentValuesCode(Set<VariableElement> columns) {
boolean notPrimitiveType = typeMirror instanceof DeclaredType;
String type = typeMirror.toString() + ".class";
String getValue = MODEL + "." + column.getSimpleName();

if (notPrimitiveType) {
stringBuilder.append(" if (ModelHelper.isSerializable(" + type + ")) {\n");
stringBuilder.append(" ModelHelper.setSerializable(" + CONTENT_VALUES + ", " + type + ", " + getValue + ", \"" + fieldName + "\");\n");
Expand Down Expand Up @@ -256,6 +274,9 @@ private boolean isTypeOf(TypeMirror typeMirror, Class<?> type) {
if (type.getName().equals(typeMirror.toString()))
return true;

if ((typeMirror.getKind() == TypeKind.ARRAY) && type.isArray())
return typeMirror.toString().equals(type.getComponentType() + "[]");

if (typeMirror instanceof DeclaredType == false)
return false;

Expand Down
16 changes: 14 additions & 2 deletions src/com/activeandroid/query/From.java
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ public final class From implements Sqlable {
private String mOrderBy;
private String mLimit;
private String mOffset;
private boolean useCache = true;

private List<Object> mArguments;

Expand Down Expand Up @@ -295,7 +296,7 @@ public String toCountSql() {

public <T extends Model> List<T> execute() {
if (mQueryBase instanceof Select) {
return SQLiteUtils.rawQuery(mType, toSql(), getArguments());
return SQLiteUtils.rawQuery(mType, toSql(), getArguments(), useCache);

} else {
SQLiteUtils.execSql(toSql(), getArguments());
Expand All @@ -308,7 +309,7 @@ public <T extends Model> List<T> execute() {
public <T extends Model> T executeSingle() {
if (mQueryBase instanceof Select) {
limit(1);
return (T) SQLiteUtils.rawQuerySingle(mType, toSql(), getArguments());
return (T) SQLiteUtils.rawQuerySingle(mType, toSql(), getArguments(), useCache);

} else {
limit(1);
Expand Down Expand Up @@ -343,4 +344,15 @@ public String[] getArguments() {

return args;
}

/**
* Retrieve entities from ActiveAndroid's cache. The cache is enabled by default.
* @param useCache
* @return
*/
public From setUseCache(boolean useCache) {
this.useCache = useCache;
return this;
}

}
Loading