AppWidgetHost类、AppWidgetHostView类

转载:http://blog.sina.com.cn/s/blog_5da93c8f0100ydtp.html

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;

        Context c = prepareContext(context);

        LayoutInflater inflater = (LayoutInflater)
                c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

        inflater = inflater.cloneInContext(c);
        inflater.setFilter(this);

        result = inflater.inflate(mLayoutId, parent, false);

        performApply(result);

        return result;
    }

Host的实现者

AppWidgetHost和AppWidgetHostView是在框架中定义的两个基类。应用程序可以利用这两个类来实现自己的Host。Launcher是缺省的桌面,它是一个Host的实现者。

LauncherAppWidgetHostView扩展了AppWidgetHostView,实现了对长按事件的处理。

LauncherAppWidgetHost扩展了AppWidgetHost,这里只是重载了onCreateView,创建LauncherAppWidgetHostView的实例。

AppWidgetService

AppWidgetService 存在的目的主要是解开AppWidgetProvider和AppWidgetHost之间的耦合。如果AppWidgetProvider和 AppWidgetHost的关系固定死了,AppWidget就无法在任意进程里显示了。而有了 AppWidgetService,AppWidgetProvider根本不需要知道自己的AppWidget在哪里显示了。


其它资料:
http://www.iteye.com/topic/825407
http://disanji.net/2011/02/20/android-application-appwidget/
http://www.iteye.com/topic/910431

你可能感兴趣的:(AppWidgetHost类、AppWidgetHostView类)