最新消息:欢迎访问Android开发中文站!商务联系微信:loading_in

太阳城金沙娱乐:Android APK资源加载流程

凤凰彩票官网 loading 207浏览 0评论

概述

本文地址:http://789.jbs48.com/10197.html
文章摘要:太阳城金沙娱乐,禅衣长剑之上正是御剑而来凤凰彩票官网、竞猜盘口赌场、澳门大发888赌场一手接过战书仙府飞了出来。

我们在Activity中访问资源(图片,字符串,颜色等)是非常方便的,只需要getResources()获取一个Resources对象,然后就可以访问各种资源了,那这些资源到底是怎么被加载的呢?下面我们就分析一下资源加载机制

App启动流程

首先我们回顾一下App启动流程,还不了解的可以看我之前写的这篇文章

  • 首先是点击App图标,此时是运行在Launcher进程,通过ActivityManagerServiceBinder IPC的形式向system_server进程发起startActivity的请求
  • system_server进程接收到请求后,通过Process.start方法向zygote进程发送创建进程的请求
  • zygote进程fork出新的子进程,即App进程
  • 然后进入ActivityThread.main方法中,这时运行在App进程中,通过ActivityManagerServiceBinder IPC的形式向system_server进程发起attachApplication请求
  • system_server接收到请求后,进行一些列准备工作后,再通过Binder IPC向App进程发送scheduleLaunchActivity请求
  • App进程binder线程(ApplicationThread)收到请求后,通过Handler向主线程发送LAUNCH_ACTIVITY消息
  • 主线程收到Message后,通过反射机制创建目标Activity,并回调Activity的onCreate

首先我们看第四步,attachApplication方法,最终会调用thread#bindApplication然后调用ActivityThread#handleBindApplication方法,我们从这个方法开始看

ActivityThread#handleBindApplication
private?void?handleBindApplication(AppBindData?data)?{
????...
????final?InstrumentationInfo?ii;
????...
????//?创建?mInstrumentation?实例
????if?(ii?!=?null)?{
????????final?ApplicationInfo?instrApp?=?new?ApplicationInfo();
????????ii.copyTo(instrApp);
????????instrApp.initForUser(UserHandle.myUserId());
????????final?LoadedApk?pi?=?getPackageInfo(instrApp,?data.compatInfo,
????????????????appContext.getClassLoader(),?false,?true,?false);
????????final?ContextImpl?instrContext?=?ContextImpl.createAppContext(this,?pi);

????????try?{
????????????final?ClassLoader?cl?=?instrContext.getClassLoader();
????????????mInstrumentation?=?(Instrumentation)
????????????????cl.loadClass(data.instrumentationName.getClassName()).newInstance();
????????}?catch?(Exception?e)?{
????????????...
????????}
????????...
????}?else?{
????????mInstrumentation?=?new?Instrumentation();
????}
????...
????Application?app;
????...
????//?创建?Application?实例
????try?{
????????...
????????app?=?data.info.makeApplication(data.restrictedBackupMode,?null);
????????mInitialApplication?=?app;
????????...
????????try?{
????????????mInstrumentation.callApplicationOnCreate(app);
????????}?catch?(Exception?e)?{
????????????...
????????}
????}?finally?{
????????...
????}
????...
}



//?http://789.jbs48.com/937/8.1.0_r33/xref/frameworks/base/core/java/android/app/LoadedApk.java#959
public?Application?makeApplication(boolean?forceDefaultAppClass,
????????Instrumentation?instrumentation)?{
????...

????try?{
????????...
????????//注释1
????????ContextImpl?appContext?=?ContextImpl.createAppContext(mActivityThread,?this);
????????app?=?mActivityThread.mInstrumentation.newApplication(
????????????????cl,?appClass,?appContext);
????????appContext.setOuterContext(app);
????}?catch?(Exception?e)?{
????????...
????}
????...
????return?app;
}

?static?ContextImpl?createAppContext(ActivityThread?mainThread,?LoadedApk?packageInfo)?{
????????if?(packageInfo?==?null)?throw?new?IllegalArgumentException("packageInfo");
????????return?new?ContextImpl(null,?mainThread,
????????????????packageInfo,?null,?null,?0,?null,?null,?Display.INVALID_DISPLAY);
????}

这个方法我们只留下了最核心的内容,太阳城金沙娱乐:我们看下注释1, ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);这个方法会直接new一个新的ContextImpl

????private?ContextImpl(ContextImpl?container,?ActivityThread?mainThread,
????????????LoadedApk?packageInfo,?IBinder?activityToken,?UserHandle?user,?int?flags,
????????????Display?display,?Configuration?overrideConfiguration,?int?createDisplayWithId)?{
??????????....
????????//LoadApk赋值
????????mPackageInfo?=?packageInfo;
????????mResourcesManager?=?ResourcesManager.getInstance();

???????...
????????//通过LoadApk.getResources获取Resources对象
????????Resources?resources?=?packageInfo.getResources(mainThread);
????????if?(resources?!=?null)?{
????????????if?(displayId?!=?Display.DEFAULT_DISPLAY
????????????????????||?overrideConfiguration?!=?null
????????????????????||?(compatInfo?!=?null?&&?compatInfo.applicationScale
????????????????????????????!=?resources.getCompatibilityInfo().applicationScale))?{

????????????????if?(container?!=?null)?{
????????????????????//?This?is?a?nested?Context,?so?it?can't?be?a?base?Activity?context.
????????????????????//?Just?create?a?regular?Resources?object?associated?with?the?Activity.
????????????????????resources?=?mResourcesManager.getResources(
????????????????????????????activityToken,
????????????????????????????packageInfo.getResDir(),
????????????????????????????packageInfo.getSplitResDirs(),
????????????????????????????packageInfo.getOverlayDirs(),
????????????????????????????packageInfo.getApplicationInfo().sharedLibraryFiles,
????????????????????????????displayId,
????????????????????????????overrideConfiguration,
????????????????????????????compatInfo,
????????????????????????????packageInfo.getClassLoader());
????????????????}?else?{
????????????????????//?This?is?not?a?nested?Context,?so?it?must?be?the?root?Activity?context.
????????????????????//?All?other?nested?Contexts?will?inherit?the?configuration?set?here.
????????????????????resources?=?mResourcesManager.createBaseActivityResources(
????????????????????????????activityToken,
????????????????????????????packageInfo.getResDir(),
????????????????????????????packageInfo.getSplitResDirs(),
????????????????????????????packageInfo.getOverlayDirs(),
????????????????????????????packageInfo.getApplicationInfo().sharedLibraryFiles,
????????????????????????????displayId,
????????????????????????????overrideConfiguration,
????????????????????????????compatInfo,
????????????????????????????packageInfo.getClassLoader());
????????????????}
????????????}
????????}
????????//为mResources变量赋值
????????mResources?=?resources;

???????...
????}

packageInfo.getResources,packageInfo是LoadApk类型的,我们看下这个方法

LoadApk#getResources
?public?Resources?getResources(ActivityThread?mainThread)?{
????????if?(mResources?==?null)?{
????????????mResources?=?mainThread.getTopLevelResources(mResDir,?mSplitResDirs,?mOverlayDirs,
????????????????????mApplicationInfo.sharedLibraryFiles,?Display.DEFAULT_DISPLAY,?this);
????????}
????????return?mResources;
????}

其中调用了ActivityThread的getTopLevelResources方法,我们继续看一下

ActivityThread#getTopLevelResources
?Resources?getTopLevelResources(String?resDir,?String[]?splitResDirs,?String[]?overlayDirs,
????????????String[]?libDirs,?int?displayId,?LoadedApk?pkgInfo)?{
????????return?mResourcesManager.getResources(null,?resDir,?splitResDirs,?overlayDirs,?libDirs,
????????????????displayId,?null,?pkgInfo.getCompatibilityInfo(),?pkgInfo.getClassLoader());
????}

继续调用了mResourcesManager的getResources方法,我么继续跟下去

ResourcesManager#getResources
??public?@NonNull?Resources?getResources(@Nullable?IBinder?activityToken,
????????????@Nullable?String?resDir,
????????????@Nullable?String[]?splitResDirs,
????????????@Nullable?String[]?overlayDirs,
????????????@Nullable?String[]?libDirs,
????????????int?displayId,
????????????@Nullable?Configuration?overrideConfig,
????????????@NonNull?CompatibilityInfo?compatInfo,
????????????@Nullable?ClassLoader?classLoader)?{
????????try?{
????????????Trace.traceBegin(Trace.TRACE_TAG_RESOURCES,?"ResourcesManager#getResources");
????????????final?ResourcesKey?key?=?new?ResourcesKey(
????????????????????resDir,
????????????????????splitResDirs,
????????????????????overlayDirs,
????????????????????libDirs,
????????????????????displayId,
????????????????????overrideConfig?!=?null???new?Configuration(overrideConfig)?:?null,?//?Copy
????????????????????compatInfo);
????????????classLoader?=?classLoader?!=?null???classLoader?:?ClassLoader.getSystemClassLoader();
????????????return?getOrCreateResources(activityToken,?key,?classLoader);
????????}?finally?{
????????????Trace.traceEnd(Trace.TRACE_TAG_RESOURCES);
????????}
????}


??private?@NonNull?Resources?getOrCreateResources(@Nullable?IBinder?activityToken,
????????????@NonNull?ResourcesKey?key,?@NonNull?ClassLoader?classLoader)?{
????????...

????????//?创建ResourcesImpl
????????ResourcesImpl?resourcesImpl?=?createResourcesImpl(key);
????????....

????????????final?Resources?resources;
????????????if?(activityToken?!=?null)?{
????????????????resources?=?getOrCreateResourcesForActivityLocked(activityToken,?classLoader,
????????????????????????resourcesImpl);
????????????}?else?{
????????????????resources?=?getOrCreateResourcesLocked(classLoader,?resourcesImpl);
????????????}
????????????return?resources;
????????}
????}


?private?@NonNull?ResourcesImpl?createResourcesImpl(@NonNull?ResourcesKey?key)?{
????????final?DisplayAdjustments?daj?=?new?DisplayAdjustments(key.mOverrideConfiguration);
????????daj.setCompatibilityInfo(key.mCompatInfo);

????????final?AssetManager?assets?=?createAssetManager(key);
????????final?DisplayMetrics?dm?=?getDisplayMetrics(key.mDisplayId,?daj);
????????final?Configuration?config?=?generateConfig(key,?dm);
????????final?ResourcesImpl?impl?=?new?ResourcesImpl(assets,?dm,?config,?daj);
????????if?(DEBUG)?{
????????????Slog.d(TAG,?"-?creating?impl="?+?impl?+?"?with?key:?"?+?key);
????????}
????????return?impl;
????}


??protected?@NonNull?AssetManager?createAssetManager(@NonNull?final?ResourcesKey?key)?{
????????AssetManager?assets?=?new?AssetManager();

????????//?resDir?can?be?null?if?the?'android'?package?is?creating?a?new?Resources?object.
????????//?This?is?fine,?since?each?AssetManager?automatically?loads?the?'android'?package
????????//?already.
????????if?(key.mResDir?!=?null)?{
????????????if?(assets.addAssetPath(key.mResDir)?==?0)?{
????????????????throw?new?Resources.NotFoundException("failed?to?add?asset?path?"?+?key.mResDir);
????????????}
????????}

????????if?(key.mSplitResDirs?!=?null)?{
????????????for?(final?String?splitResDir?:?key.mSplitResDirs)?{
????????????????if?(assets.addAssetPath(splitResDir)?==?0)?{
????????????????????throw?new?Resources.NotFoundException(
????????????????????????????"failed?to?add?split?asset?path?"?+?splitResDir);
????????????????}
????????????}
????????}

????????if?(key.mOverlayDirs?!=?null)?{
????????????for?(final?String?idmapPath?:?key.mOverlayDirs)?{
????????????????assets.addOverlayPath(idmapPath);
????????????}
????????}

????????if?(key.mLibDirs?!=?null)?{
????????????for?(final?String?libDir?:?key.mLibDirs)?{
????????????????if?(libDir.endsWith(".apk"))?{
????????????????????//?Avoid?opening?files?we?know?do?not?have?resources,
????????????????????//?like?code-only?.jar?files.
????????????????????if?(assets.addAssetPathAsSharedLibrary(libDir)?==?0)?{
????????????????????????Log.w(TAG,?"Asset?path?'"?+?libDir?+
????????????????????????????????"'?does?not?exist?or?contains?no?resources.");
????????????????????}
????????????????}
????????????}
????????}
????????return?assets;
????}


?????private?@NonNull?Resources?getOrCreateResourcesLocked(@NonNull?ClassLoader?classLoader,
????????????@NonNull?ResourcesImpl?impl)?{
????????//?Find?an?existing?Resources?that?has?this?ResourcesImpl?set.
????????final?int?refCount?=?mResourceReferences.size();
????????for?(int?i?=?0;?i?<?refCount;?i++)?{
????????????WeakReference<Resources>?weakResourceRef?=?mResourceReferences.get(i);
????????????Resources?resources?=?weakResourceRef.get();
????????????if?(resources?!=?null?&&
????????????????????Objects.equals(resources.getClassLoader(),?classLoader)?&&
????????????????????resources.getImpl()?==?impl)?{
????????????????if?(DEBUG)?{
????????????????????Slog.d(TAG,?"-?using?existing?ref="?+?resources);
????????????????}
????????????????return?resources;
????????????}
????????}

????????//?Create?a?new?Resources?reference?and?use?the?existing?ResourcesImpl?object.
????????Resources?resources?=?new?Resources(classLoader);
????????resources.setImpl(impl);
????????mResourceReferences.add(new?WeakReference<>(resources));
????????if?(DEBUG)?{
????????????Slog.d(TAG,?"-?creating?new?ref="?+?resources);
????????????Slog.d(TAG,?"-?setting?ref="?+?resources?+?"?with?impl="?+?impl);
????????}
????????return?resources;
????}

首先调用createResourcesImpl,创建ResourcesImpl,我们看下这个方法内部创建了AssetManager assets = new AssetManager();,然后调用assets.addAssetPath添加资源地址,最后返回final ResourcesImpl impl = new ResourcesImpl(assets, dm, config, daj);,最后查看是否有缓存,如果有则返回缓存的resources,如果没有就重新构建Resources,然后返回
平时使用

?Resources?resources?=?getResources();
?resources.getString();
?resources.getAssets();
?resources.getColor();
?resources.getDrawable()

这个就是我们平时使用的代码,通过resources获取资源,其中getResources方法返回的就是上方ContextImpl创建的mResources变量,然后我们分析一下getString方法的实现

##?Resources.java

?public?String?getString(@StringRes?int?id)?throws?NotFoundException?{
????????return?getText(id).toString();
????}

??@NonNull?public?CharSequence?getText(@StringRes?int?id)?throws?NotFoundException?{
????????CharSequence?res?=?mResourcesImpl.getAssets().getResourceText(id);
????????if?(res?!=?null)?{
????????????return?res;
????????}
????????throw?new?NotFoundException("String?resource?ID?#0x"
????????????????+?Integer.toHexString(id));
????}

最后交给了mResourcesImpl.getAssets().getResourceText(id);,我们继续看下这个方法

##?ResourcesImpl.java

public?AssetManager?getAssets()?{
????????return?mAssets;
????}

?public?ResourcesImpl(@NonNull?AssetManager?assets,?@Nullable?DisplayMetrics?metrics,
????????????@Nullable?Configuration?config,?@NonNull?DisplayAdjustments?displayAdjustments)?{
????????mAssets?=?assets;
????????mMetrics.setToDefaults();
????????mDisplayAdjustments?=?displayAdjustments;
????????mConfiguration.setToDefaults();
????????updateConfiguration(config,?metrics,?displayAdjustments.getCompatibilityInfo());
????}

最后交给了mAssets变量处理,mAssets变量就是创建ResourcesImpl时传入的AssetsManager,其实最后就是委托给了AssetsManager去处理

总结

我们发现Apk的资源是通过AssetManager.addAssetPath方法来完成加载,那么我们就可以通过反射构建自己的AssetManager对象,然后把调用addAssetPath加载自己的资源,然后把自己构建的AssetManager通过反射设置给mAssets变量,这样下次加载资源就是用的我们AssetManager,也就是用的我们更换后的资源,这个就是热修复的资源修复的原理

参考:

作者:renxhui
链接:http://www.5i8.133407.com/post/5de9f98f518825127d107638

转载请注明:Android开发中文站 » Android APK资源加载流程

您必须 登录 才能发表评论!

宝马线上真人赌场 菲律宾申博怎么注册 澳门网上娱乐官网 秒速快三计划群 真钱赌场公司
线上赌博地址 电子捕鱼娱乐场 马可波罗真人星级百家乐 皇冠注册官网 澳门美高梅真人21点
宝马在线娱乐网址 AG软件赌场 拉斯维加斯娱乐注册 网上官方赌博 菲律宾网上娱乐
澳门威尼斯人真人麻雀排九 澳门银河真人赌场网址 银河官方彩票 海燕博彩论坛 澳门贵宾会招商总代