Using ContentResolver instead of ContentProviderCl

2019-04-26 14:42发布

问题:

As I understand from the docs, one SyncAdapter defined in a SyncService is limited to receive only one ContentProvider authority to work on.

But, at the same time, it has access to ContentResolver which it allows to run queries on other ContentProviders as well. I don't understand this particular design concept if a developer is needed to provide a single content authority to SyncAdapter and nonetheless she is able to do whatever she wants on whatever ContentProvider she has access to. My question is: What are the consequences of ignoring onPerformSync's parameters: String authority and ContentProviderClient provider and going with pure ContentResolver?

My application's (actually its SyncService) idea is simple: query a calendar server (OwnCloud in my case) to get not only events (synced with com.android.calendar) but also VTODOS, which are then distributed between various task management apps I can get source code and/or ContentProviderContract. I also thought of my own "Hub" ContentProvider, which has basic VTODO/Task structure, and is the only one compared to the server. It should be able to sync 2-way with different content providers of task management apps and then it syncs with the server.

I have read using ContentProviderClient vs ContentResolver to access content provider and I think I understand the difference. I'm now puzzled why there is so strong suggestion from android SDK to use a single ContentProvider in a single SyncAdapter and yet you are allowed to use ContentResolver to bypass that limitation.

I spent all day figuring this out and searched hundreds of SO/Google resources on the matter (some of them multiple times). I have also seen questions regarding using one SyncAdapter to sync multiple ContentProviders, but none of the answers were any close to suggesting using ContentResolver instead.

回答1:

There is no special limitation on ContentResolver's API when used from the context of SyncAdapter. IMHO, the only reason why the framework passes ContentProviderClient and authority to onPerformSync() is convenience and kind of a hint to developers as to how SyncAdapter intended work.

This fact is easily seen in the source code for AbstractThreadedSyncAdapter.SyncThread - the ContentProviderClient passed to onPerformSync() is obtained in a standard fashion:

    @Override
    public void run() {
        Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

        // Trace this sync instance.  Note, conceptually this should be in
        // SyncStorageEngine.insertStartSyncEvent(), but the trace functions require unique
        // threads in order to track overlapping operations, so we'll do it here for now.
        Trace.traceBegin(Trace.TRACE_TAG_SYNC_MANAGER, mAuthority);

        SyncResult syncResult = new SyncResult();
        ContentProviderClient provider = null;
        try {
            if (isCanceled()) {
                return;
            }
            provider = mContext.getContentResolver().acquireContentProviderClient(mAuthority);
            if (provider != null) {
                AbstractThreadedSyncAdapter.this.onPerformSync(mAccount, mExtras,
                        mAuthority, provider, syncResult);
            } else {
                syncResult.databaseError = true;
            }
        } finally {
            Trace.traceEnd(Trace.TRACE_TAG_SYNC_MANAGER);

            if (provider != null) {
                provider.release();
            }
            if (!isCanceled()) {
                mSyncContext.onFinished(syncResult);
            }
            // synchronize so that the assignment will be seen by other threads
            // that also synchronize accesses to mSyncThreads
            synchronized (mSyncThreadLock) {
                mSyncThreads.remove(mThreadsKey);
            }
        }
    }

Therefore, the bootom line: you can use ContentResolver in your SyncAdapter as you wish - just call getContext().getContentResolver() and access any exported ContentProvider.