前言
上一篇文章《Android 关于RemoteViews的理解(三)》介绍了RemoteViews的构造方法及支持的View类型,对RemoteViews的创建和工作流程进行了粗略说明也列了相关的set方法,这篇文章我会接着从源码角度对RemoteViews内部机制流程进行分析,最后对RemoteViews进行总结。
1:RemoteViews内部机制
还是之前的setTextViewText方法,源码为:
/**
* Equivalent to calling {@link TextView#setText(CharSequence)}
*
* @param viewId The id of the view whose text should change
* @param text The new text for the view
*/
public void setTextViewText(int viewId, CharSequence text) {
setCharSequence(viewId, "setText", text);
}
从注释可以看到viewId指的是需要改变的View的id,而text则是需要设置的文本,类型是CharSequence 类型,它调用了setCharSequence这个方法来实现具体的功能,setCharSequence源码为:
/**
* Call a method taking one CharSequence on a view in the layout for this RemoteViews.
*
* @param viewId The id of the view on which to call the method.
* @param methodName The name of the method to call.
* @param value The value to pass to the method.
*/
public void setCharSequence(int viewId, String methodName, CharSequence value) {
addAction(new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value));
}
可以看出这个方法也没有具体实现对需要修改的View的相关操作,它调用了addAction方法,这个方法new了一个ReflectionAction对象,从名字可以大概猜出来这个可能是个反射(Reflection)动作(Action),接下去看addAction方法的源码:
/**
* Add an action to be executed on the remote side when apply is called.
*
* @param a The action to add
*/
private void addAction(Action a) {
if (hasLandscapeAndPortraitLayouts()) {
throw new RuntimeException("RemoteViews specifying separate landscape and portrait" +
" layouts cannot be modified. Instead, fully configure the landscape and" +
" portrait layouts individually before constructing the combined layout.");
}
if (mActions == null) {
mActions = new ArrayList<>();
}
mActions.add(a);
}
可以看到RemoteViews内部定义了一个mActions变量,如果变量不存在则new出来,保证其内部必定有这变量,当我们操作setTextViewText方法(或其他set方法)时就会创建一个Action对象然后保存到ArrayList中,到这一步仍然没有对View有实际的操作,可能我们会疑惑,就这?细心的童鞋可能会想前面不是有一个
new ReflectionAction(viewId, methodName, ReflectionAction.CHAR_SEQUENCE, value)
方法,会不会在那里进行操作了,这个方法如下:
ReflectionAction(int viewId, String methodName, int type, Object value) {
this.viewId = viewId;
this.methodName = methodName;
this.type = type;
this.value = value;
}
好像也看不出啥,正着看不出,我们到源头去看,源头不就是apply方法,它的源码如下:
public View apply(Context context, ViewGroup parent, OnClickHandler handler) {
RemoteViews rvToApply = getRemoteViewsToApply(context);
View result = inflateView(context, rvToApply, parent);
loadTransitionOverride(context, handler);
rvToApply.performApply(result, parent, handler);
return result;
}
当布局被注入加载之后里面调用了loadTransitionOverride方法进行一些数据的重载和操作:
private static void loadTransitionOverride(Context context,
RemoteViews.OnClickHandler handler) {
if (handler != null && context.getResources().getBoolean(
com.android.internal.R.bool.config_overrideRemoteViewsActivityTransition)) {
TypedArray windowStyle = context.getTheme().obtainStyledAttributes(
com.android.internal.R.styleable.Window);
int windowAnimations = windowStyle.getResourceId(
com.android.internal.R.styleable.Window_windowAnimationStyle, 0);
TypedArray windowAnimationStyle = context.obtainStyledAttributes(
windowAnimations, com.android.internal.R.styleable.WindowAnimation);
handler.setEnterAnimationId(windowAnimationStyle.getResourceId(
com.android.internal.R.styleable.
WindowAnimation_activityOpenRemoteViewsEnterAnimation, 0));
windowStyle.recycle();
windowAnimationStyle.recycle();
}
}
这些操作之后RemoteViews调用performApply方法执行更新,这个方法的源码如下:
private void performApply(View v, ViewGroup parent, OnClickHandler handler) {
if (mActions != null) {
handler = handler == null ? DEFAULT_ON_CLICK_HANDLER : handler;
final int count = mActions.size();
for (int i = 0; i < count; i++) {
Action a = mActions.get(i);
a.apply(v, parent, handler);
}
}
}
这个方法先判空之前定义的mActions,不为空就遍历mActions列表执行每一个Action对象的apply方法,所以可以看到,这里是操作View的方法。
从这一连串的源码可以分析出,调用RemoteViews的set方法时,不会立刻更新他们的操作。实际上它是通过NotificationManager的notify方法和AppWidgetManger的updataAppWidget方法才能更新通知栏和桌面小部件的界面,值得说的是AppWidgetManger的updataAppWidget,源码如下:
/**
* Set the RemoteViews to use for the specifi