Android Open Source - adme A D M E Content Provider






From Project

Back to project page adme.

License

The source code is released under:

Apache License

If you think the Android project adme listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.danielesegato.adme.provider;
/*w  w  w .  ja v a  2s. c om*/
import android.content.ContentProvider;
import android.content.ContentProviderOperation;
import android.content.ContentProviderResult;
import android.content.ContentValues;
import android.content.OperationApplicationException;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteTransactionListener;
import android.net.Uri;
import android.util.SparseArray;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;

/**
 * Ease up the implementation of Content Providers by letting the provider split the different
 * implementations for different contents in different {@link ADMEContentProviderComponent}.
 * <p/>
 * This class is openly inspired by the great implementation of the despicable / component content
 * provider implementation from Sergi Martnez: https://gist.github.com/sergiandreplace/8165986
 * <p/>
 * But it also add a lot of functionality commonly needed by content providers, like:
 * <ul>
 * <li>Properly handling transactions</li>
 * <li>Batch operations support</li>
 * <li>related URI notification handling (very useful when you have a view or a join uri)</li>
 * </ul>
 * <p/>
 * TODO features:
 * <ul>
 * <li>Support for syncToNetwork notifications</li>
 * <li>Support for non-DB operations (File stream etc..)</li>
 * </ul>
 */
public abstract class ADMEContentProvider extends ContentProvider {
    public static final String URI_SCHEME = "content";
    /**
     * Maximum number of operations allowed in a batch between yield points.
     */
    private static final int YIELD_MAX_OPERATIONS_PER_YIELD_POINT = 500;
    private static final long YIELD_SLEEP_AFTER_YIELD_DELAY = 300L;
    private final ThreadLocal<Boolean> tNotifyChange = new ThreadLocal<Boolean>();
    private final ThreadLocal<Set<Uri>> tNotifyUris = new ThreadLocal<Set<Uri>>();
    private final ThreadLocal<Boolean> tApplyingBatch = new ThreadLocal<Boolean>();
    private static final SQLiteTransactionListener DUMMY_TRANSACTION_LISTENER = new SQLiteTransactionListener() {
        @Override
        public void onBegin() {
        }

        @Override
        public void onCommit() {
        }

        @Override
        public void onRollback() {
        }
    };

    public static final Uri buildUri(String scheme, String authority, String path) {
        return new Uri.Builder()
                .scheme(scheme)
                .authority(authority)
                .path(path)
                .build();
    }

    public static final Uri buildUri(String authority, String path) {
        return buildUri(URI_SCHEME, authority, path);
    }

    SparseArray<ADMEContentProviderComponent> components;
    int componentId = 0;
    private UriMatcher uriMatcher;
    private boolean componentsRegistered = false;
    private SQLiteTransactionListener transactionListener = DUMMY_TRANSACTION_LISTENER;

    @Override
    public boolean onCreate() {
        uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
        components = new SparseArray<ADMEContentProviderComponent>();
        return false;
    }

    /**
     * Invoked, lazily, once the first time the ContentProvider is used, it has the responsibility of
     * registering all the components of this content provider.
     *
     * @see #registerComponent(ADMEContentProviderComponent)
     */
    public abstract void registerComponents();

    /**
     * @return the Authority string, must match the one registered in the android manifest
     */
    public abstract String getAuthority();


    /**
     * Register a component to the list of components that will answer to data requests
     *
     * @param component the component to register
     * @return the identifier of the component
     */
    protected int registerComponent(ADMEContentProviderComponent component) {
        componentId++;
        component.setContext(getContext());
        component.setAuthority(getAuthority());
        final Uri componentUri = component.getUri();
        uriMatcher.addURI(componentUri.getAuthority(), component.getBaseUriPath(), componentId);
        components.put(componentId, component);
        return componentId;
    }

    /**
     * Retrieves the component that answers to the provided uri.
     *
     * @param uri the uri you want to match to a component
     * @return The component who requested the type of uri provided
     * @throws java.lang.IllegalArgumentException if the uri doesn't match any registered component
     */
    private ADMEContentProviderComponent getComponent(Uri uri) throws IllegalArgumentException {
        if (!componentsRegistered) {
            registerComponents();
            componentsRegistered = true;
        }
        int uriType = uriMatcher.match(uri);
        if (uriType == -1) {
            throw new IllegalArgumentException("Unknown URI: " + uri);
        }
        return components.get(uriType);
    }

    /**
     * Getter for the current database
     *
     * @return returns the database used by the content provider
     */
    public abstract SQLiteDatabase getDb();

    @Override
    public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
        ADMEContentProviderComponent component = getComponent(uri);
        Cursor cursor = component.query(getDb(), uri, projection, selection, selectionArgs, sortOrder);
        if (cursor != null && component.isAutoSetNotificationUriOnQuery()) {
            cursor.setNotificationUri(getContext().getContentResolver(), uri);
        }
        return cursor;
    }

    @Override
    public String getType(Uri uri) {
        return getComponent(uri).getType();
    }

    @Override
    public Uri insert(Uri uri, ContentValues contentValues) {
        ADMEContentProviderComponent component = getComponent(uri);
        SQLiteDatabase db = getDb();
        Uri result = null;
        boolean applyingBatch = applyingBatch();
        if (!applyingBatch) {
            db.beginTransactionWithListener(transactionListener);
            try {
                result = component.insert(db, uri, contentValues);
                if (result != null) {
                    component.updateNotificationUris(getNotificationUris(), uri);
                }
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
            notifyChangeNow();
        } else {
            result = component.insert(db, uri, contentValues);
            if (result != null) {
                component.updateNotificationUris(getNotificationUris(), uri);
                tNotifyChange.set(true);
            }
        }
        return result;
    }

    @Override
    public int delete(Uri uri, String where, String[] selectionArgs) {
        ADMEContentProviderComponent component = getComponent(uri);
        SQLiteDatabase db = getDb();
        int count = 0;
        boolean applyingBatch = applyingBatch();
        if (!applyingBatch) {
            db.beginTransactionWithListener(transactionListener);
            try {
                count = component.delete(db, uri, where, selectionArgs);
                if (count > 0) {
                    component.updateNotificationUris(getNotificationUris(), uri);
                }
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
            notifyChangeNow();
        } else {
            count = component.delete(db, uri, where, selectionArgs);
            if (count > 0) {
                component.updateNotificationUris(getNotificationUris(), uri);
                tNotifyChange.set(true);
            }
        }
        return count;
    }

    @Override
    public int update(Uri uri, ContentValues values, String where, String[] selectionArgs) {
        ADMEContentProviderComponent component = getComponent(uri);
        SQLiteDatabase db = getDb();
        int count = 0;
        boolean applyingBatch = applyingBatch();
        if (!applyingBatch) {
            db.beginTransactionWithListener(transactionListener);
            try {
                count = component.update(db, uri, values, where, selectionArgs);
                if (count > 0) {
                    component.updateNotificationUris(getNotificationUris(), uri);
                }
                db.setTransactionSuccessful();
            } finally {
                db.endTransaction();
            }
            notifyChangeNow();
        } else {
            count = component.update(db, uri, values, where, selectionArgs);
            if (count > 0) {
                component.updateNotificationUris(getNotificationUris(), uri);
            }
        }
        return count;
    }

    @Override
    public int bulkInsert(Uri uri, ContentValues[] valuesTable) {
        ADMEContentProviderComponent component = getComponent(uri);
        SQLiteDatabase db = getDb();
        int numValues = valuesTable.length;
        db.beginTransactionWithListener(transactionListener);
        int ypCount = 0;
        boolean success = false;
        try {
            for (int i = 0; i < numValues; i++) {
                Uri result = component.insert(db, uri, valuesTable[i]);
                if (result != null) {
                    component.updateNotificationUris(getNotificationUris(), uri);
                    tNotifyChange.set(true);
                }
                if (db.yieldIfContendedSafely()) {
                    ypCount++;
                }
            }
            db.setTransactionSuccessful();
            success = true;
        } finally {
            db.endTransaction();
            if (success || ypCount > 0) {
                notifyChangeNowIfNeeded();
            }
        }
        return numValues;
    }

    @Override
    public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
            throws OperationApplicationException {
        int ypCount = 0;
        int opCount = 0;
        SQLiteDatabase db = getDb();
        db.beginTransactionWithListener(transactionListener);
        boolean success = false;
        try {
            tApplyingBatch.set(true);
            final int numOperations = operations.size();
            final ContentProviderResult[] results = new ContentProviderResult[numOperations];
            for (int i = 0; i < numOperations; i++) {
                if (++opCount > getMaxOperationsPerYield()) {
                    throw new OperationApplicationException(
                            String.format("Too many content provider operations between yield points. The maximum number of operations per yield point is %d",
                                    getMaxOperationsPerYield())
                            , ypCount
                    );
                }
                final ContentProviderOperation operation = operations.get(i);
                if (i > 0 && operation.isYieldAllowed()) {
                    opCount = 0;
                    if (db.yieldIfContendedSafely(YIELD_SLEEP_AFTER_YIELD_DELAY)) {
                        ypCount++;
                    }
                }
                results[i] = operation.apply(this, results, i);
            }
            db.setTransactionSuccessful();
            success = true;
            return results;
        } finally {
            tApplyingBatch.set(false);
            db.endTransaction();
            if (success || ypCount > 0) {
                notifyChangeNowIfNeeded();
            }
        }
    }

    protected void setTransactionListener(SQLiteTransactionListener transactionListener) {
        this.transactionListener = transactionListener != null ? transactionListener : DUMMY_TRANSACTION_LISTENER;
    }

    protected void notifyChangeNowIfNeeded() {
        if (tNotifyChange.get() != null && tNotifyChange.get()) {
            tNotifyChange.set(false);
            notifyChangeNow();
        }
    }

    private void notifyChangeNow() {
        for (Uri uri : getNotificationUris()) {
            // TODO syncToNetwork should be decided by the component when inserting / updating / deleting
            // The idea is that you change the DB locally then set syncToNetwork = true to wake up the
            // SyncAdapter which will use the content provider again to actually update the remote content
            // when the content is modified locally and should be synced remotely we should pass true here
            getContext().getContentResolver().notifyChange(uri, null, false);
        }
        getNotificationUris().clear();
    }

    /**
     * @return the current thread notification uris map
     */
    protected Set<Uri> getNotificationUris() {
        Set<Uri> notificationUris = tNotifyUris.get();
        if (notificationUris == null) {
            notificationUris = new HashSet<Uri>();
            tNotifyUris.set(notificationUris);
        }
        return notificationUris;
    }

    private boolean applyingBatch() {
        return tApplyingBatch.get() != null && tApplyingBatch.get();
    }

    /**
     * @return Number of operations that can be applied at once without a yield point.
     */
    protected int getMaxOperationsPerYield() {
        return YIELD_MAX_OPERATIONS_PER_YIELD_POINT;
    }
}




Java Source Code List

com.danielesegato.adme.ADME.java
com.danielesegato.adme.annotation.ADMEEntity.java
com.danielesegato.adme.annotation.ADMEField.java
com.danielesegato.adme.annotation.ADMEIndexConstraint.java
com.danielesegato.adme.config.ADMEConfigUtils.java
com.danielesegato.adme.config.ADMEEntityConfig.java
com.danielesegato.adme.config.ADMEFieldConfig.java
com.danielesegato.adme.config.ADMEIndexConstraintConfig.java
com.danielesegato.adme.config.OnForeignUpdateDelete.java
com.danielesegato.adme.config.SQLiteType.java
com.danielesegato.adme.db.ADMESerializerMapping.java
com.danielesegato.adme.db.ADMESerializer.java
com.danielesegato.adme.db.ContentProviderUris.java
com.danielesegato.adme.db.SQLiteContentProvider.java
com.danielesegato.adme.db.serializer.BaseADMESerializer.java
com.danielesegato.adme.db.serializer.BigDecimalADMESerializer.java
com.danielesegato.adme.db.serializer.BooleanADMESerializer.java
com.danielesegato.adme.db.serializer.BooleanObjectADMESerializer.java
com.danielesegato.adme.db.serializer.CurrencyADMESerializer.java
com.danielesegato.adme.db.serializer.DateAsStringADMESerializer.java
com.danielesegato.adme.db.serializer.DateAsTimestampADMESerializer.java
com.danielesegato.adme.db.serializer.DoubleADMESerializer.java
com.danielesegato.adme.db.serializer.DoubleObjectADMESerializer.java
com.danielesegato.adme.db.serializer.EnumIntADMESerializer.java
com.danielesegato.adme.db.serializer.EnumStringADMESerializer.java
com.danielesegato.adme.db.serializer.IntADMESerializer.java
com.danielesegato.adme.db.serializer.IntObjectADMESerializer.java
com.danielesegato.adme.db.serializer.LongADMESerializer.java
com.danielesegato.adme.db.serializer.LongObjectADMESerializer.java
com.danielesegato.adme.db.serializer.StringADMESerializer.java
com.danielesegato.adme.provider.ADMEContentProviderComponent.java
com.danielesegato.adme.provider.ADMEContentProvider.java
com.danielesegato.adme.utils.DateHelper.java
com.danielesegato.adme.utils.SQLStringHelper.java
com.danielesegato.adme.utils.SQLiteScriptParser.java
com.danielesegato.demo.adme.ADMEDemoMainActivity.java
com.danielesegato.demo.adme.NavigationDrawerFragment.java