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

 

你可能感兴趣的:(android,Android开发,AppWidgetHost)