插件式换肤框架搭建资源加载源码分析.docx

上传人:b****5 文档编号:7031640 上传时间:2023-01-16 格式:DOCX 页数:17 大小:228.84KB
下载 相关 举报
插件式换肤框架搭建资源加载源码分析.docx_第1页
第1页 / 共17页
插件式换肤框架搭建资源加载源码分析.docx_第2页
第2页 / 共17页
插件式换肤框架搭建资源加载源码分析.docx_第3页
第3页 / 共17页
插件式换肤框架搭建资源加载源码分析.docx_第4页
第4页 / 共17页
插件式换肤框架搭建资源加载源码分析.docx_第5页
第5页 / 共17页
点击查看更多>>
下载资源
资源描述

插件式换肤框架搭建资源加载源码分析.docx

《插件式换肤框架搭建资源加载源码分析.docx》由会员分享,可在线阅读,更多相关《插件式换肤框架搭建资源加载源码分析.docx(17页珍藏版)》请在冰豆网上搜索。

插件式换肤框架搭建资源加载源码分析.docx

插件式换肤框架搭建资源加载源码分析

插件式换肤框架搭建-资源加载源码分析

1.概述

  大部分控件我们都会使用,但是我们未必知道其资源加载的原理,目前换肤的框架比较多我们可以随随便便拿过来用,但早在几年前这些资料是比较少的,如果想做一个换肤的框架那就只能自己一点一点啃源码。

  如果说我们现在不去用第三方的开源框架,要做一个换肤的功能,摆在我们面前的其实只有一个问题需要解决,那就是如何读取另外一个皮肤apk中的资源。

 

2.资源加载源码分析

2.1我们先来看一下ImageView的scr属性到底是怎么加载图片资源的:

android:

layout_width="wrap_content"

android:

src="@drawable/app_icon"

android:

layout_height="wrap_content"/>

//ImageView.java解析属性

finalTypedArraya=context.obtainStyledAttributes(

attrs,R.styleable.ImageView,defStyleAttr,defStyleRes);

//通过TypedArray获取图片

finalDrawabled=a.getDrawable(R.styleable.ImageView_src);

if(d!

=null){

setImageDrawable(d);

}

//TypedArray.getDrawable()方法

publicDrawablegetDrawable(@StyleableResintindex){

//省略部分代码....

//加载资源其实是通过mResources去获取的

returnmResources.loadDrawable(value,value.resourceId,mTheme);

}

2.2Resource创建过程分析:

  

  我们在Activity中也经常这样使用context.getResources().getColor(R.id.title_color),那么这个Resources实例是怎么创建的呢?

我们可以先从context的实现类ContextImpl入手

privateContextImpl(ContextImplcontainer,ActivityThreadmainThread,

LoadedApkpackageInfo,IBinderactivityToken,UserHandleuser,intflags,

Displaydisplay,ConfigurationoverrideConfiguration,intcreateDisplayWithId){

......

Resourcesresources=packageInfo.getResources(mainThread);

if(resources!

=null){

//不会走此分支,因为6.0中还不支持多屏显示,虽然已经有不少相关代码了,7.0以及正式支持多屏操作了

if(displayId!

=Display.DEFAULT_DISPLAY

||overrideConfiguration!

=null

||(compatInfo!

=null&&compatInfo.applicationScale

!

=resources.getCompatibilityInfo().applicationScale)){

......

}

}

......

mResources=resources;

}

//packageInfo.getResources方法

publicResourcesgetResources(ActivityThreadmainThread){

//缓存机制,如果LoadedApk中的mResources已经初始化则直接返回,

//否则通过ActivityThread创建resources对象

if(mResources==null){

mResources=mainThread.getTopLevelResources(mResDir,mSplitResDirs,mOverlayDirs,

mApplicationInfo.sharedLibraryFiles,Display.DEFAULT_DISPLAY,this);

}

returnmResources;

}

最终会来到ResourcesManager的getResources方法

public@NonNullResourcesgetResources(@NullableIBinderactivityToken,

@NullableStringresDir,//app资源文件夹路径,实际上是apk文件的路径,如/data/app/包名/base.apk

@NullableString[]splitResDirs,//针对一个app由多个apk组成(将原本一个apk切片为若干apk)时,每个子apk中的资源文件夹

@NullableString[]overlayDirs,

@NullableString[]libDirs,//app依赖的共享jar/apk路径

intdisplayId,

@NullableConfigurationoverrideConfig,

@NonNullCompatibilityInfocompatInfo,

@NullableClassLoaderclassLoader){

try{

Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,"ResourcesManager#getResources");

//以apk路径为参数创建key

finalResourcesKeykey=newResourcesKey(

resDir,

splitResDirs,

overlayDirs,

libDirs,

displayId,

overrideConfig!

=null?

newConfiguration(overrideConfig):

null,//Copy

compatInfo);

classLoader=classLoader!

=null?

classLoader:

ClassLoader.getSystemClassLoader();

returngetOrCreateResources(activityToken,key,classLoader);

}finally{

Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);

}

}

private@NonNullResourcesgetOrCreateResources(@NullableIBinderactivityToken,

@NonNullResourcesKeykey,@NonNullClassLoaderclassLoader){

synchronized(this){

//.......

if(activityToken!

=null){

//根据key从缓存里面找找ResourcesImpl

ResourcesImplresourcesImpl=findResourcesImplForKeyLocked(key);

if(resourcesImpl!

=null){

if(DEBUG){

Slog.d(TAG,"-usingexistingimpl="+resourcesImpl);

}

//如果resourcesImpl有那么根据resourcesImpl和classLoader从缓存找找Resource

returngetOrCreateResourcesForActivityLocked(activityToken,classLoader,

resourcesImpl);

}

//WewillcreatetheResourcesImplobjectoutsideofholdingthislock.

}else{

//.......

}

}

//Ifwe'rehere,wedidn'tfindasuitableResourcesImpltouse,socreateonenow.

//这个比较重要createResourcesImpl通过key

ResourcesImplresourcesImpl=createResourcesImpl(key);

synchronized(this){

//.......

finalResourcesresources;

if(activityToken!

=null){

//根据resourcesImpl和classLoader获取Resources

resources=getOrCreateResourcesForActivityLocked(activityToken,classLoader,

resourcesImpl);

}else{

resources=getOrCreateResourcesLocked(classLoader,resourcesImpl);

}

returnresources;

}

}

private@NonNullResourcesImplcreateResourcesImpl(@NonNullResourcesKeykey){

finalDisplayAdjustmentsdaj=newDisplayAdjustments(key.mOverrideConfiguration);

daj.setCompatibilityInfo(key.mCompatInfo);

//创建AssetManager

finalAssetManagerassets=createAssetManager(key);

finalDisplayMetricsdm=getDisplayMetrics(key.mDisplayId,daj);

finalConfigurationconfig=generateConfig(key,dm);

//根据AssetManager创建一个ResourcesImpl其实找资源是:

Resources->ResourcesImpl->AssetManager

finalResourcesImplimpl=newResourcesImpl(assets,dm,config,daj);

if(DEBUG){

Slog.d(TAG,"-creatingimpl="+impl+"withkey:

"+key);

}

returnimpl;

}

@VisibleForTesting

protected@NonNullAssetManagercreateAssetManager(@NonNullfinalResourcesKeykey){

//创建一个AssetManager对象

AssetManagerassets=newAssetManager();

//resDircanbenullifthe'android'packageiscreatinganewResourcesobject.

//Thisisfine,sinceeachAssetManagerautomaticallyloadsthe'android'package

//already.

//将app中的资源路径都加入到AssetManager对象中

if(key.mResDir!

=null){

//这个方法很重要,待会我们就是用它去加载皮肤的apk

if(assets.addAssetPath(key.mResDir)==0){

thrownewResources.NotFoundException("failedtoaddassetpath"+key.mResDir);

}

}

if(key.mLibDirs!

=null){

for(finalStringlibDir:

key.mLibDirs){

//仅仅选择共享依赖中的apk,因为jar中不会有资源文件

if(libDir.endsWith(".apk")){

//Avoidopeningfilesweknowdonothaveresources,

//likecode-ly.jarfiles.

if(assets.addAssetPathAsSharedLibrary(libDir)==0){

Log.w(TAG,"Assetpath'"+libDir+

"'doesnotexistorcontainsnoresources.");

}

}

}

}

returnassets;

}

/**

*GetsanexistingResourcesobjectiftheclassloaderandResourcesImplarethesame,

*otherwisecreatesanewResourcesobject.

*/

private@NonNullResourcesgetOrCreateResourcesLocked(@NonNullClassLoaderclassLoader,

@NonNullResourcesImplimpl){

//FindanexistingResourcesthathasthisResourcesImplset.

finalintrefCount=mResourceReferences.size();

for(inti=0;i

WeakReferenceweakResourceRef=mResourceReferences.get(i);

//从软引用缓存里面找一找

Resourcesresources=weakResourceRef.get();

if(resources!

=null&&

Objects.equals(resources.getClassLoader(),classLoader)&&

resources.getImpl()==impl){

if(DEBUG){

Slog.d(TAG,"-usingexistingref="+resources);

}

returnresources;

}

}

//CreateanewResourcesreferenceandusetheexistingResourcesImplobject.

//创建一个Resources,Resource有好几个构造方法,每个版本之间有稍微的差别

//有的版本是用的这一个构造方法Resources(assets,dm,config,compatInfo)

Resourcesresources=newResources(classLoader);

resources.setImpl(impl);

//加入缓存

mResourceReferences.add(newWeakReference<>(resources));

if(DEBUG){

Slog.d(TAG,"-creatingnewref="+resources);

Slog.d(TAG,"-settingref="+resources+"withimpl="+impl);

}

returnresources;

}

【看了这么多我们大致可以总结一下Resources的创建流程了:

-packageInfo.getResources(mainThread)->mainThread.getTopLevelResources()->mResourcesManager.getResources()->getOrCreateResources()这里首先会找ResourcesImpl缓存如果有则会获取Resource缓存返回;

-如果没有ResourcesImpl缓存,那么回去创建ResourcesImpl,ResourcesImpl的创建依赖于AssetManager;

-AssetManager的创建是通过直接实例化对象调用了一个addAssetPath(path)方法把应用的apk路径添加到AssetManager,addAssetPath()方法请看源码解释。

-创建好ResourcesImpl之后会再去缓存中找Resource如果没有,那么则会创建Resource并将其缓存,创建我们看到的源码是newResources(classLoader),resources.setImpl(impl)而不同的版本可能是newResources(assets,dm,config,compatInfo)具体请看6.0源码。

3.加载皮肤资源

  如果大致知道了资源的加载流程以及Resource的创建过程,现在我们要去加载另外一个apk中的资源就好办了,只需要自己创建一个Resource对象,下面这段代码网上找一大堆,如果分析过源码相信你会有更深的认识:

publicclassMainActivityextendsAppCompatActivity{

@Override

protectedvoidonCreate(BundlesavedInstanceState){

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

try{

ResourcessuperRes=getResources();

//创建AssetManager,但是不能直接new所以只能通过反射

AssetManagerassetManager=AssetManager.class.newInstance();

//反射获取addAssetPath方法

MethodaddAssetPathMethod=AssetManager.class.getDeclaredMethod("addAssetPath",String.class);

//皮肤包的路径:

本地sdcard/plugin.skin

StringskinPath=Environment.getExternalStorageDirectory().getAbsoluteFile()+File.separator+"plugin.skin";

//反射调用addAssetPath方法

addAssetPathMethod.invoke(assetManager,skinPath);

//创建皮肤的Resources对象

ResourcesskinResources=newResources(assetManager,superRes.getDisplayMetrics(),superRes.getConfiguration());

//通过资源名称,类型,包名获取Id

intbgId=skinResources.getIdentifier("main_bg","drawable","com.hc.skin");

DrawablebgDrawable=skinResources.getDrawable(bgId);

//设置背景

findViewById(R.id.activity_main).setBackgroundDrawable(bgDrawable);

}catch(Exceptione){

e.printStackTrace();

}

}

}

4.AssetManager创建过程分析

  下面的分析希望不要有强迫症,看不懂其实也不打紧因为涉及到JNI。

通过前面的分析可知,Android系统中实际对资源的管理是AssetManager类.每个Resources对象都会关联一个AssetManager对象,Resources将对资源的操作大多数委托给了AssetManager。

当然有些源码还有一层ResourcesImpl刚刚我们也看到了。

  另外还会存在一个native层的AssetManager对象与Java层的这个AssetManager对象相对应,而这个native层AssetManager对象在内存的地址存储在java层的AssetManager.mObject中。

所以在java层AssetManager的jni方法中可以快速找到它对应的native层的AssetManager对象。

4.1AssetManager的init()

/**

*CreateanewAssetManagercontainingonlythebasicsystemassets.

*Applicationswillnotgenerallyusethismethod,insteadretrievingthe

*appropriateassetmanagerwith{@linkResources#getAssets}.Notfor

*usebyapplications.

*{@hide}

*/

publicAssetManager(){

synchronized(this){

if(DEBUG_REFS){

mNumRefs=0;

incRefsLocked(this.hashCode());

}

init(false);

if(localLOGV)Log.v(TAG,"Newassetmanager:

"+thi

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

当前位置:首页 > 小学教育 > 小升初

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

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