深入理解ContentProvider共享數據更新通知機制

前面一篇文章  深入理解Android四大組件之一ContentProvider 講了ContentProvider的簡單使用和它的啓動過程.
這裏接着講解一下關於ContentProvider共享數據更新通知機制.
數據更新通知是很有必要的,比如我們在第二個應用程序中添加了一個聯繫人(接着上一篇文章例子),返回到顯示主界面時,我們不會去更新列表數據,所以就沒有剛剛添加的聯繫人信息,這樣數據不同步,影響客戶體驗.不單單是第二個程序不會察覺到數據庫裏數據已經更新了,其他的客戶端程序,這些程序都是共享第一個應用程序的數據的,也不會察覺到.因此,在第一個應用程序裏面,我們需要做一件事,就是當本地數據庫的數據更新時,就通知其他對該數據庫數據感興趣的客戶端程序,好讓它們及時更新數據.

Android應用程序組件ContentProvider中的數據更新通知機制和Android系統中的廣播(Broadcast)通知機制的實現思路是相似的。在Android的廣播機制中,首先是接收者對自己感興趣的廣播進行註冊,接着當發送者發出這些廣播時,接收者就會得到通知了。然而,ContentProvider中的數據監控機制與Android系統中的廣播機制又有三個主要的區別,一是前者是通過URI來把通知的發送者和接收者關聯在一起的,而後者是通過Intent來關聯的,二是前者的通知註冊中心是由ContentService服務來扮演的,而後者是由ActivityManagerService服務來扮演的,三是前者負責接收數據更新通知的類必須要繼承ContentObserver類,而後者要繼承BroadcastReceiver類。之所以會有這些區別,是由於ContentProivder組件的數據共享功能本身就是建立在URI的基礎之上的,因此專門針對URI來設計另外一套通知機制會更實用和方便,而Android系統的廣播機制是一種更加通用的事件通知機制,它的適用範圍會更廣泛一些。

        這裏我們把ContentProvider的數據更新機制劃分爲三個單元進行分析,第一個單元是ContentService的啓動過程,第二個單元是監控數據變化的ContentObserver的註冊過程,第二個單元是數據更新通知的發送過程。

1,ContentService在系統啓動的時候就啓動起來了,以便後面啓動起來的應用程序可以使用它.關於ContentService的啓動過程這裏沒分析了.

2.ContentObserver的註冊過程分析

    resolver.registerContentObserver(Uri.parse("content://com.cj.mycontentprovider/contact"),  
                    true,new MyContentObserver(new Handler()));


在第二個應用程序中 通過調用ContentResolver對象的registerContentObserver()方法 來註冊一個自定義的ContentObserver(MyContentObserver)來監控MyContentProvider這個Content Provider中的數據變化.

     private class MyContentObserver extends ContentObserver{  
      
            /**
             * Creates a content observer.
             *
             * @param handler The handler to run {@link #onChange} on, or null if none.
             */  
            public MyContentObserver(Handler handler) {  
                super(handler);  
            }  
      
            @Override  
            public void onChange(boolean selfChange) {  
                myAdapter.notifyDataSetChanged();  
      
            }  
        }  


從ContentObserver繼承下來的子類必須要實現onChange函數。當這個ContentObserver子類負責監控的數據發生變化時,ContentService就會調用它的onChange函數來處理,參數selfChange表示這個變化是否是由自己引起的。在這個應用程序中,MyContentObserver繼承了ContentObserver類,它負責監控的URI是"content://com.cj.mycontentprovider/contact".當以這個URI爲前綴的URI對應的數據發生改變時,ContentService都會調用這個MyContentObserver類的onChange函數來處理。在MyContentObserver類的onChange函數中,執行的操作就是重新獲取MyContentProvider中的數據來更新界面上的聯繫人信息列表。

        在MyContentObserver類的構造函數中,有一個參數handler,它的類型爲Handler,它是從MainActivity類的onCreate函數中創建並傳過來的。這個handler是用來分發和處理消息用的。由於MainActivity類的onCreate函數是在應用程序的主線程中被調用的,因此,這個handler參數就是和應用程序主線程的消息循環關聯在一起的。在後面我們分析數據更新通知的發送過程時,便會看到這個handler參數是如何使用的了。

        下面我們就開始分析註冊MyContentObserver來監控MyContentProvider中的數據變化的過程.

第一步.ContentResolver.registerContentObserver()

   在frameworks/base/core/java/android/content/ContentResolver.java文件中:

      public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
                ContentObserver observer)
        {
            registerContentObserver(uri, notifyForDescendents, observer, UserHandle.myUserId());
        }
     
        /** @hide - designated user version */
        public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
                ContentObserver observer, int userHandle)
        {
            try {
                getContentService().registerContentObserver(uri, notifyForDescendents,
                        observer.getContentObserver(), userHandle);
            } catch (RemoteException e) {
            }
        }


當參數notifyForDescendents爲true時,表示要監控所有以uri爲前綴的URI對應的數據變化。上面函數做了三件事情,一是調用getContentService函數來獲得前面已經啓動起來了的ContentService服務,二是調用從參數傳進來的ContentObserver對象observer的getContentObserver函數來獲得一個Binder對象,三是通過調用這個ContentService遠程接口的registerContentObserver函數來把這個Binder對象註冊到ContentService中去。

第二步. ContentResolver.getContentService()

   在frameworks/base/core/java/android/content/ContentResolver.java文件中:

     public static IContentService getContentService() {
            if (sContentService != null) {
                return sContentService;
            }
            IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
           ...
            sContentService = IContentService.Stub.asInterface(b);
           ..
            return sContentService;
        }

 在ContentResolver類中,有一個靜態成員變量sContentService,開始時它的值爲null。當ContentResolver類的getContentService函數第一次被調用時,它便會通過ServiceManager類的getService函數來獲得前面已經啓動起來了的ContentService服務的遠程接口,然後把它保存在sContentService變量中。這樣,當下次ContentResolver類的getContentService函數再次被調用時,就可以直接把這個ContentService遠程接口返回給調用者了。

 第三步. ContentResolver.getContentObserver()

  在frameworks/base/core/java/android/database/ContentObserver.java文件中:

    public abstract class ContentObserver {
        private final Object mLock = new Object();
        private Transport mTransport; // guarded by mLock
        Handler mHandler;
        /**
         * Creates a content observer.
         *
         * @param handler The handler to run {@link #onChange} on, or null if none.
         */
        public ContentObserver(Handler handler) {
            mHandler = handler;
        }
        /**
         * Gets access to the binder transport object. Not for public consumption.
         *
         * {@hide}
         */
        public IContentObserver getContentObserver() {
            synchronized (mLock) {
                if (mTransport == null) {
                    mTransport = new Transport(this);
                }
                return mTransport;
            }
        }
        /**


ContentObserver類的getContentObserver函數返回的是一個成員變量mTransport,它的類型爲ContentObserver的內部類Transport。ContentObserver類的成員變量mTransport是一個Binder對象,它是要傳遞給ContentService服務的,以便當ContentObserver所監控的數據發生變化時,ContentService服務可以通過這個Binder對象通知相應的ContentObserver它監控的數據發生變化了。

 

 第四步. ContentService.registerContentObserver()

   在frameworks/base/core/java/android/content/ContentService.java文件中:

    public void registerContentObserver(Uri uri, boolean notifyForDescendants,
                IContentObserver observer, int userHandle) {
        .....
            synchronized (mRootNode) {
                mRootNode.addObserverLocked(uri, observer, notifyForDescendants, mRootNode,
                        Binder.getCallingUid(), Binder.getCallingPid(), userHandle);
           ...
            }
        }

它調用了ContentService類的成員變量mRootNode的addObserverLocked函數來註冊這個ContentObserver對象observer。成員變量mRootNode的類型爲ContentService在內部定義的一個類ObserverNode。

第五步. ObserverNode.addObserverLocked()

  在frameworks/base/core/java/android/content/ContentService.java文件中:

            public void addObserverLocked(Uri uri, IContentObserver observer,
                    boolean notifyForDescendants, Object observersLock,
                    int uid, int pid, int userHandle) {
                addObserverLocked(uri, 0, observer, notifyForDescendants, observersLock,
                        uid, pid, userHandle);
            }
     
            private void addObserverLocked(Uri uri, int index, IContentObserver observer,
                    boolean notifyForDescendants, Object observersLock,
                    int uid, int pid, int userHandle) {
                // If this is the leaf node add the observer
                if (index == countUriSegments(uri)) {//這裏的index 上一步傳進來的是0
                    mObservers.add(new ObserverEntry(observer, notifyForDescendants, observersLock,
                            uid, pid, userHandle));
                    return;
                }
     
                // Look to see if the proper child already exists
                String segment = getUriSegment(uri, index);
        
                int N = mChildren.size();
                for (int i = 0; i < N; i++) {
                    ObserverNode node = mChildren.get(i);
                    if (node.mName.equals(segment)) {
                        node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
                                observersLock, uid, pid, userHandle);
                        return;
                    }
                }
     
                // No child found, create one
                ObserverNode node = new ObserverNode(segment);
                mChildren.add(node);
                node.addObserverLocked(uri, index + 1, observer, notifyForDescendants,
                        observersLock, uid, pid, userHandle);
            }


 從這裏我們就可以看出,註冊到ContentService中的ContentObserver按照樹形來組織,樹的節點類型爲ObserverNode,而樹的根節點就爲ContentService類的成員變量mRootNode。
private final ObserverNode mRootNode = new ObserverNode("");
每一個ObserverNode節點都對應一個名字,它是從URI中解析出來的。

在我們這個情景中,傳進來的uri爲"content://com.cj.mycontentprovider/contact",從第五步第一個用mRootNode的addObserverLocked()函數來往樹上增加一個ObserverNode節點時,傳進來的參數index的值爲0,而調用countUriSegments("content://com.cj.mycontentprovider/contact")函數的返回值爲2,不等於index的值.這裏看一下countUriSegments()函數:

     private int countUriSegments(Uri uri) {
                if (uri == null) {
                    return 0;
                }
                return uri.getPathSegments().size() + 1;
            }

--getPathSegments得到uri的path部分,並拆分,去掉"/",取到第一個元素(從第0個開始)。這裏只有contact一個元素  .   

因此就會往下執行,而通過調用getUriSegment("content://com.cj.mycontentprovider/contact", 0)函數得到的返回值爲"com.cj.mycontentprovider"。

    private String getUriSegment(Uri uri, int index) {
                if (uri != null) {
                    if (index == 0) {
                        return uri.getAuthority();
                    } else {
                        return uri.getPathSegments().get(index - 1);
                    }
                } else {
                    return null;
                }
            }

假設這裏是第一次調用樹的根節點mRootNode來增加"content://com.cj.mycontentprovider/contact"這個URI,那麼在接下來的for循環中,就不會在mRootNode的孩子節點列表mChildren中找到與名稱"com.cj.mycontentprovider"對應的ObserverNode,於是就會以"com.cj.mycontentprovider"爲名稱來創建一個新的ObserverNode,並增加到mRootNode的孩子節點列表mChildren中去,並以這個新的ObserverNode來開始新一輪的addObserverLocked()函數調用。

  第二次進入到addObserverLocked()函數時,countUriSegments("content://com.cj.mycontentprovider/contact")的值仍爲2(參考前面函數),而index的值爲1,因此就會往下執行,這時候通過調用getUriSegment("content://shy.luo.providers.articles/item", 1)函數得到的返回值爲"contact"。這裏不存在名稱爲"contact"的孩子節點,於是又會以"contact"爲名稱來創建一個新的ObserverNode,並以這個新的ObserverNode來開始新一輪的addObserverLocked函數調用。

     第三次進入到addObserverLocked()函數時,countUriSegments("content://com.cj.mycontentprovider/contact")的值仍爲2,而index的值也爲2,因此就會新建一個ObserverEntry對象,並保存在這個以"contact"爲名稱的ObserverNode的ContentObserver列表mObervers中。

        最終我們得到的樹形結構如下所示:

        mRootNode("")

            -- ObserverNode("com.cj.mycontentprovider")

                --ObserverNode("contact") , which has a ContentObserver in mObservers  
      這樣,ContentObserver的註冊過程就完成了。

 2. 數據更新通知的發送過程
在第二個應用程序中,在添加聯繫人的界面田添加一個聯繫人時:

    ContentValues contentValues = new ContentValues();  
                contentValues.put("name",name);  
                contentValues.put("number",num);  
                getContentResolver().insert(Uri.parse("content://com.cj.mycontentprovider/contact"),contentValues);  

便會進入到第一個應用程序的MyContentProvider類的insert函數中:

     public Uri insert(Uri uri, ContentValues values) {  
            Uri u = null;  
            if(uriMatcher.match(uri) == CONTACT){  
                SQLiteDatabase database = dbHelp.getWritableDatabase();  
      
                long d = database.insert("contact", "_id", values);  
                u = ContentUris.withAppendedId(uri,d);  
                contentResolver.notifyChange(u,null);  
            }  
            return u;  
      
        }  


 從上面傳來的參數uri的值爲"content://com.cj.mycontentprovider/contact"。假設當這個函數把數據成功增加到SQLite數據庫之後,返回來的id值爲d,於是通過調用ContentUris.withAppendedId(uri,d);得到的newUri的值就爲"content://com.cj.mycontentprovider/contact/d"。這時候就會調用下面語句來通知那些註冊了監控"content://com.cj.mycontentprovider/contact/d"這個URI的ContentObserver,它監控的數據發生變化了:
contentResolver.notifyChange(u,null);  
下面就根據源碼來分析這個數據變化通知的發送過程:
第一步:ContentResolver.notifyChange()
  在frameworks/base/core/java/android/content/ContentResolver.java文件中:

    public void notifyChange(Uri uri, ContentObserver observer) {
            notifyChange(uri, observer, true /* sync to network */);
        }
        public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
            notifyChange(uri, observer, syncToNetwork, UserHandle.getCallingUserId());
        }
     
        /**
         * Notify registered observers within the designated user(s) that a row was updated.
         *
         * @hide
         */
        public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork,
                int userHandle) {
            try {
                getContentService().notifyChange(
                        uri, observer == null ? null : observer.getContentObserver(),
                        observer != null && observer.deliverSelfNotifications(), syncToNetwork,
                        userHandle);
            } catch (RemoteException e) {
            }
        }


 這裏調用了ContentService的遠接程口來調用它的notifyChange()函數來發送數據更新通知。

第二步: ContentService.notifyChange()

  在frameworks/base/core/java/android/content/ContentService.java文件中

    public void notifyChange(Uri uri, IContentObserver observer,
                boolean observerWantsSelfNotifications, boolean syncToNetwork,
                int userHandle) {
           ...
            // This makes it so that future permission checks will be in the context of this
            // process rather than the caller's process. We will restore this before returning.
          
            try {
                ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
                synchronized (mRootNode) {
                    mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
                            userHandle, calls);
                }
                final int numCalls = calls.size();
                for (int i=0; i<numCalls; i++) {
                    ObserverCall oc = calls.get(i);
                    try {
                        oc.mObserver.onChange(oc.mSelfChange, uri);
                    ..
                    } catch (RemoteException ex) {
                     ..
                    }
                }
                if (syncToNetwork) {
                    SyncManager syncManager = getSyncManager();
                    if (syncManager != null) {
                        syncManager.scheduleLocalSync(null /* all accounts */, callingUserHandle,
                                uri.getAuthority());
                    }
                }
            } finally {
                restoreCallingIdentity(identityToken);
            }
        }


這個函數主要做了兩件事情,第一件事情是調用ContentService的成員變量mRootNode的collectObserverLocked()函數來收集那些註冊了監控"content://com.cj.mycontentprovider/contact/d"這個URI的ContentObserver,第二件事情是分別調用了這些ContentObserver的onChange()函數來通知它們監控的數據發生變化了。

第三步:  ObserverNode.collectObserversLocked()

 在frameworks/base/core/java/android/content/ContentService.java文件中:

    public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
                    boolean observerWantsSelfNotifications, int targetUserHandle,
                    ArrayList<ObserverCall> calls) {
                String segment = null;
                int segmentCount = countUriSegments(uri);
                if (index >= segmentCount) {
                    // This is the leaf node, notify all observers
                    collectMyObserversLocked(true, observer, observerWantsSelfNotifications,
                            targetUserHandle, calls);
                } else if (index < segmentCount){
                    segment = getUriSegment(uri, index);
                    // Notify any observers at this level who are interested in descendants
                    collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
                            targetUserHandle, calls);
                }
     
                int N = mChildren.size();
                for (int i = 0; i < N; i++) {
                    ObserverNode node = mChildren.get(i);
                    if (segment == null || node.mName.equals(segment)) {
                        // We found the child,
                        node.collectObserversLocked(uri, index + 1,
                                observer, observerWantsSelfNotifications, targetUserHandle, calls);
                        if (segment != null) {
                            break;
                        }
                    }
                }
            }


    private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
                    boolean observerWantsSelfNotifications, int targetUserHandle,
                    ArrayList<ObserverCall> calls) {
                int N = mObservers.size();
                IBinder observerBinder = observer == null ? null : observer.asBinder();
                for (int i = 0; i < N; i++) {
                    ObserverEntry entry = mObservers.get(i);
     
                    // Don't notify the observer if it sent the notification and isn't interested
                    // in self notifications
                    boolean selfChange = (entry.observer.asBinder() == observerBinder);
                    if (selfChange && !observerWantsSelfNotifications) {
                        continue;
                    }
     
                    // Does this observer match the target user?
                    if (targetUserHandle == UserHandle.USER_ALL
                            || entry.userHandle == UserHandle.USER_ALL
                            || targetUserHandle == entry.userHandle) {
                        // Make sure the observer is interested in the notification
                        if (leaf || (!leaf && entry.notifyForDescendants)) {
                            calls.add(new ObserverCall(this, entry.observer, selfChange));
                        }
                    }
                }
            }

   第一次調用collectObserversLocked()時,是在mRootNode的這個ObserverNode節點中進行收集ContentObserver的。這時候傳進來的uri的值爲"content://com.cj.mycontentprovider/contact/d",index的值爲0。調用countUriSegments("content://shy.luo.providers.articles/item/n")函數得到的返回值爲3(參考前面就知道是3   2+1=3),於是就會調用下面語句:

    segment = getUriSegment(uri, 0);
                    // Notify any observers at this level who are interested in descendants
                    collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
                            targetUserHandle, calls);


 這裏得到的segment爲"com.cj.mycontentprovider"。在我們這個情景中,mRootNode這個節點中沒有註冊ContentObserver,於是調用collectMyObserversLocked()函數就不會收集到ContentObserver。

   在接下來的for循環中,在mRootNode的孩子節點列表mChildren中查找名稱等於"com.cj.mycontentprovider"的OberverNode節點。在上面分析ContentObserver的註冊過程時,我們已經往mRootNode的孩子節點列表mChildren中增加了一個名稱爲"com.cj.mycontentprovider"的OberverNode節點,因此,這裏會成功找到它,並且調用它的collectObserversLocked()函數來繼續收集ContentObserver。

     第二次進入到collectObserversLocked()函數時,是在名稱爲"com.cj.mycontentprovider"的OberverNode節點中收集ContentObserver的。這時候傳來的uri值不變,但是index的值爲1,於是執行下面語句:

    segment = getUriSegment(uri, 1);
                    // Notify any observers at this level who are interested in descendants
                    collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
                            targetUserHandle, calls);

</pre><pre name="code" class="java" style="font-size: 14px;">

這裏得到的segment爲"contact"。在我們這個情景中,我們沒有在名稱爲"com.cj.mycontentprovider"的OberverNode節點中註冊有ContentObserver,因此這裏調用collectMyObserversLocked()函數也不會收集到ContentObserver。

    在接下來的for循環中,在名稱爲"com.cj.mycontentprovider"的ObserverNode節點的孩子節點列表mChildren中查找名稱等於"contact"的OberverNode節點。在上面分析ContentObserver的註冊過程時,我們已經往名稱爲"com.cj.mycontentprovider"的ObserverNode節點的孩子節點列表mChildren中增加了一個名稱爲"contact"的OberverNode節點,因此,這裏會成功找到它,並且調用它的collectObserversLocked()函數來繼續收集ContentObserver。

        第三次進入到collectObserversLocked()函數時,是在名稱爲"com.cj.mycontentprovider"的OberverNode節點的子節點中名稱爲"contact"的ObserverNode節點中收集ContentObserver的。這時候傳來的uri值不變,但是index的值爲2,於是執行下面語句:

    segment = getUriSegment(uri, 2);
                    // Notify any observers at this level who are interested in descendants
                    collectMyObserversLocked(false, observer, observerWantsSelfNotifications,
                            targetUserHandle, calls);


這裏得到的segment爲"d"。前面我們已經在名稱爲"com.cj.mycontentprovider"的OberverNode節點的子節點中名稱爲"contact"的ObserverNode節點中註冊了一個ContentObserver,即MyContentObserver,因此這裏調用collectMyObserversLocked()函數會收集到這個ContentObserver。注意,這次調用collectMyObserversLocked()函數時,雖然傳進去的參數leaf爲false,但是由於我們註冊MyContentObserver時,指定了notifyForDescendents參數爲true,因此,這裏可以把它收集回來。
在接下來的for循環中,繼續在該節點的子節點列表mChildren中查找名稱等於"d"的OberverNode節點。在我們這個情景中,不存在這個名稱爲"d"的子節點了,於是收集ContentObserver的工作就結束了,收集結果是隻有一個ContentObserver,即我們在前面註冊的MyContentObserver。

  返回到第二步中,調用下面語句來通知相應的ContentObserver,它們監控的數據發生變化了:

     for (int i=0; i<numCalls; i++) {
                    ObserverCall oc = calls.get(i);
                    try {
                        oc.mObserver.onChange(oc.mSelfChange, uri);
                    } catch (RemoteException ex) {
                     
                    }
                }


 前面我們在分析ContentObserver的註冊過程的第三步時,介紹到註冊到ContentService服務中的mObserver是一個在ContentObserver內部定義的一個類Transport的對象的遠程接口,於是這裏調用這個接口的onChange()函數時,就會進入到ContentObserver的內部類Transport的onChange()函數中去。

第四步:  Transport.onChange()

在frameworks/base/core/java/android/database/ContentObserver.java文件中:

     private static final class Transport extends IContentObserver.Stub {
            private ContentObserver mContentObserver;
     
            public Transport(ContentObserver contentObserver) {
                mContentObserver = contentObserver;
            }
     
            @Override
            public void onChange(boolean selfChange, Uri uri) {
                ContentObserver contentObserver = mContentObserver;
                if (contentObserver != null) {
                    contentObserver.dispatchChange(selfChange, uri);
                }
            }
     
            public void releaseContentObserver() {
                mContentObserver = null;
            }
        }


    前面我們在分析ContentObserver的註冊過程的第三步時,把MyContentObserver這個ContentObserver保存在了這個Transport對象的mContentObserver成員變量中,因此,會調用它的dispatchChange()函數來執行數據更新通知的操作。

第五步:. ContentObserver.dispatchChange()

 在frameworks/base/core/java/android/database/ContentObserver.java文件中:

    public final void dispatchChange(boolean selfChange, Uri uri) {
            if (mHandler == null) {
                onChange(selfChange, uri);
            } else {
                mHandler.post(new NotificationRunnable(selfChange, uri));
            }
        }


在前面分析MyContentObserver的註冊過程時,我們以第二個應用程序的主線程的消息循環創建了一個Handler,並且以這個Handler來創建了這個MyContentObserver,這個Handler就保存在MyContentObserver的父類ContentObserver的成員變量mHandler中。因此,這裏的mHandler不爲null,於是把這個數據更新通知封裝成了一個消息,放到第二個應用程序的主線程中去處理,最終這個消息是由NotificationRunnable類的run()函數來處理的。

第六步:. NotificationRunnable.run()

在frameworks/base/core/java/android/database/ContentObserver.java文件中:

    private final class NotificationRunnable implements Runnable {
            private final boolean mSelfChange;
            private final Uri mUri;
     
            public NotificationRunnable(boolean selfChange, Uri uri) {
                mSelfChange = selfChange;
                mUri = uri;
            }
     
            @Override
            public void run() {
                ContentObserver.this.onChange(mSelfChange, mUri);
            }
        }

這個函數就直接調用ContentObserver的子類的onChange函數來處理這個數據更新通知了。在我們這個情景中,這個ContentObserver子類便是MyContentObserver了。
這裏它要執行的操作便是更新界面上的ListView列表中的聯繫人信息了。
以上就是ContentProvider的共享數據更新通知機制的運行過程.

 

 

 

 

 

 

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章