前不久看了博客主 stonecao 关于AppWidget的两篇文章,在读本篇博客之前,请务必先读这两篇博客:
AppWidget实现机制分析–什么是桌面插件
AppWidget实现机制分析–launcher添加和删除appwidget深入分析
这篇文章是对这两篇文章的一个补充,主要是描述下当桌面挂件应用在更新之后,桌面挂件是怎么更新的
luancher注册监听
在launcher主界面(一个Activity)起来的时候,会在onCreate执行如下一段代码:
Launcher.java
//mAppWidgetHost是launcher承载AppWidgetView的宿主
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
AppWidgetHost.java : startListening
try {
//mCallbacks是比较重要的回调,它是在AppWidgetHost初始化的时候创建的
updatedIds = sService.startListening(mCallbacks,
mContextOpPackageName,mHostId,updatedViews);
}
catch (RemoteException e) {
throw new RuntimeException("system server dead?", e);
}
AppWidgetHost.java : Callbacks
static class Callbacks extends IAppWidgetHost.Stub {
private final WeakReference mWeakHandler;
public Callbacks(Handler handler) {
mWeakHandler = new WeakReference<>(handler);
}
// 更新Widget
public void updateAppWidget(int appWidgetId, RemoteViews views) {
if (isLocalBinder() && views != null) {
views = views.clone();
}
Handler handler = mWeakHandler.get();
if (handler == null) {
return;
}
Message msg = handler.obtainMessage(HANDLE_UPDATE, appWidgetId, 0, views);
msg.sendToTarget();
}
// 当桌面挂件应用的privider发生变化的时候,会回调该方法
public void providerChanged(int appWidgetId, AppWidgetProviderInfo info) {
if (isLocalBinder() && info != null) {
info = info.clone();
}
Handler handler = mWeakHandler.get();
if (handler == null) {
return;
}
Message msg = handler.obtainMessage(HANDLE_PROVIDER_CHANGED,
appWidgetId, 0, info);
msg.sendToTarget();
}
public void providersChanged() {
Handler handler = mWeakHandler.get();
if (handler == null) {
return;
}
handler.obtainMessage(HANDLE_PROVIDERS_CHANGED).sendToTarget();
}
public void viewDataChanged(int appWidgetId, int viewId) {
Handler handler = mWeakHandler.get();
if (handler == null) {
return;
}
Message msg = handler.obtainMessage(HANDLE_VIEW_DATA_CHANGED,
appWidgetId, viewId);
msg.sendToTarget();
}
private static boolean isLocalBinder() {
return Process.myPid() == Binder.getCallingPid();
}
}
Callbacks具有远程通信的功能,通过startListening,将该对象传给AppWidgetService,AppWidgetService在发生一些重要事情的时候,通过该它实现回调。
AppWidgetServiceImpl.java : startListening
//通过构建一个HostId来获取一个Host,该Host对应于Launcher里的mAppWidgetHost。
HostId id = new HostId(Binder.getCallingUid(), hostId, callingPackage);
Host host = lookupOrAddHostLocked(id);
//将callbacks保存起来
host.callbacks = callbacks;
updatedViews.clear();
ArrayList instances = host.widgets;
int N = instances.size();
int[] updatedIds = new int[N];
for (int i = 0; i < N; i++) {
Widget widget = instances.get(i);
updatedIds[i] = widget.appWidgetId;
updatedViews.add(cloneIfLocalBinder(widget.views));
}
从源码上看,整个startlistening的过程,是在AppwidgetService里建立一个与Launcher中的AppwidgetHost对应的Host对象,并将回调对象保存在Host结构里。
当应用程序安装的时候AppWidgetServiceImpl的处理
AppWidgetServiceImpl的构造函数里,会注册一系列的广播:
Intent.ACTION_PACKAGE_ADDED
Intent.ACTION_PACKAGE_CHANGED
Intent.ACTION_PACKAGE_REMOVED
当应用程序安装的时候之后,AppWidgetServiceImpl会收到广播,调用onPackageBroadcastReceived方法
AppWidgetServiceImpl : onPackageBroadcastReceived
//onPackageBroadcastReceived代码里其他的部分都是处理非包安装广播的,就不贴出来了;
// changed 为true
if (added || changed) {
final boolean newPackageAdded = added && (extras == null
|| !extras.getBoolean(Intent.EXTRA_REPLACING, false));
// pkgList 此处只有一个元素,就是安装的应用的包名
for (String pkgName : pkgList) {
//updateProvidersForPackageLocked非常重要,执行一些更新操作
componentsModified |= updateProvidersForPackageLocked(pkgName, userId, null);
// ... and see if these are hosts we've been awaiting.
// NOTE: We are backing up and restoring only the owner.
if (newPackageAdded && userId == UserHandle.USER_OWNER) {
final int uid = getUidForPackage(pkgName, userId);
if (uid >= 0 ) {
resolveHostUidLocked(pkgName, uid);
}
}
}
} else {
// If the package is being updated, we'll receive a PACKAGE_ADDED
// shortly, otherwise it is removed permanently.
final boolean packageRemovedPermanently = (extras == null
|| !extras.getBoolean(Intent.EXTRA_REPLACING, false));
if (packageRemovedPermanently) {
for (String pkgName : pkgList) {
componentsModified |= removeHostsAndProvidersForPackageLocked(
pkgName, userId);
}
}
}
if (componentsModified) {
saveGroupStateAsync(userId);
// If the set of providers has been modified, notify each active AppWidgetHost
scheduleNotifyGroupHostsForProvidersChangedLocked(userId);
}
AppWidgetServiceImpl : updateProvidersForPackageLocked
//该方法执行对相应package的对应的AppWidget更新操作
private boolean updateProvidersForPackageLocked(String packageName, int userId,
Set removedProviders) {
boolean providersUpdated = false;
HashSet keep = new HashSet<>();
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE);
intent.setPackage(packageName);
//获取所有能收到ACTION_APPWIDGET_UPDATE的广播组件
List broadcastReceivers = queryIntentReceivers(intent, userId);
// add the missing ones and collect which ones to keep
int N = broadcastReceivers == null ? 0 : broadcastReceivers.size();
for (int i = 0; i < N; i++) {
ResolveInfo ri = broadcastReceivers.get(i);
ActivityInfo ai = ri.activityInfo;
//该应用不能安装在手机U盘里
if ((ai.applicationInfo.flags & ApplicationInfo.FLAG_EXTERNAL_STORAGE) != 0) {
continue;
}
if (packageName.equals(ai.packageName)) {
ProviderId providerId = new ProviderId(ai.applicationInfo.uid,
new ComponentName(ai.packageName, ai.name));
//从保存的provider里查找,相应的Provider
Provider provider = lookupProviderLocked(providerId);
if (provider == null) {
//如果没有找到相应的provider,构建一个Provider,并add进Provider集合里
if (addProviderLocked(ri)) {
keep.add(providerId);
providersUpdated = true;
}
} else {
//对新的Provider进行解析,获取新的Provider
Provider parsed = parseProviderInfoXml(providerId, ri);
if (parsed != null) {
keep.add(providerId);
// Use the new AppWidgetProviderInfo.
provider.info = parsed.info;
// If it's enabled
final int M = provider.widgets.size();
//如果Launcher已经添加了该Provider对应的widget,就需要对该Widget进行更新了
if (M > 0) {
int[] appWidgetIds = getWidgetIds(provider.widgets);
// Reschedule for the new updatePeriodMillis (don't worry about handling
// it specially if updatePeriodMillis didn't change because we just sent
// an update, and the next one will be updatePeriodMillis from now).
//取消之前对该Provider设置的定时广播
cancelBroadcasts(provider);
//重新注册定时广播
registerForBroadcastsLocked(provider, appWidgetIds);
// If it's currently showing, call back with the new
// AppWidgetProviderInfo.
for (int j = 0; j < M; j++) {
Widget widget = provider.widgets.get(j);
widget.views = null;
// 这个方法会通知所有的Host,说Provider改变了
scheduleNotifyProviderChangedLocked(widget);
}
// Now that we've told the host, push out an update.
// 发送ACTION_APPWIDGET_UPDATE广播,通知相应的挂件应用,执行更新挂件的操作
sendUpdateIntentLocked(provider, appWidgetIds);
}
}
providersUpdated = true;
}
}
}
// prune the ones we don't want to keep
N = mProviders.size();
for (int i = N - 1; i >= 0; i--) {
Provider provider = mProviders.get(i);
if (packageName.equals(provider.info.provider.getPackageName())
&& provider.getUserId() == userId
&& !keep.contains(provider.id)) {
if (removedProviders != null) {
removedProviders.add(provider.id);
}
deleteProviderLocked(provider);
providersUpdated = true;
}
}
return providersUpdated;
}
scheduleNotifyProviderChangedLocked和sendUpdateIntentLocked两个方法的作用不一样,前者是告诉所有的Host,就是之前startListening时注册过的Host,说Provider发生了变化,后者则是发送一个ACTION_APPWIDGET_UPDATE广播给该Provider所属的应用,让它执行一次更新挂件的操作。
AppWidgetServiceImpl : scheduleNotifyProviderChangedLocked
private void scheduleNotifyProviderChangedLocked(Widget widget) {
if (widget == null || widget.provider == null || widget.provider.zombie
|| widget.host.callbacks == null || widget.host.zombie) {
return;
}
SomeArgs args = SomeArgs.obtain();
args.arg1 = widget.host;
args.arg2 = widget.host.callbacks;
args.arg3 = widget.provider.info;
args.argi1 = widget.appWidgetId;
// 向Handler里抛出了Provider改变的消息一个消息
mCallbackHandler.obtainMessage(
CallbackHandler.MSG_NOTIFY_PROVIDER_CHANGED,
args).sendToTarget();
}
AppWidgetServiceImpl : CallbackHandler
case MSG_NOTIFY_PROVIDER_CHANGED: {
SomeArgs args = (SomeArgs) message.obj;
Host host = (Host) args.arg1;
IAppWidgetHost callbacks = (IAppWidgetHost) args.arg2;
AppWidgetProviderInfo info = (AppWidgetProviderInfo)args.arg3;
final int appWidgetId = args.argi1;
args.recycle();
handleNotifyProviderChanged(host, callbacks, appWidgetId, info);
} break;
AppWidgetServiceImpl : handleNotifyProviderChanged
private void handleNotifyProviderChanged(Host host, IAppWidgetHost callbacks,
int appWidgetId, AppWidgetProviderInfo info) {
try {
//这个就是回调到了AppWidgetHost的mCallbacks.providerChanged
callbacks.providerChanged(appWidgetId, info);
} catch (RemoteException re) {
synchronized (mLock){
Slog.e(TAG, "Widget host dead: " + host.id, re);
host.callbacks = null;
}
}
}
进而会调到AppWidgetHost的onProviderChanged
AppWidgetHost : onProviderChanged
protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) {
AppWidgetHostView v;
// Convert complex to dp -- we are getting the AppWidgetProviderInfo from the
// AppWidgetService, which doesn't have our context, hence we need to do the
// conversion here.
appWidget.minWidth =
TypedValue.complexToDimensionPixelSize(appWidget.minWidth, mDisplayMetrics);
appWidget.minHeight =
TypedValue.complexToDimensionPixelSize(appWidget.minHeight, mDisplayMetrics);
appWidget.minResizeWidth =
TypedValue.complexToDimensionPixelSize(appWidget.minResizeWidth, mDisplayMetrics);
appWidget.minResizeHeight =
TypedValue.complexToDimensionPixelSize(appWidget.minResizeHeight, mDisplayMetrics);
synchronized (mViews) {
v = mViews.get(appWidgetId);
}
//如果Launcher添加该Provider的widget,会v 不为null
if (v != null) {
v.resetAppWidget(appWidget);
}
}
AppWidgetHostView : resetAppWidget
void resetAppWidget(AppWidgetProviderInfo info) {
mInfo = info;
mViewMode = VIEW_MODE_NOINIT;
updateAppWidget(null);
}
AppWidgetHostView : updateAppWidget
public void updateAppWidget(RemoteViews remoteViews) {
boolean recycled = false;
View content = null;
Exception exception = null;
...................................
// Prepare a local reference to the remote Context so we're ready to
// inflate any requested LayoutParams.
mRemoteContext = getRemoteContext();
int layoutId = remoteViews.getLayoutId();
//此处remoteViews为null
if (remoteViews == null) {
// 此处 mViewMode = VIEW_MODE_NOINIT
if (mViewMode == VIEW_MODE_DEFAULT) {
// We've already done this -- nothing to do.
return;
}
// 获取默认的View
content = getDefaultView();
mLayoutId = -1;
mViewMode = VIEW_MODE_DEFAULT;
}
......................................
}
总结
整个过程大概是,AppWidgetHost通过startListening,在AppWidgetService创建了一个与它相对应的Host对象,并将Launcher里创建的一个Binder对象保存在这个Host对象里,当有应用安装更新的时候,AppWidgetService会通过回调该Host对象中保存的Binder对象的方法,通知AppWidgetHost来更新AppWidgetHostView。