<rt id="bn8ez"></rt>
<label id="bn8ez"></label>

  • <span id="bn8ez"></span>

    <label id="bn8ez"><meter id="bn8ez"></meter></label>

    posts - 101,  comments - 29,  trackbacks - 0

            在Android系統中,應用程序組件Content Provider為不同的應用程序實現數據共享提供了基礎設施,它主要通過Binder進程間通信機制和匿名共享內存機制來實現的。關于數據共享的另一個話題便是數據更新通知機制了,即如果一個應用程序對共享數據做了修改,它應該如何通知其它正在使用這些共享數據的應用程序呢?本文將分析Content Provider的共享數據更新通知機制,為讀者解答這個問題。

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

            與分析Android系統的廣播機制類似,我們把Content Provider的數據更新機制劃分為三個單元進行分析,第一個單元是ContentService的啟動過程,第二個單元是監控數據變化的ContentObserver的注冊過程,第二個單元是數據更新通知的發送過程。

            與前面兩篇文章Android應用程序組件Content Provider的啟動過程源代碼分析Android應用程序組件Content Provider在應用程序之間共享數據的原理分析一樣,本文仍然以Android應用程序組件Content Provider應用實例這篇文章介紹的應用程序為例來分析Content Provider的數據更新機制。

            1. ContentService的啟動過程分析

            前面提到,在Content Provider的數據更新通知機制中,ContentService扮演者ContentObserver的注冊中心的角色,因此,它必須要系統啟動的時候就啟動起來,以便后面啟動起來的應用程序可以使用它。在前面這篇文章Android系統進程Zygote啟動過程的源代碼分析中,我們提到,Android系統進程Zygote在啟動的時候,在啟動一個System進程來加載系統的一些關鍵服務,而ContentService就這些關鍵服務之一了。在System進程中,負責加載系統關鍵服務的類為SystemServer類,它定義在frameworks/base/services/java/com/android/server/SystemServer.java文件中,它會通過啟動一個線程SystemThread來加載這些關鍵服務:

    class ServerThread extends Thread {
    	......
    
    	@Override
    	public void run() {
    		......
    
    		Looper.prepare();
    
    		// Critical services...
    		try {
    			......
    
    			ContentService.main(context,
    				factoryTest == SystemServer.FACTORY_TEST_LOW_LEVEL);
    
    			......
    
    		}catch (RuntimeException e) {
    			......
    		}
    
    		......
    
    		Looper.loop();
    		......
    	}
    }
            ContentService類定義在frameworks/base/core/java/android/content/ContentService.java文件中,它的main函數的實現如下所示:

    public final class ContentService extends IContentService.Stub {
    	......
    
    	public static IContentService main(Context context, boolean factoryTest) {
    		ContentService service = new ContentService(context, factoryTest);
    		ServiceManager.addService(ContentResolver.CONTENT_SERVICE_NAME, service);
    		return service;
    	}
    
    	......
    }

     

            從這里我們就可以看到,在ContentService類的main函數中,會創建一個ContentService實例,然后把它添加到ServiceManager中去,這樣,ContentService服務就啟動起來了,  其它地方可以通過ServiceManager來獲得它的一個遠程接口來使用它提供的服務。

            2. ContentObserver的注冊過程分析

            在前面這篇文章Android應用程序組件Content Provider應用實例介紹的應用程序Acticle中,主窗口MainActivity在創建的時候,會調用應用程序上下文的ContentResolver接口來注冊一個自定義的ContentObserver來監控ArticlesProvider這個Content Provider中的數據變化:

     

    public class MainActivity extends Activity implements View.OnClickListener, AdapterView.OnItemClickListener {
    	......
    
    	private ArticleAdapter adapter = null;
    	private ArticleObserver observer = null;
    
    	......
    
    	@Override
    	public void onCreate(Bundle savedInstanceState) {
    		super.onCreate(savedInstanceState);
    		......
    
    		observer = new ArticleObserver(new Handler());
    		getContentResolver().registerContentObserver(Articles.CONTENT_URI, true, observer);
    
    		......
    	}
    
    	private class ArticleObserver extends ContentObserver {
    		public ArticleObserver(Handler handler) {
    			super(handler);
    		}
    
    		@Override
    		public void onChange (boolean selfChange) {
    			adapter.notifyDataSetChanged();
    		}
    	}
    
    	......
    }
            從ContentObserver繼承下來的子類必須要實現onChange函數。當這個ContentObserver子類負責監控的數據發生變化時,ContentService就會調用它的onChange函數來處理,參數selfChange表示這個變化是否是由自己引起的,在我們這個情景中,不需要關注這個參數的值。在這個應用程序中,ArticleObserver繼承了ContentObserver類,它負責監控的URI是Articles.CONTENT_URI,它的值為"content://shy.luo.providers.articles/item",這個值是在這篇文章Android應用程序組件Content Provider應用實例介紹的應用程序ActiclesProvider中的Articles.java文件中定義的。當所有以Articles.CONTENT_URI為前綴的URI對應的數據發生改變時,ContentService都會調用這個ArticleObserver類的onChange函數來處理。在ArticleObserver類的onChange函數中,執行的操作就是重新獲取ActiclesProvider中的數據來更新界面上的文章信息列表。

     

            在ArticleObserver類的構造函數中,有一個參數handler,它的類型為Handler,它是從MainActivity類的onCreate函數中創建并傳過來的。通過前面這篇文章Android應用程序消息處理機制(Looper、Handler)分析的學習,我們知道,這個handler是用來分發和處理消息用的。由于MainActivity類的onCreate函數是在應用程序的主線程中被調用的,因此,這個handler參數就是和應用程序主線程的消息循環關聯在一起的。在后面我們分析數據更新通知的發送過程時,便會看到這個handler參數是如何使用的了。

            下面我們就開始分析注冊ArticleObserver來監控ActiclesProvider中的數據變化的過程,首先來看一下這個過程的時序圖,然后再詳細分析每一個步驟:


            Step 1. ContentResolver.registerContentObserver

            這個函數定義在frameworks/base/core/java/android/content/ContentResolver.java文件中:

     

    public abstract class ContentResolver {
    	......
    
    	public final void registerContentObserver(Uri uri, boolean notifyForDescendents,
    		ContentObserver observer)
    	{
    		try {
    			getContentService().registerContentObserver(uri, notifyForDescendents,
    				observer.getContentObserver());
    		} catch (RemoteException e) {
    		}
    	}
    
    	......
    }
            當參數notifyForDescendents為true時,表示要監控所有以uri為前綴的URI對應的數據變化。這個函數做了三件事情,一是調用getContentService函數來獲得前面已經啟動起來了的ContentService遠程接口,二是調用從參數傳進來的ContentObserver對象observer的getContentObserver函數來獲得一個Binder對象,三是通過調用這個ContentService遠程接口的registerContentObserver函數來把這個Binder對象注冊到ContentService中去。

     

            Step 2.ContentResolver.getContentService

            這個函數定義在frameworks/base/core/java/android/content/ContentResolver.java文件中:

    public abstract class ContentResolver {
    	......
    
    	public static IContentService getContentService() {
    		if (sContentService != null) {
    			return sContentService;
    		}
    		IBinder b = ServiceManager.getService(CONTENT_SERVICE_NAME);
    		......
    		sContentService = IContentService.Stub.asInterface(b);
    		......
    		return sContentService;
    	}
    
    	private static IContentService sContentService;
    	......
    }
            在ContentResolver類中,有一個靜態成員變量sContentService,開始時它的值為null。當ContentResolver類的getContentService函數第一次被調用時,它便會通過ServiceManager類的getService函數來獲得前面已經啟動起來了的ContentService服務的遠程接口,然后把它保存在sContentService變量中。這樣,當下次ContentResolver類的getContentService函數再次被調用時,就可以直接把這個ContentService遠程接口返回給調用者了。

     

            Step 3. ContentObserver.getContentObserver

            這個函數定義在frameworks/base/core/java/android/database/ContentObserver.java文件中:

     

    public abstract class ContentObserver {
    	......
    
    	private Transport mTransport;
    
    	......
    
    	private static final class Transport extends IContentObserver.Stub {
    		ContentObserver mContentObserver;
    
    		public Transport(ContentObserver contentObserver) {
    			mContentObserver = contentObserver;
    		}
    
    		......
    	}
    
    	......
    
    	public IContentObserver getContentObserver() {
    		synchronized(lock) {
    			if (mTransport == null) {
    				mTransport = new Transport(this);
    			}
    			return mTransport;
    		}
    	}
    
    	......
    }
            ContentObserver類的getContentObserver函數返回的是一個成員變量mTransport,它的類型為ContentObserver的內部類Transport。從Transport類的定義我們可以知道,它有一個成員變量mContentObserver,用來保存與對應的ContentObserver對象。同時我們還可以看出,ContentObserver類的成員變量mTransport是一個Binder對象,它是要傳遞給ContentService服務的,以便當ContentObserver所監控的數據發生變化時,ContentService服務可以通過這個Binder對象通知相應的ContentObserver它監控的數據發生變化了。

     

            Step 4. ContentService.registerContentObserver

            這個函數定義在frameworks/base/core/java/android/content/ContentService.java文件中:

     

    public final class ContentService extends IContentService.Stub {
    	......
    
    	private final ObserverNode mRootNode = new ObserverNode("");
    
    	......
    
    	public void registerContentObserver(Uri uri, boolean notifyForDescendents,
    			IContentObserver observer) {
    		......
    
    		synchronized (mRootNode) {
    			mRootNode.addObserverLocked(uri, observer, notifyForDescendents, mRootNode);
    			......
    		}
    	}
    
    	......
    }
            它調用了ContentService類的成員變量mRootNode的addObserverLocked函數來注冊這個ContentObserver對象observer。成員變量mRootNode的類型為ContentService在內部定義的一個類ObserverNode。

     

            Step 5. ObserverNode.addObserverLocked

            這個函數定義在frameworks/base/core/java/android/content/ContentService.java文件中:

     

    public final class ContentService extends IContentService.Stub {
    	......
    
    	public static final class ObserverNode {
    		......
    
    		private String mName;
    		private ArrayList<ObserverNode> mChildren = new ArrayList<ObserverNode>();
    		private ArrayList<ObserverEntry> mObservers = new ArrayList<ObserverEntry>();
    
    		public ObserverNode(String name) {
    			mName = name;
    		}
    
    		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;
    			}
    		}
    
    		private int countUriSegments(Uri uri) {
    			if (uri == null) {
    				return 0;
    			}
    			return uri.getPathSegments().size() + 1;
    		}
    
    		public void addObserverLocked(Uri uri, IContentObserver observer,
    				boolean notifyForDescendents, Object observersLock) {
    			addObserverLocked(uri, 0, observer, notifyForDescendents, observersLock);
    		}
    
    		private void addObserverLocked(Uri uri, int index, IContentObserver observer,
    				boolean notifyForDescendents, Object observersLock) {
    			// If this is the leaf node add the observer
    			if (index == countUriSegments(uri)) {
    				mObservers.add(new ObserverEntry(observer, notifyForDescendents, observersLock));
    				return;
    			}
    
    			// Look to see if the proper child already exists
    			String segment = getUriSegment(uri, index);
    			if (segment == null) {
    				throw new IllegalArgumentException("Invalid Uri (" + uri + ") used for observer");
    			}
    			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, notifyForDescendents, observersLock);
    					return;
    				}
    			}
    
    			// No child found, create one
    			ObserverNode node = new ObserverNode(segment);
    			mChildren.add(node);
    			node.addObserverLocked(uri, index + 1, observer, notifyForDescendents, observersLock);
    		}
    
    		......
    	}
    
    	......
    }
            從這里我們就可以看出,注冊到ContentService中的ContentObserver按照樹形來組織,樹的節點類型為ObserverNode,而樹的根節點就為ContentService類的成員變量mRootNode。每一個ObserverNode節點都對應一個名字,它是從URI中解析出來的。

     

            在我們這個情景中,傳進來的uri為"content://shy.luo.providers.articles/item",從Step 3調用mRootNode的addObserverLocked函數來往樹上增加一個ObserverNode節點時,傳進來的參數index的值為0,而調用countUriSegments("content://shy.luo.providers.articles/item")函數的返回值為2,不等于index的值,因此就會往下執行,而通過調用getUriSegment("content://shy.luo.providers.articles/item", 0)函數得到的返回值為"shy.luo.providers.articles"。假設這里是第一次調用樹的根節點mRootNode來增加"content://shy.luo.providers.articles/item"這個URI,那么在接下來的for循環中,就不會在mRootNode的孩子節點列表mChildren中找到與名稱"shy.luo.providers.articles"對應的ObserverNode,于是就會以"shy.luo.providers.articles"為名稱來創建一個新的ObserverNode,并增加到mRootNode的孩子節點列表mChildren中去,并以這個新的ObserverNode來開始新一輪的addObserverLocked函數調用。

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

            第三次進入到addObserverLocked函數時,countUriSegments("content://shy.luo.providers.articles/item")的值仍為2,而index的值也為2,因此就會新建一個ObserverEntry對象,并保存在這個以"item"為名稱的ObserverNode的ContentObserver列表mObervers中。

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

            mRootNode("")

                -- ObserverNode("shy.luo.providers.articles")

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

           3. 數據更新通知的發送過程

           在前面這篇文章Android應用程序組件Content Provider應用實例介紹的應用程序Acticle中,當調用ArticlesAdapter類的insertArticle往ArticlesProvider中增加一個文章信息條目時:

     

    public class ArticlesAdapter {
    	......
    
    	public long insertArticle(Article article) {
    		ContentValues values = new ContentValues();
    		values.put(Articles.TITLE, article.getTitle());
    		values.put(Articles.ABSTRACT, article.getAbstract());
    		values.put(Articles.URL, article.getUrl());
    
    		Uri uri = resolver.insert(Articles.CONTENT_URI, values);
    		String itemId = uri.getPathSegments().get(1);
    
    		return Integer.valueOf(itemId).longValue();
    	}
    
    	......
    }
            便會進入到應用程序ArticlesProvider中的ArticlesProvider類的insert函數中:

     

     

    public class ArticlesProvider extends ContentProvider {
    	......
    
    	@Override
    	public Uri insert(Uri uri, ContentValues values) {
    		if(uriMatcher.match(uri) != Articles.ITEM) {
    			throw new IllegalArgumentException("Error Uri: " + uri);
    		}
    
    		SQLiteDatabase db = dbHelper.getWritableDatabase();
    
    		long id = db.insert(DB_TABLE, Articles.ID, values);
    		if(id < 0) {
    			throw new SQLiteException("Unable to insert " + values + " for " + uri);
    		}
    
    		Uri newUri = ContentUris.withAppendedId(uri, id);
    		resolver.notifyChange(newUri, null);
    
    		return newUri;
    	}
    
    	......
    }
            從上面傳來的參數uri的值為"content://shy.luo.providers.articles/item"。假設當這個函數把數據成功增加到SQLite數據庫之后,返回來的id值為n,于是通過調用ContentUris.withAppendedId("content://shy.luo.providers.articles/item", n)得到的newUri的值就為"content://shy.luo.providers.articles/item/n"。這時候就會調用下面語句來通知那些注冊了監控"content://shy.luo.providers.articles/item/n"這個URI的ContentObserver,它監控的數據發生變化了:

     

     

    resolver.notifyChange(newUri, null);
            下面我們就開始分析這個數據變化通知的發送過程,首先來看一下這個過程的時序圖,然后再詳細分析每一個步驟:

     


            Step 1. ContentResolver.notifyChange
            這個函數定義在frameworks/base/core/java/android/content/ContentResolver.java文件中:

     

    public abstract class ContentResolver {
    	......
    
    	public void notifyChange(Uri uri, ContentObserver observer) {
    		notifyChange(uri, observer, true /* sync to network */);
    	}
    
    	public void notifyChange(Uri uri, ContentObserver observer, boolean syncToNetwork) {
    		try {
    			getContentService().notifyChange(
    				uri, observer == null ? null : observer.getContentObserver(),
    				observer != null && observer.deliverSelfNotifications(), syncToNetwork);
    		} catch (RemoteException e) {
    		}
    	}
    
    	......
    }
            這里調用了ContentService的遠接程口來調用它的notifyChange函數來發送數據更新通知。

     

            Step 2. ContentService.notifyChange

            這個函數定義在frameworks/base/core/java/android/content/ContentService.java文件中:

     

    public final class ContentService extends IContentService.Stub {
    	......
    
    	public void notifyChange(Uri uri, IContentObserver observer,
    			boolean observerWantsSelfNotifications, boolean syncToNetwork) {
    		......
    
    		try {
    			ArrayList<ObserverCall> calls = new ArrayList<ObserverCall>();
    			synchronized (mRootNode) {
    				mRootNode.collectObserversLocked(uri, 0, observer, observerWantsSelfNotifications,
    					calls);
    			}
    			final int numCalls = calls.size();
    			for (int i=0; i<numCalls; i++) {
    				ObserverCall oc = calls.get(i);
    				try {
    					oc.mObserver.onChange(oc.mSelfNotify);
    					......
    				} catch (RemoteException ex) {
    					......
    				}
    			}
    			......
    		} finally {
    			......
    		}
    	}
    
    	......
    }
            這個函數主要做了兩件事情,第一件事情是調用ContentService的成員變量mRootNode的collectObserverLocked函數來收集那些注冊了監控"content://shy.luo.providers.articles/item/n"這個URI的ContentObserver,第二件事情是分別調用了這些ContentObserver的onChange函數來通知它們監控的數據發生變化了。

     

            Step 3. ObserverNode.collectObserversLocked

            這個函數定義在frameworks/base/core/java/android/content/ContentService.java文件中:

     

    public final class ContentService extends IContentService.Stub {
    	......
    
    	public static final class ObserverNode {
    		......
    
    		private void collectMyObserversLocked(boolean leaf, IContentObserver observer,
    				boolean selfNotify, 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 interesed
    				// in self notifications
    				if (entry.observer.asBinder() == observerBinder && !selfNotify) {
    					continue;
    				}
    
    				// Make sure the observer is interested in the notification
    				if (leaf || (!leaf && entry.notifyForDescendents)) {
    					calls.add(new ObserverCall(this, entry.observer, selfNotify));
    				}
    			}
    		}
    
    		public void collectObserversLocked(Uri uri, int index, IContentObserver observer,
    				boolean selfNotify, ArrayList<ObserverCall> calls) {
    			String segment = null;
    			int segmentCount = countUriSegments(uri);
    			if (index >= segmentCount) {
    				// This is the leaf node, notify all observers
    				collectMyObserversLocked(true, observer, selfNotify, calls);
    			} else if (index < segmentCount){
    				segment = getUriSegment(uri, index);
    				// Notify any observers at this level who are interested in descendents
    				collectMyObserversLocked(false, observer, selfNotify, 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, selfNotify, calls);
    					if (segment != null) {
    						break;
    					}
    				}
    			}
    		}
    	}
    }
            第一次調用collectObserversLocked時,是在mRootNode的這個ObserverNode節點中進行收集ContentObserver的。這時候傳進來的uri的值為"content://shy.luo.providers.articles/item/n",index的值為0。調用countUriSegments("content://shy.luo.providers.articles/item/n")函數得到的返回值為3,于是就會調用下面語句:

     

     

    segment = getUriSegment("content://shy.luo.providers.articles/item/n",0);
    // Notify any observers at this level who are interested in descendents
    collectMyObserversLocked(false, observer, selfNotify, calls);
            這里得到的segment為"shy.luo.providers.articles"。在我們這個情景中,假設mRootNode這個節點中沒有注冊ContentObserver,于是調用collectMyObserversLocked函數就不會收集到ContentObserver。

     

            在接下來的for循環中,在mRootNode的孩子節點列表mChildren中查找名稱等于"shy.luo.providers.articles"的OberverNode節點。在上面分析ContentObserver的注冊過程時,我們已經往mRootNode的孩子節點列表mChildren中增加了一個名稱為"shy.luo.providers.articles"的OberverNode節點,因此,這里會成功找到它,并且調用它的collectObserversLocked函數來繼續收集ContentObserver。

            第二次進入到collectObserversLocked函數時,是在名稱為"shy.luo.providers.articles"的OberverNode節點中收集ContentObserver的。這時候傳來的uri值不變,但是index的值為1,于是執行下面語句:

     

    segment = getUriSegment("content://shy.luo.providers.articles/item/n",1);
    // Notify any observers at this level who are interested in descendents
    collectMyObserversLocked(false, observer, selfNotify, calls);
            這里得到的segment為"item"。在我們這個情景中,我們沒有在名稱為"shy.luo.providers.articles"的OberverNode節點中注冊有ContentObserver,因此這里調用collectMyObserversLocked函數也不會收集到ContentObserver。

     

            在接下來的for循環中,在名稱為"shy.luo.providers.articles"的ObserverNode節點的孩子節點列表mChildren中查找名稱等于"item"的OberverNode節點。在上面分析ContentObserver的注冊過程時,我們已經往名稱為"shy.luo.providers.articles"的ObserverNode節點的孩子節點列表mChildren中增加了一個名稱為"item"的OberverNode節點,因此,這里會成功找到它,并且調用它的collectObserversLocked函數來繼續收集ContentObserver。

            第三次進入到collectObserversLocked函數時,是在名稱為"shy.luo.providers.articles"的OberverNode節點的子節點中名稱為"item"的ObserverNode節點中收集ContentObserver的。這時候傳來的uri值不變,但是index的值為2,于是執行下面語句:

     

    segment = getUriSegment("content://shy.luo.providers.articles/item/n",2);
    // Notify any observers at this level who are interested in descendents
    collectMyObserversLocked(false, observer, selfNotify, calls);
            這里得到的segment為"n"。前面我們已經在名稱為"shy.luo.providers.articles"的OberverNode節點的子節點中名稱為"item"的ObserverNode節點中注冊了一個ContentObserver,即ArticlesObserver,因此這里調用collectMyObserversLocked函數會收集到這個ContentObserver。注意,這次調用collectMyObserversLocked函數時,雖然傳進去的參數leaf為false,但是由于我們注冊ArticlesObserver時,指定了notifyForDescendents參數為true,因此,這里可以把它收集回來。

     

            在接下來的for循環中,繼續在該節點的子節點列表mChildren中查找名稱等于"n"的OberverNode節點。在我們這個情景中,不存在這個名稱為"n"的子節點了,于是收集ContentObserver的工作就結束了,收集結果是只有一個ContentObserver,即我們在前面注冊的ArticlesObserver。

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

     

    for (int i=0; i<numCalls; i++) {
    	ObserverCall oc = calls.get(i);
    	try {
    		oc.mObserver.onChange(oc.mSelfNotify);
    		......
    	} catch (RemoteException ex) {
    		......
    	}
    }
            前面我們在分析ContentObserver的注冊過程的Step 3時,介紹到注冊到ContentService服務中的ContentObserver是一個在ContentObserver內部定義的一個類Transport的對象的遠程接口,于是這里調用這個接口的onChange函數時,就會進入到ContentObserver的內部類Transport的onChange函數中去。

     

            Step 4. Transport.onChange

            這個函數定義在frameworks/base/core/java/android/database/ContentObserver.java文件中:

     

    public abstract class ContentObserver {
    	......
    
    	private static final class Transport extends IContentObserver.Stub {
    		ContentObserver mContentObserver;
    	
    		......
    
    		public void onChange(boolean selfChange) {
    			ContentObserver contentObserver = mContentObserver;
    			if (contentObserver != null) {
    				contentObserver.dispatchChange(selfChange);
    			}
    		}
    
    		......
    	}
    
    	......
    }
            前面我們在分析ContentObserver的注冊過程的Step 3時,把ArticlesObserver這個ContentObserver保存在了這個Transport對象的mContentObserver成員變量中,因此,會調用它的dispatchChange函數來執行數據更新通知的操作。

     

            Step 5. ContentObserver.dispatchChange

            這個函數定義在frameworks/base/core/java/android/database/ContentObserver.java文件中:

     

    public abstract class ContentObserver {
    	......
    
    	public final void dispatchChange(boolean selfChange) {
    		if (mHandler == null) {
    			onChange(selfChange);
    		} else {
    			mHandler.post(new NotificationRunnable(selfChange));
    		}
    	}
    }
            在前面分析ArticlesObserver的注冊過程時,我們以應用程序Article的主線程的消息循環創建了一個Handler,并且以這個Handler來創建了這個ArticlesObserver,這個Handler就保存在ArticlesObserver的父類ContentObserver的成員變量mHandler中。因此,這里的mHandler不為null,于是把這個數據更新通知封裝成了一個消息,放到應用程序Article的主線程中去處理,最終這個消息是由NotificationRunnable類的run函數來處理的。

     

            Step 6. NotificationRunnable.run

            這個函數定義在frameworks/base/core/java/android/database/ContentObserver.java文件中:

     

    public abstract class ContentObserver {
    	......
    
    	private final class NotificationRunnable implements Runnable {
    		private boolean mSelf;
    
    		public NotificationRunnable(boolean self) {
    			mSelf = self;
    		}
    
    		public void run() {
    			ContentObserver.this.onChange(mSelf);
    		}
    	}
    
    	......
    }
            這個函數就直接調用ContentObserver的子類的onChange函數來處理這個數據更新通知了。在我們這個情景中,這個ContentObserver子類便是ArticlesObserver了。

     

            Step 7. ArticlesObserver.onChange

            這個函數定義在前面一篇文章Android應用程序組件Content Provider應用實例介紹的應用程序Artilce源代碼工程目錄下,在文件為packages/experimental/Article/src/shy/luo/article/MainActivity.java中:

    public class MainActivity extends Activity implements View.OnClickListener, AdapterView.OnItemClickListener {
    	......
    
    	private class ArticleObserver extends ContentObserver {
    		......
    
    		@Override
    		public void onChange (boolean selfChange) {
    			adapter.notifyDataSetChanged();
    		}
    	}
    
    	......
    }
           這里它要執行的操作便是更新界面上的ListView列表中的文章信息了,以便反映ArticlesProvider中的最新數據。

     

         這樣,Android應用程序組件Content Provider的共享數據更新通知機制就分析完了,整個Android應用程序組件Content Provider的學習也結束了,重新學習請回到Android應用程序組件Content Provider簡要介紹和學習計劃一文。

    作者:Luoshengyang 發表于2011-12-12 0:58:37 原文鏈接
    閱讀:3351 評論:24 查看評論
    posted on 2012-04-17 21:32 mixer-a 閱讀(923) 評論(0)  編輯  收藏

    只有注冊用戶登錄后才能發表評論。


    網站導航:
     
    主站蜘蛛池模板: 亚洲精品视频免费在线观看| 亚洲国产一区二区a毛片| 亚洲娇小性xxxx色| 四虎在线最新永久免费| 亚洲美女视频一区二区三区| 99久久免费中文字幕精品| 亚洲最大福利视频网站| 欧洲精品99毛片免费高清观看| 久久久亚洲精品视频| 91精品视频在线免费观看| 亚洲第一页在线视频| 一个人免费高清在线观看| 亚洲国产精品ⅴa在线观看| 国产一精品一aⅴ一免费| 一级a性色生活片久久无少妇一级婬片免费放 | 日本特黄a级高清免费大片| 欧洲亚洲国产精华液| 免费人成在线观看网站品爱网日本| 黄色大片免费网站| 精品国产亚洲一区二区在线观看| 一级看片免费视频囗交| 亚洲av无码成人黄网站在线观看| 国产精品免费大片| 亚洲不卡视频在线观看| 日韩视频在线免费| yellow免费网站| 亚洲AV无码一区二区乱子伦| 成人免费在线看片| 国产亚洲高清在线精品不卡 | 亚洲日本一区二区三区在线| 一区二区三区观看免费中文视频在线播放 | 亚洲人成电影亚洲人成9999网| 美女被免费喷白浆视频| 香港一级毛片免费看| 欧洲亚洲国产清在高| 黄色片在线免费观看| 又粗又长又爽又长黄免费视频| 亚洲国产精品国自产拍AV| 免费不卡视频一卡二卡| 色多多A级毛片免费看| 亚洲尹人九九大色香蕉网站|