AsyncAccessor.java
package org.cook_e.data;
import android.os.Handler;
import java.sql.SQLException;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* Wraps an {@link SQLAccessor} and provides asynchronous versions of its functionality
*
* Methods of this class must only be called from one thread (usually the main thread).
* Result handlers will be invoked on that thread.
*/
public class AsyncAccessor {
/**
* Interface for objects that can be notified when a result or error is available
* @param <T> the result type
*/
public interface ResultHandler<T> {
/**
* Called when a result is available
* @param result the result
*/
void onResult(T result);
/**
* Called when the computation resulted in an exception
* @param e the exception that was thrown
*/
void onException(Exception e);
}
/**
* The handler used to return results to the calling thread
*/
private final Handler mHandler;
/**
* The underlying accessor
*/
private final SQLAccessor mAccessor;
/**
* The executor service used to run tasks
*/
private final ExecutorService mExecutorService;
/**
* Creates a new AsyncAccessor
*
* This constructor must be called from the thread that will call other methods and get results.
*
* @param accessor the accessor to wrap. The accessor class does not need to be thread-safe.
*/
public AsyncAccessor(SQLAccessor accessor) {
mAccessor = accessor;
// Create a new Handler on the thread that called this method
mHandler = new Handler();
mExecutorService = Executors.newSingleThreadExecutor();
}
/**
* Inserts a recipe into the database
* @param recipe the recipe to insert
* @param handler a handler to be notified if an error occurs (on success, the handler will be
* passed a null reference)
*/
public void insertRecipe(Recipe recipe, final ResultHandler<?> handler) {
final Recipe copy = new Recipe(recipe);
mExecutorService.submit(new Runnable() {
@Override
public void run() {
try {
synchronized (mAccessor) {
mAccessor.storeRecipe(copy);
}
} catch (SQLException e) {
postException(handler, e);
}
}
});
}
/**
* Loads all available recipes from the database
* @param handler a result handler
*/
public void loadAllRecipes(final ResultHandler<? super List<Recipe>> handler) {
mExecutorService.submit(new Runnable() {
@Override
public void run() {
try {
List<Recipe> recipes;
synchronized (mAccessor) {
recipes = mAccessor.loadAllRecipes();
}
postResult(handler, recipes);
} catch (SQLException e) {
postException(handler, e);
}
}
});
}
/**
* Removes all recipes and bunches
* @param handler a result handler to be notified if an error occurs
*/
public void clearAllTables(final ResultHandler<?> handler) {
mExecutorService.submit(new Runnable() {
@Override
public void run() {
try {
synchronized (mAccessor) {
mAccessor.clearAllTables();
}
} catch (SQLException e) {
postException(handler, e);
}
}
});
}
/**
* Posts to {@link #mHandler} a Runnable that provides a result to a handler
* @param handler the handler to provide a result to
* @param result the result to provide
* @param <T> the result type
*/
private <T> void postResult(final ResultHandler<T> handler, final T result) {
mHandler.post(new Runnable() {
@Override
public void run() {
handler.onResult(result);
}
});
}
/**
* Posts to {@link #mHandler} a Runnable that provides an exception to a handler
* @param handler the handler to provide a result to
* @param exception the exception to provide
*/
private void postException(final ResultHandler<?> handler, final Exception exception) {
mHandler.post(new Runnable() {
@Override
public void run() {
handler.onException(exception);
}
});
}
}