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;iWeakReferenceweakResourceRef=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