RemoteViews的解析
来源:互联网 发布:js toggleclass 编辑:程序博客网 时间:2024/06/08 07:09
定义
RemoteViews从字面翻译来看是远程的视图,其实是表示可以在其它进程中显示的View。RemoteViews在Android实际开发中,主要用在通知栏(可参考《Android中通知栏的使用》)和桌面小部件(可参考《Android中小部件的使用》。因为通知栏和小部件都运行在系统的SystemServer进程。所以要对它们进行界面的更新就必须使用RemoteViews来进行跨进程更新界面。
RemoteViews支持类型
RemoteViews并不是所有的View类型都支持,目前RemoteViews支持的类型:
Layout:
FrameLayout、LineraLayout、RelativeLayout、GridLayout
View:
AnalogClock、Button、Chronometer、ImageButton、ImageView、ProgressBar、TextView、ViewFlipper、ListView、GridView、StackView、AdapterViewFlipper、ViewStub
注意:
RemoteViews不支持自定义View的他们的子类,如果使用其他的不支持的控件会抛 InflateException。
RemoteViews一系列set方法
RemoteViews没有提供findViewById方法,要访问View元素必须通过RemoteViews所提供的一系列set方法来完成,事实上大部分set方法是通过反射来完成的。常用部分方法如:
方法名
作用
setTextViewText
设置TextView的文本
setTextViewSize
设置TextView的字体大小
setTextColor
设置TextView的字体颜色
setImageViewResource
设置ImageView的图片资源
setImageViewResource
设置ImageView的图片
setInt
反射调用View对象的参数类型为int的方法
setLong
反射调用View对象的参数类型为long的方法
setBoolean
反射调用View对象的参数类型为boolean的方法
setOnClickPendingIntent
为View添加单击事件,事件类型只能为PendingIntent
内部机制
通知栏和桌面小部件分别由NotificationManager和AppWidgetManager管理,它们都是通过Binder分别像跨进程一样和SystemServer进程中的NotificationManager以及AppWidgetManager进行通信。所以说,通知栏和桌面小部件的View实际上是在SystemServer系统进程NotificationManager或AppWidgetManager中被加载的。
RemoteViews(实现了Parcelable接口)会通过Binder传递到SystemServer进程,系统会根据RemoteViews中的包名等信息去得到应用的资源。然后会通过LayoutInflater去加载RemoveViews中的布局文件。接着系统会对View执行一系列界面更新任务,这些任务就是之前提到通过set方法来提交的。set方法对View所做的更新并不是立刻执行,而是等到RemoteViews被加载以后才能执行,这样RemoteViews就可以在SystemServer进程中显示了。
原理解析
因为通过Binder去支持所有View和View操作代价太大,而且大量IPC操作会影响效率。所以系统提供了一个Action代表一个View操作,它实现了parcelable接口。在我们应用中每调用一次set方法,RemoteViews中就会添加一个对应的Action对象(系统将View操作封装到Action对象),当我们通过NotificationManager或AppWidgetManager来提交更新时,这些Action对象就会传输到远程进程SystemServer并依次进行。
远程进程通过RemoteViews的apply方法以及reapply方法来加载或更新界面的(apply会加载布局并更新界面,而reapply则只会更新界面),RemoteViews的apply方法和reapply方法内部则会去遍历所有Action对象并调用它们的apply方法。
重要源码解析
我们先来看看RemoteViews类的构造函数,构造函数接收包名和view资源id,为了后面系统根据这两个值来获取资源
/** * Create a newRemoteViews object that will display the views contained * in thespecified layout file. * * @parampackageName Name of the package that contains the layout resource * @paramlayoutId The id of the layout resource */public RemoteViews(String packageName, int layoutId){ mPackage =packageName; mLayoutId =layoutId; // setupthe memory usage statistics mMemoryUsageCounter = new MemoryUsageCounter(); recalculateMemoryUsage();}
接着我们拿setTextViewText方法来看,setTextViewText方法调用了setCharSequence方法,setCharSequence方法第二个参数是methodName,意思是方法名,值是:“setText”:
/** * Equivalentto calling TextView.setText * * @paramviewId The id of the view whose text should change * @param textThe new text for the view */public void setTextViewText(int viewId, CharSequencetext) { setCharSequence(viewId, "setText", text);}/** * Call amethod taking one CharSequence on a view in the layout for this RemoteViews. * * @paramviewId The id of the view whose text should change * @parammethodName The name of the method to call. * @param valueThe value to pass to the method. */public void setCharSequence(int viewId, StringmethodName, CharSequence value) { addAction(new ReflectionAction(viewId, methodName,ReflectionAction.CHAR_SEQUENCE, value));}
setCharSequence方法调用了addAction方法,addAction方法接收一个内部抽象类Action的对象。可以看出每次调用setTextViewText方法就会创建一个Action对象然后用一个ArrayList保存起来:
/** * Add anaction to be executed on the remote side when apply is called. * * @param a Theaction to add */private void addAction(Action a) { if(mActions == null) { mActions = new ArrayList<Action>(); } mActions.add(a); // updatethe memory usage stats a.updateMemoryUsageEstimate(mMemoryUsageCounter);}
接着,再看看RemoteViews的apply方法。它首先调用了inflateView方法,inflateView方法内部通过LayoutInflater加载RemoveViews的布局文件(通过getLayoutId方法获得构造函数传入的view资源id),加载完成后通过performApply方法去执行一些更新操作。
/** @hide */public View apply(Context context, ViewGroup parent,OnClickHandler handler) { RemoteViewsrvToApply = getRemoteViewsToApply(context); View result= inflateView(context, rvToApply, parent); loadTransitionOverride(context, handler); rvToApply.performApply(result, parent, handler); returnresult;}private View inflateView(Context context, RemoteViewsrv, ViewGroup parent) { //RemoteViews may be built by an application installed in another // user. Sobuild a context that loads resources from that user but // stillreturns the current users userId so settings like data / time formats // areloaded without requiring cross user persmissions. finalContext contextForResources = getContextForResources(context); ContextinflationContext = new ContextWrapper(context) { @Override publicResources getResources() { return contextForResources.getResources(); } @Override publicResources.Theme getTheme() { return contextForResources.getTheme(); } @Override publicString getPackageName() { return contextForResources.getPackageName(); } }; LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); // Cloneinflater so we load resources from correct context and // we don'tadd a filter to the static version returned by getSystemService. inflater =inflater.cloneInContext(inflationContext); inflater.setFilter(this); View v =inflater.inflate(rv.getLayoutId(), parent, false); v.setTagInternal(R.id.widget_frame, rv.getLayoutId()); return v;}
再来看看performApply方法的源码。它遍历mActions里的对象,然后执行Action对象的apply方法。所以Action对象的apply方法就是真正操作View的地方:
private void performApply(View v, ViewGroup parent,OnClickHandler handler) { if(mActions != null) { handler= handler == null ? DEFAULT_ON_CLICK_HANDLER : handler; finalint count = mActions.size(); for(int i = 0; i < count; i++) { Action a = mActions.get(i); a.apply(v, parent, handler); } }}
回头看下,我们之前addAction中传入的Action对象是内部类ReflectionAction,我们从它的apply方法可以清楚看到是通过反射来对View的操作调用:
@Overridepublic void apply(View root, ViewGroup rootParent,OnClickHandler handler) { final Viewview = root.findViewById(viewId); if (view ==null) return; Class<?> param = getParameterType(); if (param== null) { thrownew ActionException("bad type: " + this.type); } try { getMethod(view, this.methodName, param).invoke(view,wrapArg(this.value)); } catch(ActionException e) { throwe; } catch(Exception ex) { thrownew ActionException(ex); }}
好了,到这相信大家应该会对RemoteViews的工原理有了一定的理解了,我们之前介绍的通知栏和桌面小部件的使用中,RemoteViews只是当成参数传递给NotificationManager或AppWidgetManager,并没有使用到apply和reapply,下一节我们会对以实例方式来对RemoteViews的使用演示跨进程更新界面的使用。
- RemoteViews的解析
- (十一)RemoteViews的使用解析
- RemoteViews完全解析
- RemoteViews之RemoteViews的内部机制
- Android的RemoteViews
- RemoteViews的使用
- RemoteViews的使用
- RemoteViews的应用
- RemoteViews的用处
- RemoteViews的用途
- RemoteViews的内部机制
- RemoteViews的简单应用
- RemoteViews
- RemoteViews
- RemoteViews
- RemoteViews
- Android中RemoteViews的实现
- Android中RemoteViews的实现 .
- android组件ListView之BaseAdapter简单使用
- html5调控颜色
- 【代码重构 & JDT】修改Eclipse重构撤回的深度限制
- AngularJS的组件化、过滤器、自定义过滤器之旅
- shell 脚步分析数据
- RemoteViews的解析
- servlet在web应用中的过程
- 关于javascript对象的认识详解
- dblink创建以及出现问题
- B
- Ubuntu16.10安装Lua5.3.4
- 安卓逆向工程与代码安全
- js基础运算符
- 电脑一直重复弹出adb.exe已停止工作