支持Bundle配置Limit的CursorLoader

125 阅读1分钟
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.os.CancellationSignal;
import androidx.core.os.OperationCanceledException;
import androidx.loader.content.CursorLoader;

/**
 * 支持Bundle配置Limit的CursorLoader
 */
public class BundleCursorLoader extends CursorLoader {
    ForceLoadContentObserver mObserver;
    Uri mUri;
    String[] mProjection;
    Bundle mArgsBundle;
    Cursor mCursor;
    CancellationSignal mCancellationSignal;

    public BundleCursorLoader(@NonNull Context context) {
        super(context);
        mObserver = new ForceLoadContentObserver();
    }

    public BundleCursorLoader(@NonNull Context context, @NonNull Uri uri, @Nullable String[] projection, @Nullable Bundle argsBundle) {
        super(context);
        mObserver = new ForceLoadContentObserver();
        mUri = uri;
        mProjection = projection;
        mArgsBundle = argsBundle;
    }


    /* Runs on a worker thread */
    @Override
    public Cursor loadInBackground() {
        synchronized (this) {
            if (isLoadInBackgroundCanceled()) {
                throw new OperationCanceledException();
            }
            mCancellationSignal = new CancellationSignal();
        }
        try {
            Cursor cursor = queryWithArgs(getContext().getContentResolver(),
                    mUri, mProjection, mArgsBundle,
                    mCancellationSignal);
            if (cursor != null) {
                try {
                    // Ensure the cursor window is filled.
                    cursor.getCount();
                    cursor.registerContentObserver(mObserver);
                } catch (RuntimeException ex) {
                    cursor.close();
                    throw ex;
                }
            }
            return cursor;
        }catch (Throwable ex){
            ex.printStackTrace();
            return null;
        } finally {
            synchronized (this) {
                mCancellationSignal = null;
            }
        }
    }

    @Override
    public void cancelLoadInBackground() {
        synchronized (this) {
            if (mCancellationSignal != null) {
                mCancellationSignal.cancel();
            }
        }
    }

    /* Runs on the UI thread */
    @Override
    public void deliverResult(Cursor cursor) {
        if (isReset()) {
            // An async query came in while the loader is stopped
            if (cursor != null) {
                cursor.close();
            }
            return;
        }
        Cursor oldCursor = mCursor;
        mCursor = cursor;

        if (isStarted()) {
            super.deliverResult(cursor);
        }

        if (oldCursor != null && oldCursor != cursor && !oldCursor.isClosed()) {
            oldCursor.close();
        }
    }

    /**
     * Starts an asynchronous load of the contacts list data. When the result is ready the callbacks
     * will be called on the UI thread. If a previous load has been completed and is still valid
     * the result may be passed to the callbacks immediately.
     * <p>
     * Must be called from the UI thread
     */
    @Override
    protected void onStartLoading() {
        if (mCursor != null) {
            deliverResult(mCursor);
        }
        if (takeContentChanged() || mCursor == null) {
            forceLoad();
        }
    }

    /**
     * Must be called from the UI thread
     */
    @Override
    protected void onStopLoading() {
        // Attempt to cancel the current load task if possible.
        cancelLoad();
    }

    @Override
    public void onCanceled(Cursor cursor) {
        if (cursor != null && !cursor.isClosed()) {
            cursor.close();
        }
    }

    @Override
    protected void onReset() {
        super.onReset();

        // Ensure the loader is stopped
        onStopLoading();

        if (mCursor != null && !mCursor.isClosed()) {
            mCursor.close();
        }
        mCursor = null;
    }

    @NonNull
    public Uri getUri() {
        return mUri;
    }

    public void setUri(@NonNull Uri uri) {
        mUri = uri;
    }

    @Nullable
    public String[] getProjection() {
        return mProjection;
    }

    public void setProjection(@Nullable String[] projection) {
        mProjection = projection;
    }

    public Bundle getArgsBundle() {
        return mArgsBundle;
    }

    public void setArgsBundle(Bundle argsBundle) {
        mArgsBundle = argsBundle;
    }

    @Nullable
    public static Cursor queryWithArgs(@NonNull ContentResolver resolver,
                                       @NonNull Uri uri, @Nullable String[] projection, @Nullable Bundle queryArgs,
                                       @Nullable CancellationSignal cancellationSignal) {
        final android.os.CancellationSignal cancellationSignalObj =
                (android.os.CancellationSignal)
                        (cancellationSignal != null
                                ? cancellationSignal.getCancellationSignalObject()
                                : null);
        return resolver.query(uri, projection, queryArgs, cancellationSignalObj);
    }
}