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文件中:
这个函数定义在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函数中去。
本文转自 Luoshengyang 51CTO博客,原文链接:http://blog.51cto.com/shyluo/967021,如需转载请自行联系原作者