一,写在前面
在前面两篇文章RemoteViews的基本使用(上)之通知栏 ,RemoteViews的基本使用(下)之窗口小部件 中讲述了RemoteViews的两个应用场景,这篇文章主要介绍RemoteViews的内部机制,以及一个小扩展,使用RemoteViews实现跨进程操作界面。本篇文章以窗口小部件为例,来分析RemoteViews如何实现跨进程操作界面。我们都知道在将小部件列表中将窗口小部件拖到桌面,会调用onUpdate方法,在该方法中会调用AppWidgetManager.updateAppWidget(appWidgetIds,remoteViews)来更新窗口小部件,调用RemoteViews方法的一些set..方法,修改窗口小部件的界面。对于这些不是很清楚的哥们,可以查看文章RemoteViews的基本使用(下)之窗口小部件 ,这篇文章对窗口小部件做了简单的介绍,本篇文章主要从源码角度分析RemoteViews,对窗口小部件的生命周期以及使用不再阐述。
二,以窗口小部件为例
查看AppWidgetManager$updateAppWidget源码:
public void updateAppWidget(int[] appWidgetIds, RemoteViews views) {
try {
sService.updateAppWidgetIds(appWidgetIds, views, mContext.getUserId());
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
}
public static AppWidgetManager getInstance(Context context) {
synchronized (sManagerCache) {
if (sService == null) {
IBinder b = ServiceManager.getService(Context.APPWIDGET_SERVICE);
sService = IAppWidgetService.Stub.asInterface(b);
}
WeakReference<AppWidgetManager> ref = sManagerCache.get(context);
AppWidgetManager result = null;
if (ref != null) {
result = ref.get();
}
if (result == null) {
result = new AppWidgetManager(context);
sManagerCache.put(context, new WeakReference<AppWidgetManager>(result));
}
return result;
}
}
sService是一个代理对象,updateAppWidgetIds方法的真正调用在服务里,IAppWidgetService是一个AIDL接口,需要找到继承IAppWidgetService.Stub的那个类,这里直接告诉大家该类是AppWidgetService。
查看AppWidgetService$updateAppWidgetIds源码:
public void updateAppWidgetIds(int[] appWidgetIds, RemoteViews views) {
if (appWidgetIds == null) {
return;
}
if (appWidgetIds.length == 0) {
return;
}
final int N = appWidgetIds.length;
synchronized (mAppWidgetIds) {
for (int i=0; i<N; i++) {
AppWidgetId id = lookupAppWidgetIdLocked(appWidgetIds[i]);
updateAppWidgetInstanceLocked(id, views);
}
}
}
//进入updateAppWidgetInstanceLocked方法
void updateAppWidgetInstanceLocked(AppWidgetId id, RemoteViews views) {
// allow for stale appWidgetIds and other badness
// lookup also checks that the calling process can access the appWidgetId
// drop unbound appWidgetIds (shouldn't be possible under normal circumstances)
if (id != null && id.provider != null && !id.provider.zombie && !id.host.zombie) {
id.views = views;
// is anyone listening?
if (id.host.callbacks != null) {
try {
// the lock is held, but this is a oneway call
id.host.callbacks.updateAppWidget(id.appWidgetId, views);
} catch (RemoteException e) {
// It failed; remove the callback. No need to prune because
// we know that this host is still referenced by this instance.
id.host.callbacks = null;
}
}
}
}
//callbacks实例化的位置
public int[] startListening(IAppWidgetHost callbacks, String packageName, int hostId,
List<RemoteViews> updatedViews) {
int callingUid = enforceCallingUid(packageName);
synchronized (mAppWidgetIds) {
Host host = lookupOrAddHostLocked(callingUid, packageName, hostId);
host.callbacks = callbacks;
updatedViews.clear();
ArrayList<AppWidgetId> instances = host.instances;
int N = instances.size();
int[] updatedIds = new int[N];
for (int i=0; i<N; i++) {
AppWidgetId id = instances.get(i);
updatedIds[i] = id.appWidgetId;
updatedViews.add(id.views);
}
return updatedIds;
}
}
最后会调用id.host.callbacks.updateAppWidget(id.appWidgetId, views),需要找到callbacks的实例化位置,上面代码已经给出答案,调用AppWidgetService$startListening方法会实例化callbacks对象。那么,谁调用了AppWidgetService$startListening方法呢。
查看类AppWidgetHost$startListening方法,源码如下:
public void startListening() {
int[] updatedIds;
ArrayList<RemoteViews> updatedViews = new ArrayList<RemoteViews>();
try {
if (mPackageName == null) {
mPackageName = mContext.getPackageName();
}
updatedIds = sService.startListening(mCallbacks, mPackageN