ContentService完成数据内容注册和更新的机制。在N平台上SystemServer通过SystemServiceManager.startService的方式拉起ContentService,并且注册到ServiceManager中。
从图中可以看出ContentService完成注册和更新机制的原理:当客户端注册的时候会将ContentObserver注册到ContentService中,但是ContentObserver本身是没有Binder通讯能力的,因此ContentObserver把这个工作丢给Transport类,这个类实现Binder通讯接口IContentObserver。ContentService保存ContentObserver是采取树的形式来保存的,ContentService保存树的根节点mRootNode,每个节点保存孩子节点mChidren和监听节点mObservers,监听节点的类型是ObserverEntry,里面有成员指向客户端Binder IContentObserver,通过这个可以完成和客户端的交互。
核心API接口
public final void registerContentObserver(Uri uri, boolean notifyForDescendents, ContentObserver observer)
构造方法 public void ContentObserver(Handler handler)
void onChange(boolean selfChange)
补充:注册树的维护和查找,利用树的特性,采用递归操作。
/**
* Hide this class since it is not part of api,
* but current unittest framework requires it to be public
* @hide
*/
public static final class ObserverNode {
private class ObserverEntry implements IBinder.DeathRecipient {
public final IContentObserver observer;
public final int uid;
public final int pid;
public final boolean notifyForDescendants;
private final int userHandle;
private final Object observersLock;
public ObserverEntry(IContentObserver o, boolean n, Object observersLock,
int _uid, int _pid, int _userHandle) {
this.observersLock = observersLock;
observer = o;
uid = _uid;
pid = _pid;
userHandle = _userHandle;
notifyForDescendants = n;
try {
observer.asBinder().linkToDeath(this, 0);
} catch (RemoteException e) {
binderDied();
}
}
public void binderDied() {
synchronized (observersLock) {
removeObserverLocked(observer);
}
}
public void dumpLocked(FileDescriptor fd, PrintWriter pw, String[] args,
String name, String prefix, SparseIntArray pidCounts) {
pidCounts.put(pid, pidCounts.get(pid)+1);
pw.print(prefix); pw.print(name); pw.print(": pid=");
pw.print(pid); pw.print(" uid=");
pw.print(uid); pw.print(" user=");
pw.print(userHandle); pw.print(" target=");
pw.println(Integer.toHexString(System.identityHashCode(
observer != null ? observer.asBinder() : null)));
}
}
public static final int INSERT_TYPE = 0;
public static final int UPDATE_TYPE = 1;
public static final int DELETE_TYPE = 2;
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;
}
// Invariant: userHandle is either a hard user number or is USER_ALL
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)) {
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);
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, 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);
}
从代码来看,逐级建立ObserverNode,到达根目录则添加叶子节点。
public boolean removeObserverLocked(IContentObserver observer) {
int size = mChildren.size();
for (int i = 0; i < size; i++) {
boolean empty = mChildren.get(i).removeObserverLocked(observer);
if (empty) {
mChildren.remove(i);
i--;
size--;
}
}
IBinder observerBinder = observer.asBinder();
size = mObservers.size();
for (int i = 0; i < size; i++) {
ObserverEntry entry = mObservers.get(i);
if (entry.observer.asBinder() == observerBinder) {
mObservers.remove(i);
// We no longer need to listen for death notifications. Remove it.
observerBinder.unlinkToDeath(entry, 0);
break;
}
}
if (mChildren.size() == 0 && mObservers.size() == 0) {
return true;
}
return false;
}
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));
}
}
}
}
/**
* targetUserHandle is either a hard user handle or is USER_ALL
*/
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;
}
}
}
}
}
从查找来说,也是从根目录开始进行查找,查看当前是否是叶子节点,叶子节点直接通知,其他非叶子节点则查看是否父节点是否对子节点感兴趣, if (leaf || (!leaf && entry.notifyForDescendants)) {
扩展:ContentObserver是否传Handler是由区别的
private Handler mainHandler= new Handler();
private SettingObserver airplaneCO = new SettingObserver(mainHandler);
// 通过调用getUriFor 方法获得 system表里的"飞行模式"所在行的Uri
Uri airplaneUri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
// 注册内容观察者
getContentResolver().registerContentObserver(airplaneUri, false, airplaneCO);
打印出来的是main线程处理
04-18 15:05:17.485 18028-18028/com.example.broadcast2 D/zhanghao: onChangeid:1 name: main
private SettingObserver airplaneCO = new SettingObserver(null);
// 通过调用getUriFor 方法获得 system表里的"飞行模式"所在行的Uri
Uri airplaneUri = Settings.Global.getUriFor(Settings.Global.AIRPLANE_MODE_ON);
// 注册内容观察者
getContentResolver().registerContentObserver(airplaneUri, false, airplaneCO);
打印结果是
04-18 15:06:43.996 21942-21960/com.example.broadcast2 D/zhanghao: onChangeid:385 name: Binder:21942_3
说明这个是在应用的Binder线程中处理的。– 下一步要查的是Binder线程是如何生成的,是不是在客户端和服务端都会有Binder线程?
还有如果存在多次注册,就会存在多次调用onChange的情况,但是只要调用一次反注册。因为里面是通过ArrayList来存储的,没有去重的功能。
对比广播注册就没有这个问题:广播在AMS中注册存储是以receiver.asBinder为key进行存储的。因而达到一个去重的功能。
另外相对于SubscriptionManager.addOnSubscriptionChangeListener,最终实现在TelephonyRegistry.addOnSubscriptionChangeListener,其中的实现有一个查找在添加的过程。
相比较而言,几个实现方式都是类似的,在创建的时候都会隐式创建一个Binder对象,注册的时候都是将这个Binder本地对象注册到注册中心,这个注册中心可以是AMS,ContentService,也可以是TelephonyRegistry,特点都是使用System进程中的服务来充当。
// register
Record r = null;
find_and_add: {
IBinder b = callback.asBinder();
final int N = mRecords.size();
for (int i = 0; i < N; i++) {
r = mRecords.get(i);
if (b == r.binder) {
break find_and_add;
}
}
r = new Record();
r.binder = b;
r.callback = callback;
r.pkgForDebug = pkgForDebug;
r.callerUid = callerUid;
mRecords.add(r);
if (DBG) Slog.i(TAG, "listen: add new record=" + r);
}