深入分析ContentProvider


  ContentProvider是Android四大組件之一,承擔着跨進程數據訪問的重要職責。本文就從一次ContentProvider訪問入手,分析下它是怎么完成跨進程數據訪問的。

  既然是跨進程,那就必須有一個客戶端進程和一個ContentProvider進程,我們先從客戶端進程分析,看它如何訪問ContentProvider進程。以Query操作為例,一般情況下,當我們需要訪問ContentProvider的時候一般都會執行這么一句:

getContentResolver().query(uri, projection, selection, selectionArgs, sortOrder);

  ContentResolver是什么?Google的解釋是“This class provides applications access to the content model”。實際上我們正是通過ContentResolver來獲取一個IContentProvider對象,通過IContentProvider對象我們盡可以進行IPC通訊了。getContentResolver()方法定義Context類中,實際上Context是一個抽象類,在客戶端應用程序中getContext()實際上返回的是一個ContextImp對象,getContentResolver()方法就定義在ContextImp.java中,並且最終返回ContextImp的內部類ApplicationContentResolver,從名字上看這是一個Application級別的對象。

  ApplicationContentResolver我們稍后再說,先看下query方法都干了什么:

public final Cursor query(final Uri uri, String[] projection,
            String selection, String[] selectionArgs, String sortOrder,
            CancellationSignal cancellationSignal) {
        IContentProvider unstableProvider = acquireUnstableProvider(uri);
        if (unstableProvider == null) {
            return null;
        }
        IContentProvider stableProvider = null;
        try {
            long startTime = SystemClock.uptimeMillis();

            ICancellationSignal remoteCancellationSignal = null;
            if (cancellationSignal != null) {
                cancellationSignal.throwIfCanceled();
                remoteCancellationSignal = unstableProvider.createCancellationSignal();
                cancellationSignal.setRemote(remoteCancellationSignal);
            }
            Cursor qCursor;
            try {
                qCursor = unstableProvider.query(uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            } catch (DeadObjectException e) {
                // The remote process has died...  but we only hold an unstable
                // reference though, so we might recover!!!  Let's try!!!!
                // This is exciting!!1!!1!!!!1
                unstableProviderDied(unstableProvider);
                stableProvider = acquireProvider(uri);
                if (stableProvider == null) {
                    return null;
                }
                qCursor = stableProvider.query(uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            }
            if (qCursor == null) {
                return null;
            }
            // force query execution
            qCursor.getCount();
            long durationMillis = SystemClock.uptimeMillis() - startTime;
            maybeLogQueryToEventLog(durationMillis, uri, projection, selection, sortOrder);
            // Wrap the cursor object into CursorWrapperInner object
            CursorWrapperInner wrapper = new CursorWrapperInner(qCursor,
                    stableProvider != null ? stableProvider : acquireProvider(uri));
            stableProvider = null;
            return wrapper;
        } catch (RemoteException e) {
            // Arbitrary and not worth documenting, as Activity
            // Manager will kill this process shortly anyway.
            return null;
        } finally {
            if (unstableProvider != null) {
                releaseUnstableProvider(unstableProvider);
            }
            if (stableProvider != null) {
                releaseProvider(stableProvider);
            }
        }
    }

  上面的代碼有四個關鍵步驟:

  1. acquireUnstableProvider

  2. unstableProvider.query(......)

  3. qCursor.getCount();

  4. return new CursorWrapperInner(......)

  接下來我們一步一步分析這四個關鍵步驟。

  第一步:acquireUnstableProvider

  乍看上去感覺怪怪的,好端端的為什么加上了一個Unstable的標簽?難道還有stable的不成?事實確實如此,我們知道此時的ContentResolver實際上是一個ApplicationContentResovler對象,來看下ApplicationContentResovler

private static final class ApplicationContentResolver extends ContentResolver {
        private final ActivityThread mMainThread;
        private final UserHandle mUser;

        public ApplicationContentResolver(
                Context context, ActivityThread mainThread, UserHandle user) {
            super(context);
            mMainThread = Preconditions.checkNotNull(mainThread);
            mUser = Preconditions.checkNotNull(user);
        }

        @Override
        protected IContentProvider acquireProvider(Context context, String auth) {
            return mMainThread.acquireProvider(context, auth, mUser.getIdentifier(), true);
        }

        @Override
        protected IContentProvider acquireExistingProvider(Context context, String auth) {
            return mMainThread.acquireExistingProvider(context, auth, mUser.getIdentifier(), true);
        }

        @Override
        public boolean releaseProvider(IContentProvider provider) {
            return mMainThread.releaseProvider(provider, true);
        }

        @Override
        protected IContentProvider acquireUnstableProvider(Context c, String auth) {
            return mMainThread.acquireProvider(c, auth, mUser.getIdentifier(), false);
        }

        @Override
        public boolean releaseUnstableProvider(IContentProvider icp) {
            return mMainThread.releaseProvider(icp, false);
        }

        @Override
        public void unstableProviderDied(IContentProvider icp) {
            mMainThread.handleUnstableProviderDied(icp.asBinder(), true);
        }
    }

  實際上,是否是stable的,都將調用ActivityThread的acquireProvider方法,區別就是最后的一個參數boolean stable。這個機制是API 16引入的,文章的最后會對此進行說明。現在我們只要知道它最終走到了ActivityThread的acquireProvider就可以了。在ActivityThread的acquireProvider方法中,我們首先會去acquireExistingProvider,從字面上就可以看出這是從一個類緩存的地方讀取已經保存的ContentProvider對象,如果不存在,就會調用ActivityManagerNative.getDefault().getContentProvider(getApplicationThread(), auth, userId, stable);從這兒開始,ActivityManagerService就要登場了,上面的代碼最終會通過binder通信調用ActivityManagerService的getContentProviderImpl,這塊的邏輯比較復雜,涉及到了Android組件啟動的過程,我們只需知道客戶端調用會阻塞在ActivityManagerNative.getDefault().getContentProvider,ActivityManagerService啟動目標ContentProvider進程后(如果ContentProvider進程已經存在則不必重啟),返回一個目標ContentProvider的實例。在這兒需要說明的是,ContentProvider可以再Manifest中配置一個叫做android:multiprocess的屬性,默認值是false,表示ContentProvider是單例的,無論哪個客戶端應用的訪問都將是一個ContentProvider對象(當然,必須是同一個ContentProvider,即Uri或者Authority name是一個),如果設為true,系統會為每一個訪問該ContentProvider的進程創建一個實例。因為android:multiprocess的默認值是false,所以我們在寫自己的ContentProvider的時候還是要注意並發的情況。

  扯得有點遠了,回到我們之前步驟,我們已經得到了ContentProvider的實例,這個時候第一步就完成了,接下來看第二步。

  第二步:unstableProvider.query(......)

  unstableProvider實際上是IContentProvider實例,IContentProvider是進行IPC通訊的接口,這個query實際上調用的是目標ContentProvider中的query方法,當然,在真正調用目標ContentProvider的query方法之前,還需要經過enforceReadPermission方法,這一步主要是看下該ContentProvier有沒有export,讀寫權限等等(enforceReadPermission方法只判斷讀權限)。隨后執行query方法,並且返回一個cursor對象。

  以上就是第二步的大致邏輯,不過不要以為這么簡單就結束了。IContentProvider的query可是跨進程的,我們知道ContentProvider的query方法可是五花八門,有訪問數據庫返回SQLiteCursor的,有返回MatrixCursor的等等,那么IContentProvider返回的那個cursor到底是什么呢?我們來看下IContentProvider的這個IPC通信到底是怎么回事

  IPC通信需要兩端,對於我們的例子,這兩段分別是ContentProviderProxy和ContentProviderNative,首先會執行ContentProviderProxy的query方法,然后通過binder通信執行ContentProviderNative的onTransact方法。ContentProviderProxy的query方法有一下五個主要步驟:

  1. new一個BulkCursorToCursorAdaptor對象——adaptor

  2. 填充data用於binder通信

  3. 調用mRemote.transact,這是一個阻塞的過程,直到ContentProviderNative的onTransact方法返回

  4. 讀取reply數據,new一個BulkCursorDescriptor並以此初始化adaptor

  5. return adaptor

  ContentProviderNative的onTransact會調用ContentProvider的query方法,並根據query返回的cursor初始化一個CursorToBulkCursorAdaptor對象,最終將BulkCursorDescriptor對象寫入reply中。

  至此我們知道了,不管我們的ContentProvider query方法返回的到底是什么樣的cursor,最終在客戶端進程都將會被封裝在一個BulkCursorToCursorAdaptor對象中,那么這個BulkCursorToCursorAdaptor對象是不是就是我們在客戶端調用query返回的最終類型呢?別急,往下看。

  第三步:qCursor.getCount();

  這一步看似雞肋,實際上涉及到了SQLiteCursor的一個設計要點,那就是SQLiteCursor的內存共享。getCount會調用SQLiteCursor的fillWindow,在以后的文章中我會在講到SQLiteCursor,在此我們只要知道它是強制執行數據庫query就可以了。

  第四步:return new CursorWrapperInner(......)

  哈,看到了吧,我們在客戶端調用query最終返回的是一個CursorWrapperInner類型,它是ContentResolver的一個內部類。實際上我們常用的getCount,onMove等一些列方法都是通過BulkCursorToCursorAdaptor和CursorToBulkCursorAdaptor的交互實現的。

 

  到此,ContentProvider的訪問流程就結束了,下面說一下開頭買的坑:unstable和stable的ContentProvider。

  

  在4.1之前,我們都可能會遇到過這樣的場景,我們的應用程序訪問了ContentProvider,但是這個ContentProvider意外掛了,這個時候我們的應用程序也將被連帶殺死!這是Android處於對數據安全的考慮而做的決定,不過貌似Google也感覺這樣的方式不太友好,所以在4.1以后提出了stable和unstable的概念。對於ContentResolver的query方法,我們將默認使用unstable的ContentProvider。看看下面的代碼

       Cursor qCursor;
            try {
                qCursor = unstableProvider.query(uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            } catch (DeadObjectException e) {
                // The remote process has died...  but we only hold an unstable
                // reference though, so we might recover!!!  Let's try!!!!
                // This is exciting!!1!!1!!!!1
                unstableProviderDied(unstableProvider);
                stableProvider = acquireProvider(uri);
                if (stableProvider == null) {
                    return null;
                }
                qCursor = stableProvider.query(uri, projection,
                        selection, selectionArgs, sortOrder, remoteCancellationSignal);
            }

  上面是ContentResolver query中的一部分,可以看出unstable ContentProvider在query過程中如果發生了DeadObjectExeption則會被捕獲,進而重新獲取一個stable的ContentProvider。其實深入分析stable和unstable的ContentProvider還會有很多內容,以后有時間再說。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM