RemoteViews原理分析及应用.docx

上传人:b****7 文档编号:23694586 上传时间:2023-05-20 格式:DOCX 页数:28 大小:484.25KB
下载 相关 举报
RemoteViews原理分析及应用.docx_第1页
第1页 / 共28页
RemoteViews原理分析及应用.docx_第2页
第2页 / 共28页
RemoteViews原理分析及应用.docx_第3页
第3页 / 共28页
RemoteViews原理分析及应用.docx_第4页
第4页 / 共28页
RemoteViews原理分析及应用.docx_第5页
第5页 / 共28页
点击查看更多>>
下载资源
资源描述

RemoteViews原理分析及应用.docx

《RemoteViews原理分析及应用.docx》由会员分享,可在线阅读,更多相关《RemoteViews原理分析及应用.docx(28页珍藏版)》请在冰豆网上搜索。

RemoteViews原理分析及应用.docx

RemoteViews原理分析及应用

RemoteViews原理分析及应用

RemoteViews基本概念

RemoteViews乍一看名字似乎也是一种View,实则不然,它并不是View。

来看RemoteViews的定义及官方说明:

/**

*Aclassthatdescribesaviewhierarchythatcanbedisplayedin

*anotherprocess.Thehierarchyisinflatedfromalayoutresource

*file,andthisclassprovidessomebasicoperationsformodifying

*thecontentoftheinflatedhierarchy.

*/

publicclassRemoteViewsimplementsParcelable,Filter{

……

}

我们可以得到以下几点结论:

RemoteViews只是一个实现了Parcelable和Filter接口的类,而并非继承自View。

RemoteViews用来描述可运行在其他进程中的视图结构,但RemoteViews本身不是视图,只是一个描述类。

RemoteViews描述的远程视图需要通过layout资源文件定义。

RemoteViews类提供了一系列修改远程视图的方法。

现在我们对RemoteViews应该有了一个大概的认识,它可以跨进程来显示和更新视图。

RemoteViews主要有两个应用场景:

自定义通知栏

桌面小部件

本文最后会给出RemoteViews的应用实例,接下来我们先分析RemoteViews的实现原理。

RemoteViews原理分析

构造函数

RemoteViews提供了多个构造函数,如:

publicRemoteViews(StringpackageName,intlayoutId){}

publicRemoteViews(StringpackageName,intuserId,intlayoutId){}

publicRemoteViews(RemoteViewslandscape,RemoteViewsportrait){}

publicRemoteViews(Parcelparcel){}

以一个最常用的构造方法为例:

/**

*CreateanewRemoteViewsobjectthatwilldisplaytheviewscontained

*inthespecifiedlayoutfile.

*

*@parampackageNameNameofthepackagethatcontainsthelayoutresource

*@paramlayoutIdTheidofthelayoutresource

*/

publicRemoteViews(StringpackageName,intlayoutId){

this(getApplicationInfo(packageName,UserHandle.myUserId()),layoutId);

}

由注释可知,第一个参数为包名,第二个参数为布局资源文件的ID,接下来会调用:

/**

*CreateanewRemoteViewsobjectthatwilldisplaytheviewscontained

*inthespecifiedlayoutfile.

*

*@paramapplicationTheapplicationwhosecontentisshownbytheviews.

*@paramlayoutIdTheidofthelayoutresource.

*

*@hide

*/

protectedRemoteViews(ApplicationInfoapplication,intlayoutId){

mApplication=application;

mLayoutId=layoutId;

mBitmapCache=newBitmapCache();

//setupthememoryusagestatistics

mMemoryUsageCounter=newMemoryUsageCounter();

recalculateMemoryUsage();

}

同样也很简单,第一个参数为远程视图展示内容所属的Application信息,第二个参数为布局文件ID。

该构造方法主要是初始化mApplication与mLayoutId,其他代码为图片缓存及内存计算的一些逻辑。

核心属性字段

RemoteView有如下几个比较重要的属性字段:

/**

*Applicationthathoststheremoteviews.

*

*@hide

*/

privateApplicationInfomApplication;

/**

*TheresourceIDofthelayoutfile.(Addedtotheparcel)

*/

privatefinalintmLayoutId;

/**

*Anarrayofactionstoperformontheviewtreeonceithasbeen

*inflated

*/

privateArrayListmActions;

前两个比较好理解,而且上文提到是在构造函数中赋值的。

mActions是用来存储Action的一个列表,而Action可以理解为对远程视图操作的一个封装,下文会详细解释。

RemoteView注解

在RemoteViews源码中声明了如下注解:

/**

*ThisannotationindicatesthatasubclassofViewisalllowedtobeused

*withthe{@linkRemoteViews}mechanism.

*/

@Target({ElementType.TYPE})

@Retention(RetentionPolicy.RUNTIME)

public@interfaceRemoteView{

}

从注解类型来看为运行时注解,作用于类或接口,结合注释可知此注解用于View的子类,用来标识该View是否可以作为远程视图使用。

由此我们也可以推断,并非所有View都可以作为远程视图,只有声明了RemoteView注解的View才可以。

我们从源码定义来简单验证一下:

TextView的定义

@RemoteView

publicclassTextViewextendsViewimplementsViewTreeObserver.OnPreDrawListener{}

Button的定义

@RemoteView

publicclassButtonextendsTextView{}

ImageView的定义

@RemoteView

publicclassImageViewextendsView{}

ProgressBar的定义

@RemoteView

publicclassProgressBarextendsView{}

LinearLayout的定义

@RemoteView

publicclassLinearLayoutextendsViewGroup{}

EditText的定义

publicclassEditTextextendsTextView{}

不再一一列举,可见EditText虽然是继承自TextView的,但它没有使用@RemoteView注解,因此并不能用作远程视图。

RemoteViews所支持的View类型如下:

LinearLayout、RelativeLayout、FrameLayout、GridLayout、AbsoluteLayout(已弃用)

TextView、Button、ImageView、ImageButton、Chronometer、ProgressBar、ListView、GridView、StackView、ViewFlipper、AdapterViewFlipper、ViewStub、AnalogClock(已弃用)

也就是说远程视图只能使用上述所列举的View,它们的子类及其他View都是不支持的,如果使用了不支持的View,则会报异常。

实现Parcelable和Filter接口的意义

Parcelable比较容易理解,就是支持序列化以便于跨进程操作。

那么Filter的作用是什么呢?

Filter接口的定义如下:

/**

*HooktoallowclientsoftheLayoutInflatertorestrictthesetofViewsthatareallowed

*tobeinflated.

*

*/

publicinterfaceFilter{

/**

*HooktoallowclientsoftheLayoutInflatertorestrictthesetofViews

*thatareallowedtobeinflated.

*

*@paramclazzTheclassobjectfortheViewthatisabouttobeinflated

*

*@returnTrueifthisclassisallowedtobeinflated,orfalseotherwise

*/

@SuppressWarnings("unchecked")

booleanonLoadClass(Classclazz);

}

从注释中不难看出Filter是用来限制和过滤View用的,上文提到并非所有的View都能用作远程视图,如果为上述列举的View,则onLoadClass(Classclazz)返回true,否则返回false。

在RemoteViews中实现了Filter接口的方法:

publicbooleanonLoadClass(Classclazz){

returnclazz.isAnnotationPresent(RemoteView.class);

}

可以看到就是根据@RemoteView注解来判断是否可以使用该View作为远程视图。

RemoteViews实现原理

跨进程是哪两个进程

很显然我们的应用自身是一个进程,那么另一个进程是什么呢?

在RemoteViews的应用场景中,如自定义通知栏和桌面小部件,它们都运行在系统进程中,即SystemServer进程。

如此一来,远程视图运行在SystemServer进程中,我们在自己的应用进程中跨进程来操作远程视图。

前文说到RemoteViews实现了Parcelable接口,那么RemoteViews便可以从应用进程传输到系统进程了。

远程View是如何加载的

通常情况下,我们使用LayoutInflater加载布局只需要知道布局ID即可。

还记得前文讲RemoteViews的构造函数时,有两个重要的字段:

mApplication

mLayoutId

在系统进程中加载远程视图正是利用了上述两个字段。

在RemoteViews的源码中加载布局的逻辑如下:

/**

*Inflatestheviewhierarchyrepresentedbythisobjectandapplies

*alloftheactions.

*

*

Callerbeware:

thismaythrow

*

*@paramcontextDefaultcontexttouse

*@paramparentParentthattheresultingviewhierarchywillbeattachedto.Thismethod

*doesnotattachthehierarchy.Thecallershoulddosowhenappropriate.

*@returnTheinflatedviewhierarchy

*/

publicViewapply(Contextcontext,ViewGroupparent){

returnapply(context,parent,null);

}

接下来会调用:

publicViewapply(Contextcontext,ViewGroupparent,OnClickHandlerhandler){

RemoteViewsrvToApply=getRemoteViewsToApply(context);

Viewresult=inflateView(context,rvToApply,parent);

loadTransitionOverride(context,handler);

rvToApply.performApply(result,parent,handler);

returnresult;

}

先重点关注下面这一行代码:

Viewresult=inflateView(context,rvToApply,parent);

其内部实现就是常见的布局加载方式了:

privateViewinflateView(Contextcontext,RemoteViewsrv,ViewGroupparent){

//RemoteViewsmaybebuiltbyanapplicationinstalledinanother

//user.Sobuildacontextthatloadsresourcesfromthatuserbut

//stillreturnsthecurrentusersuserIdsosettingslikedata/timeformats

//areloadedwithoutrequiringcrossuserpersmissions.

finalContextcontextForResources=getContextForResources(context);

ContextinflationContext=newContextWrapper(context){

@Override

publicResourcesgetResources(){

returncontextForResources.getResources();

}

@Override

publicResources.ThemegetTheme(){

returncontextForResources.getTheme();

}

@Override

publicStringgetPackageName(){

returncontextForResources.getPackageName();

}

};

LayoutInflaterinflater=(LayoutInflater)

context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

//Cloneinflatersoweloadresourcesfromcorrectcontextand

//wedon'taddafiltertothestaticversionreturnedbygetSystemService.

inflater=inflater.cloneInContext(inflationContext);

inflater.setFilter(this);

returninflater.inflate(rv.getLayoutId(),parent,false);

}

主要关注最后几行代码即可,把握主要流程,一些细节可以暂时忽略。

RemoteViews的apply方法中还有这样一行代码:

rvToApply.performApply(result,parent,handler);

接下来会调用:

privatevoidperformApply(Viewv,ViewGroupparent,OnClickHandlerhandler){

if(mActions!

=null){

handler=handler==null?

DEFAULT_ON_CLICK_HANDLER:

handler;

finalintcount=mActions.size();

for(inti=0;i

Actiona=mActions.get(i);

a.apply(v,parent,handler);

}

}

}

即遍历RemoteViews中存储的Action,然后执行Action的apply方法。

远程View是如何操作的

上一小节提到了Action对象,那么Action又是什么呢?

由于RemoteViews运行在远端进程中,因此无法通过findViewById的方法来获取View。

为了操作远程视图,于是就将对视图的操作封装成一个Action对象,Action是一个实现了Parcelable接口的抽象类,因此可以跨进程传输。

先来看下Action的定义:

/**

*Baseclassforallactionsthatcanbeperformedonan

*inflatedview.

*

*SUBCLASSESMUSTBEIMMUTABLESOCLONEWORKS!

!

!

!

!

*/

privateabstractstaticclassActionimplementsParcelable{

publicabstractvoidapply(Viewroot,ViewGrouprootParent,

OnClickHandlerhandler)throwsActionException;

publicstaticfinalintMERGE_REPLACE=0;

publicstaticfinalintMERGE_APPEND=1;

publicstaticfinalintMERGE_IGNORE=2;

publicintdescribeContents(){

return0;

}

/**

*Overriddenbyeachclasstoreportonit'sownmemoryusage

*/

publicvoidupdateMemoryUsageEstimate(MemoryUsageCountercounter){

//WecurrentlyonlycalculateBitmapmemoryusage,sobydefault,

//don'tdoanythinghere

}

publicvoidsetBitmapCache(BitmapCachebitmapCache){

//Donothing

}

publicintmergeBehavior(){

returnMERGE_REPLACE;

}

publicabstractStringgetActionName();

publicStringgetUniqueKey(){

return(getActionName()+viewId);

}

/**

*Thisiscalledonthebackgroundthread.Itshouldperformanynon-uicomputations

*andreturnthefinalonwhichwillrunontheUIthread.

*Overridethisifsomeofthetaskscanbeperformedasync.

*/

publicActioninitActionAsync(ViewTreeroot,ViewGrouprootParent,OnClickHandlerhandler){

returnthis;

}

intviewId;

}

从说明来看,Action就是对远程视图操作的一个封装,它提供了一个抽象方法:

publicabstractvoidapply(Viewroot,ViewGrouprootParent,

OnClickHandlerhandler)throwsActionException;

该抽象方法需要子类做具体实现。

Action有很多子类,几乎每个子类都用来辅助一种View操作,下面简单罗列两个:

/**

*HelperactiontosetcompounddrawablesonaTextView.Supportsrelative

*(s/t/e/b)orcardinal(l/t/r/b)arrangement.

*/

privateclassTextViewDrawableActionextendsAction{}

/**

*HelperactiontosettextsizeonaTextViewinanysupportedunits.

*/

privateclassTextViewSizeActionextendsAction{}

这里不再一一列举,有兴趣的可以参考源码。

了解了Action的概念之后,我们以为远程的TextView设置文本为例,来具体看一下其工作流程。

平时我们给TextView设置文本只需要调用其setText方法即可,但RemoteViews无法这样使用,RemoteViews提供了一系列关于View操作的set方法,这里会用到如下方法:

/**

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 求职职场 > 社交礼仪

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1