Skip to content
This repository has been archived by the owner on Feb 11, 2022. It is now read-only.

Commit

Permalink
Merge pull request #54 from novoda/optional_yield
Browse files Browse the repository at this point in the history
Optional yieldIfContendedSafely() on bulkInsert
  • Loading branch information
blundell committed Jun 2, 2015
2 parents 40f9015 + 726470a commit 7e8eb02
Show file tree
Hide file tree
Showing 17 changed files with 387 additions and 13 deletions.
Expand Up @@ -28,6 +28,7 @@ public class SQLiteContentProviderImpl extends SQLiteContentProvider {
private static final String LIMIT = "limit";
private static final String EXPAND = "expand";
private static final String DISTINCT = "distinct";
private static final String ALLOW_YIELD = "allowYield";

private InsertHelper helper;
private final ImplLogger logger;
Expand Down Expand Up @@ -70,13 +71,17 @@ protected Uri insertInTransaction(Uri uri, ContentValues values) {

@Override
protected int bulkInsertInTransaction(Uri uri, ContentValues[] values) {
String allowYield = uri.getQueryParameter(ALLOW_YIELD);
boolean shouldYield = allowYield == null || Boolean.parseBoolean(allowYield);
int rowsCreated = 0;
for (ContentValues value : values) {
Uri insertUri = insertSilently(uri, value);
if (insertUri != null) {
rowsCreated++;
}
getWritableDatabase().yieldIfContendedSafely();
if (shouldYield) {
getWritableDatabase().yieldIfContendedSafely();
}
}
notifyUriChange(uri);
return rowsCreated;
Expand Down
Expand Up @@ -21,9 +21,9 @@
import org.robolectric.annotation.Implements;
import org.robolectric.shadows.ShadowContentUris;

import novoda.lib.sqliteprovider.RoboRunner;
import novoda.lib.sqliteprovider.sqlite.ExtendedSQLiteOpenHelper;
import novoda.lib.sqliteprovider.sqlite.ExtendedSQLiteQueryBuilder;
import novoda.lib.sqliteprovider.RoboRunner;

import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
Expand Down Expand Up @@ -259,6 +259,39 @@ public void testDeleteNotifiesAsManyChangesAsDeletes() {

}

@Test
public void testBulkInsertDoesYieldByDefault() {
when(db.insert(anyString(), anyString(), (ContentValues) anyObject())).thenReturn(2L);
int bulkSize = 100;
ContentValues[] bulkToInsert = createContentValuesArray(bulkSize);

bulkInsert("test.com/table1", bulkToInsert);

verify(db, times(bulkSize)).yieldIfContendedSafely();
}

@Test
public void testWhenSpecifyingAllowYieldQueryParameterAsTrueThanBulkInsertDoesYield() {
when(db.insert(anyString(), anyString(), (ContentValues) anyObject())).thenReturn(2L);
int bulkSize = 100;
ContentValues[] bulkToInsert = createContentValuesArray(bulkSize);

bulkInsert("test.com/table1?allowYield=true", bulkToInsert);

verify(db, times(bulkSize)).yieldIfContendedSafely();
}

@Test
public void testWhenSpecifyingAllowYieldQueryParameterAsFalseThanBulkInsertDoesNotYield() {
when(db.insert(anyString(), anyString(), (ContentValues) anyObject())).thenReturn(2L);
int bulkSize = 100;
ContentValues[] bulkToInsert = createContentValuesArray(bulkSize);

bulkInsert("test.com/table1?allowYield=false", bulkToInsert);

verify(db, never()).yieldIfContendedSafely();
}

@Implements(ContentUris.class)
static class ShadowContentUris {

Expand Down
9 changes: 6 additions & 3 deletions demo-extended/src/main/AndroidManifest.xml
Expand Up @@ -2,17 +2,17 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.novoda.sqliteprovider.demo"
android:versionCode="1"
android:versionName="1.0" >
android:versionName="1.0">

<application
android:name=".NovodaApplication"
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
android:theme="@style/AppTheme">
<activity
android:name=".ui.MainActivity"
android:label="@string/app_name" >
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

Expand All @@ -28,6 +28,9 @@
<activity
android:name=".ui.AddBulkFireworksActivity"
android:label="@string/activity_label_add_bulk_fireworks" />
<activity
android:name=".ui.AddBulkYieldFireworksActivity"
android:label="@string/activity_label_add_bulk_yield_fireworks" />
<activity
android:name=".ui.FindFireworkWithPkActivity"
android:label="@string/activity_label_find_firework" />
Expand Down
Expand Up @@ -10,7 +10,7 @@ private UseCaseFactory() {
}

public enum UseCase {
ADD, BULK_ADD, ONE_TO_MANY, PRIMARY_KEY_LOOKUP, SELECT_ALL, DISTINCT, LIMIT, GROUP_HAVING;
ADD, BULK_ADD, BULK_YIELD_ADD, BULK_WIHOUT_YIELD, ONE_TO_MANY, PRIMARY_KEY_LOOKUP, SELECT_ALL, DISTINCT, LIMIT, GROUP_HAVING;
}

public static UseCaseInfo getInfo(UseCase useCase) {
Expand All @@ -19,6 +19,10 @@ public static UseCaseInfo getInfo(UseCase useCase) {
return createUseCaseInfo(FireworkUriConstants.ADD_FIREWORK, RawSql.INSERT_FIREWORK);
case BULK_ADD:
return createUseCaseInfo(FireworkUriConstants.ADD_FIREWORK, RawSql.BULK_INSERT_FIREWORKS);
case BULK_YIELD_ADD:
return createUseCaseInfo(FireworkUriConstants.ADD_FIREWORK_YIELD, RawSql.BULK_INSERT_FIREWORKS);
case BULK_WIHOUT_YIELD:
return createUseCaseInfo(FireworkUriConstants.ADD_FIREWORK_WITHOUT_YIELD, RawSql.BULK_INSERT_FIREWORKS);
case ONE_TO_MANY:
return createUseCaseInfo(FireworkUriConstants.ONE_TO_MANY_SEARCH, RawSql.SELECT_USING_SHOP_FOREIGN_KEY);
case PRIMARY_KEY_LOOKUP:
Expand Down
Expand Up @@ -8,6 +8,7 @@

public class DatabaseWriter {

private static final String KEY_ALLOW_YIELD = "allowYield";
private final ContentResolver contentResolver;

public DatabaseWriter(ContentResolver contentResolver) {
Expand All @@ -19,11 +20,15 @@ public void saveDataToFireworksTable(ContentValues values) {
}

public void bulkSaveDataToFireworksTable(ContentValues[] values) {
bulkSaveDataToTable(DatabaseConstants.TBL_FIREWORKS, values);
bulkSaveDataToTable(DatabaseConstants.TBL_FIREWORKS, values, "true");
}

private void bulkSaveDataToTable(String table, ContentValues[] values) {
Uri uri = createUri(table);
public void bulkSaveDataToFireworksTableWithoutYield(ContentValues[] values) {
bulkSaveDataToTable(DatabaseConstants.TBL_FIREWORKS, values, "false");
}

private void bulkSaveDataToTable(String table, ContentValues[] values, String allowYield) {
Uri uri = createUri(table).buildUpon().appendQueryParameter(KEY_ALLOW_YIELD, allowYield).build();
contentResolver.bulkInsert(uri, values);
}

Expand Down
Expand Up @@ -32,6 +32,16 @@ public void saveFireworks(List<Firework> fireworks) {
databaseWriter.bulkSaveDataToFireworksTable(valuesArray.toArray(new ContentValues[fireworks.size()]));
}

public void saveFireworksWithoutYield(List<Firework> fireworks) {
List<ContentValues> valuesArray = new ArrayList<>();
for (Firework firework : fireworks) {
ContentValues values = createContentValuesFrom(firework);
valuesArray.add(values);
}

databaseWriter.bulkSaveDataToFireworksTableWithoutYield(valuesArray.toArray(new ContentValues[fireworks.size()]));
}

private ContentValues createContentValuesFrom(Firework firework) {
ContentValues values = new ContentValues();
values.put(COL_NAME, firework.getName());
Expand Down
Expand Up @@ -7,6 +7,8 @@ private FireworkUriConstants() {

public static final String VIEW_ALL_SEARCH = FireworkProvider.AUTHORITY + "firework";
public static final String ADD_FIREWORK = FireworkProvider.AUTHORITY + "firework (with ContentValues)";
public static final String ADD_FIREWORK_YIELD = FireworkProvider.AUTHORITY + "firework?allowYield=true (with ContentValues)";
public static final String ADD_FIREWORK_WITHOUT_YIELD = FireworkProvider.AUTHORITY + "firework?allowYield=false (with ContentValues)";
public static final String PRIMARY_KEY_SEARCH = FireworkProvider.AUTHORITY + "firework/1";
public static final String ONE_TO_MANY_SEARCH = FireworkProvider.AUTHORITY + "shop/1/firework";
public static final String GROUP_BY_SEARCH = FireworkProvider.AUTHORITY + "firework?groupBy=shop_id&having=SUM(price)>40";
Expand Down
Expand Up @@ -9,7 +9,6 @@
import com.novoda.sqliteprovider.demo.R;
import com.novoda.sqliteprovider.demo.domain.Firework;
import com.novoda.sqliteprovider.demo.domain.UseCaseFactory;
import com.novoda.sqliteprovider.demo.loader.FireworkSaver;
import com.novoda.sqliteprovider.demo.loader.FireworksBulkSaver;
import com.novoda.sqliteprovider.demo.persistance.DatabaseConstants;
import com.novoda.sqliteprovider.demo.ui.base.NovodaActivity;
Expand Down Expand Up @@ -40,7 +39,7 @@ public void onEmptyInput() {
@Override
public void onBulkAddClick(List<Firework> fireworks) {
this.fireworks = fireworks;
getSupportLoaderManager().initLoader(FireworkSaver.LOADER_ID, null, this);
getSupportLoaderManager().initLoader(FireworksBulkSaver.LOADER_ID, null, this);
}

@Override
Expand Down
@@ -0,0 +1,79 @@
package com.novoda.sqliteprovider.demo.ui;

import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
import android.widget.Toast;

import com.novoda.sqliteprovider.demo.R;
import com.novoda.sqliteprovider.demo.domain.Firework;
import com.novoda.sqliteprovider.demo.domain.UseCaseFactory;
import com.novoda.sqliteprovider.demo.persistance.DatabaseConstants;
import com.novoda.sqliteprovider.demo.persistance.DatabaseWriter;
import com.novoda.sqliteprovider.demo.persistance.FireworkWriter;
import com.novoda.sqliteprovider.demo.ui.base.NovodaActivity;
import com.novoda.sqliteprovider.demo.ui.fragment.AddBulkYieldFireworksFragment;
import com.novoda.sqliteprovider.demo.ui.fragment.UriSqlFragment;

import java.util.List;

public class AddBulkYieldFireworksActivity extends NovodaActivity implements AddBulkYieldFireworksFragment.AddBulkYieldFireworksListener, FireworksWriteTask.Listener {

private UriSqlFragment uriSqlFragment;
private List<Firework> fireworks;
private FireworkWriter writer;
private AddBulkYieldFireworksFragment bulkInsertFragment;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_add_bulk_yield_fireworks);
DatabaseWriter databaseWriter = new DatabaseWriter(getContentResolver());
writer = new FireworkWriter(databaseWriter);
uriSqlFragment = (UriSqlFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_uri_sql);
bulkInsertFragment = (AddBulkYieldFireworksFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_add_bulk_yield_fireworks);
}

@Override
public void onEmptyInput() {
Toast.makeText(this, "Fill in the number of fireworks and threads!", Toast.LENGTH_SHORT).show();
}

@Override
public void onBulkAddClick(List<Firework> fireworks, int numberOfThreads, boolean shouldYield) {
this.fireworks = fireworks;
Firework[] fireworksArray = fireworks.toArray(new Firework[fireworks.size()]);
for (int i = 0; i < numberOfThreads; i++) {
String name = "Thread - " + i + " items: " + fireworks.size() + " - allowYield: " + shouldYield;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
new FireworksWriteTask(name, writer, shouldYield, this).executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, fireworksArray);
} else {
new FireworksWriteTask(name, writer, shouldYield, this).execute(fireworksArray);
}
}
uriSqlFragment.setInfo(shouldYield ? UseCaseFactory.UseCase.BULK_YIELD_ADD : UseCaseFactory.UseCase.BULK_WIHOUT_YIELD);
}

private String createSQL(List<Firework> data) {
Firework firework = data.get(0);
return TextUtils.replace(
DatabaseConstants.RawSql.BULK_INSERT_FIREWORKS,
new String[]{"Times", "Na", "Co", "No", "Ft", "Pr", "Sh"},
new CharSequence[]{
String.valueOf(data.size()),
firework.getName(),
firework.getColor(),
firework.getNoise(),
firework.getType(),
String.valueOf(firework.getPrice()), "1"
}).toString();
}

@Override
public void onFinish(String name, long duration) {
uriSqlFragment.updateSql(createSQL(fireworks));
String text = "Finished: " + name + " - took: " + duration + " millis";
bulkInsertFragment.appendLog(text);
}
}
@@ -0,0 +1,51 @@
package com.novoda.sqliteprovider.demo.ui;

import android.os.AsyncTask;

import com.novoda.sqliteprovider.demo.domain.Firework;
import com.novoda.sqliteprovider.demo.persistance.FireworkWriter;

import java.util.Arrays;
import java.util.List;

class FireworksWriteTask extends AsyncTask<Firework, Void, Void> {

private final String name;
private final FireworkWriter writer;
private final boolean allowYield;
private final Listener listener;

private long startTime;

FireworksWriteTask(String name, FireworkWriter writer, boolean allowYield, Listener listener) {
this.name = name;
this.writer = writer;
this.allowYield = allowYield;
this.listener = listener;
}

@Override
protected void onPreExecute() {
startTime = System.currentTimeMillis();
}

@Override
protected Void doInBackground(Firework... params) {
List<Firework> fireworks = Arrays.asList(params);
if (allowYield) {
writer.saveFireworks(fireworks);
} else {
writer.saveFireworksWithoutYield(fireworks);
}
return null;
}

@Override
protected void onPostExecute(Void fireworks) {
listener.onFinish(name, System.currentTimeMillis() - startTime);
}

interface Listener {
void onFinish(String name, long duration);
}
}
Expand Up @@ -36,6 +36,12 @@ public void onBulkAddFireworksClick(View view) {
startActivity(AddBulkFireworksActivity.class);
}

@Override
@FromXML
public void onBulkAddYieldFireworksClick(View view) {
startActivity(AddBulkYieldFireworksActivity.class);
}

@Override
@FromXML
public void onFindFireworkWithPrimaryKeyClick(View button) {
Expand Down

0 comments on commit 7e8eb02

Please sign in to comment.