AppWidgetHost AppWidgetHost是真正容纳AppWidget的地方,它的主要功能有两个: (1) 监听来自AppWidgetService的事件,这是主要处理update和provider_changed两个事件,根据这两个事件更新widget。 class UpdateHandler extends Handler { public UpdateHandler(Looper looper) { super(looper); } public void handleMessage(Message msg) { switch (msg.what) { case HANDLE_UPDATE: {//更新事件 updateAppWidgetView(msg.arg1, (RemoteViews)msg.obj); break; } case HANDLE_PROVIDER_CHANGED: {//升级事件 onProviderChanged(msg.arg1, (AppWidgetProviderInfo)msg.obj); break; } } } } (2)另外一个功能就是创建AppWidgetHostView。前面我们说过RemoteViews不是真正的View,只是View的描述,而AppWidgetHostView才是真正的View。这里先创建AppWidgetHostView,然后通过AppWidgetService查询appWidgetId对应的RemoteViews,最后把RemoteViews传递给AppWidgetHostView去updateAppWidget。 protected AppWidgetHostView onCreateView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget) { return new AppWidgetHostView(context); }
更新和升级AppWidget实现:通过appWidgetId,获得相应的AppWidgetHostView 。 protected void onProviderChanged(int appWidgetId, AppWidgetProviderInfo appWidget) { AppWidgetHostView v; synchronized (mViews) { v = mViews.get(appWidgetId); } if (v != null) { v.updateAppWidget(null, AppWidgetHostView.UPDATE_FLAGS_RESET); } }
void updateAppWidgetView(int appWidgetId, RemoteViews views) { AppWidgetHostView v; synchronized (mViews) { v = mViews.get(appWidgetId); } if (v != null) { v.updateAppWidget(views, 0); } } AppWidgetHostView AppWidgetHostView是真正的View,但它只是一个容器,用来容纳实际的AppWidget的View。这个AppWidget的View是根据RemoteViews的描述来创建。这是在AppWidgetHostView.updateAppWidget里做的: public void updateAppWidget(RemoteViews remoteViews) { updateAppWidget(remoteViews, 0); }
void updateAppWidget(RemoteViews remoteViews, int flags) { if (LOGD) Log.d(TAG, "updateAppWidget called mOld=" + mOld + " flags=0x" + Integer.toHexString(flags));
if ((flags & UPDATE_FLAGS_RESET) != 0) { mViewMode = VIEW_MODE_NOINIT; } boolean recycled = false; View content = null; Exception exception = null; // Capture the old view into a bitmap so we can do the crossfade. if (CROSSFADE) { if (mFadeStartTime < 0) { if (mView != null) { final int width = mView.getWidth(); final int height = mView.getHeight(); try { mOld = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888); } catch (OutOfMemoryError e) { // we just won't do the fade mOld = null; } if (mOld != null) { //mView.drawIntoBitmap(mOld); } } } } if (remoteViews == null) { if (mViewMode == VIEW_MODE_DEFAULT) { // We've already done this -- nothing to do. return; } content = getDefaultView(); mLayoutId = -1; mViewMode = VIEW_MODE_DEFAULT; } else { // Prepare a local reference to the remote Context so we're ready to // inflate any requested LayoutParams. mRemoteContext = getRemoteContext(remoteViews); int layoutId = remoteViews.getLayoutId();
// If our stale view has been prepared to match active, and the new // layout matches, try recycling it if (content == null && layoutId == mLayoutId) { try { remoteViews.reapply(mContext, mView); content = mView; recycled = true; if (LOGD) Log.d(TAG, "was able to recycled existing layout"); } catch (RuntimeException e) { exception = e; } } // Try normal RemoteView inflation if (content == null) { try { content = remoteViews.apply(mContext, this); if (LOGD) Log.d(TAG, "had to inflate new layout"); } catch (RuntimeException e) { exception = e; } }
mLayoutId = layoutId; mViewMode = VIEW_MODE_CONTENT; } if (content == null) { if (mViewMode == VIEW_MODE_ERROR) { // We've already done this -- nothing to do. return ; } Log.w(TAG, "updateAppWidget couldn't find any view, using error view", exception); content = getErrorView(); mViewMode = VIEW_MODE_ERROR; } if (!recycled) { prepareView(content); addView(content); }
if (mView != content) { removeView(mView); mView = content; }
if (CROSSFADE) { if (mFadeStartTime < 0) { // if there is already an animation in progress, don't do anything -- // the new view will pop in on top of the old one during the cross fade, // and that looks okay. mFadeStartTime = SystemClock.uptimeMillis(); invalidate(); } } } remoteViews.apply创建了实际的View,下面代码可以看出: public View apply(Context context, ViewGroup parent) { View result;