AppWidget实现机制分析--应用更新时更新AppWidget深入分析

前不久看了博客主 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。

你可能感兴趣的:(Android)