11

ContentProviderSQLiteデータベースへのアクセスを管理するカスタムがあります。データベーステーブルのコンテンツをにロードするには、aと:ListFragmentを使用します。LoaderManagerCursorLoaderCursorAdapter

public class MyListFragment extends ListFragment implements LoaderCallbacks<Cursor> {
    // ...
    CursorAdapter mAdapter;

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        mAdapter = new CursorAdapter(getActivity(), null, 0);
        setListAdapter(mAdapter);
        getLoaderManager().initLoader(LOADER_ID, null, this);
    }

    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        return new CursorLoader(getActivity(), CONTENT_URI, PROJECTION, null, null, null);
    }

    public void onLoadFinished(Loader<Cursor> loader, Cursor c) {
        mAdapter.swapCursor(c);
    }

    public void onLoaderReset(Loader<Cursor> loader) {
        mAdapter.swapCursor(null);
    }
}

ContentProviderSQLiteデータベースは、Webサービスから多数のアイテムをフェッチし、バッチ操作( )を介してこれらのアイテムをデータベースに挿入するバックグラウンドタスクによって更新されますContentResolver#applyBatch()

これがバッチ操作であっても、ContentProvider#insert()行ごとに呼び出されるgetsはデータベースに挿入され、現在の実装では、挿入コマンドごとにContentProvider呼び出されます。setNotificationUri()

その結果CursorAdapter、通知のバーストを受信し、UIが頻繁に更新されて、結果として迷惑なちらつき効果が発生します。

理想的には、バッチ操作が進行中の場合、ContentObserver各挿入コマンドではなく、バッチ操作の最後にのみ通知する方法が必要です。

これが可能かどうか誰かが知っていますか?ContentProvider実装を変更して、そのメソッドをオーバーライドできることに注意してください。

4

3 に答える 3

13

Google の I/O アプリから発見した、より簡単な解決策を見つけました。ContentProvider で applyBatch メソッドをオーバーライドし、トランザクションですべての操作を実行するだけです。トランザクションがコミットされるまで通知は送信されません。これにより、送信される ContentProvider 変更通知の数が最小限に抑えられます。

@Override
public ContentProviderResult[] applyBatch(ArrayList<ContentProviderOperation> operations)
        throws OperationApplicationException {

    final SQLiteDatabase db = dbOpenHelper.getWritableDatabase();
    db.beginTransaction();
    try {
        final int numOperations = operations.size();
        final ContentProviderResult[] results = new ContentProviderResult[numOperations];
        for (int i = 0; i < numOperations; i++) {
            results[i] = operations.get(i).apply(this, results, i);
        }
        db.setTransactionSuccessful();
        return results;
    } finally {
        db.endTransaction();
    }
}
于 2012-05-19T21:57:01.300 に答える
7

To address this exact problem, I overrode applyBatch and set a flag which blocked other methods from sending notifications.

    volatile boolean applyingBatch=false;
    public ContentProviderResult[] applyBatch(
        ArrayList<ContentProviderOperation> operations)
        throws OperationApplicationException {
    applyingBatch=true;
    ContentProviderResult[] result;
    try {
        result = super.applyBatch(operations);
    } catch (OperationApplicationException e) {
        throw e;
    }
    applyingBatch=false;
    synchronized (delayedNotifications) {
        for (Uri uri : delayedNotifications) {
            getContext().getContentResolver().notifyChange(uri, null);
        }
    }
    return result;
}

I exposed a method to "store" notifications to be sent when the batch was complete:

protected void sendNotification(Uri uri) {
    if (applyingBatch) {
        if (delayedNotifications==null) {
            delayedNotifications=new ArrayList<Uri>();
        }
        synchronized (delayedNotifications) {
            if (!delayedNotifications.contains(uri)) {
                delayedNotifications.add(uri);
            }
        }
    } else {
        getContext().getContentResolver().notifyChange(uri, null);
    }
}

And any methods that send notifications employ sendNotification, rather than directly firing a notification.

There may well be better ways of doing this - it certainly seems as though they're ought to be - but that's what I did.

于 2012-03-21T09:48:01.603 に答える
0

元の回答へのコメントで、Jens は AOSP の SQLiteContentProvider に向けて私たちを導きました。これが SDK に (まだ?) 含まれていない理由の 1 つは、AOSP にこのコードの複数のバリエーションが含まれているように見えるためです。

たとえば、com.android.browser.provider.SQLiteContentProviderは、Phillip Fitzsimmons によって提案された「遅延通知」の原則を取り入れながら、バッチ フラグに ThreadLocal を使用してアクセスを同期することでプロバイダーをスレッドセーフに保ちながら、もう少し完全なソリューションのようです。遅延通知セットに。

しかし、変更が通知される一連の URI へのアクセスが同期されていても、競合状態が発生する可能性があることは想像できます。たとえば、長い操作がいくつかの通知を投稿した場合、最初の操作がコミットされる前に、通知を起動してセットをクリアする小さなバッチ操作に追い越されます。

それでも、上記のバージョンは、独自のプロバイダーを実装する際のリファレンスとして最適な方法のようです。

于 2012-11-22T00:25:44.407 に答える