基于之前对插件化的了解,大部分的插件对于资源都是合并式的,通过addAssetPath将插件的资源加入到assetManager中,这种方式会带来一个问题:resource id冲突。当然目前已经有方案去解决:aapt或者修改arsc文件。
VirtualApp中可以安装任意的第三方APK,如果采用合并式资源加载方案,那肯定也会有resource id冲突问题,但上面说的解决方案都需要第三方APK去配合修改,明显不能满足任意安装。
VirtualApp采用的是独立式资源加载方案,这样就不会有resource id冲突的问题。
在分析VirtualApp是怎么做到独立式资源加载前,需要先了解一下应用获取resources大概的流程。在自定义Application中,我们可以通过getResources去获取当前的资源,下面看看getResources调用流程:

最终会走到ContextWrapper中:

    public Resources getResources() {return mBase.getResources();}

发现是从mBase中拿到resources,那mBase从哪里来?也是在ContextWrapper类中:

 /*** Set the base context for this ContextWrapper.  All calls will then be* delegated to the base context.  Throws* IllegalStateException if a base context has already been set.* * @param base The new base context for this wrapper.*/protected void attachBaseContext(Context base) {if (mBase != null) {throw new IllegalStateException("Base context already set");}mBase = base;}

ContextWrapper的attachBaseContext方法是在哪里被调用的呢?base context几时被创建的?

具体调用流程分析参考:Android应用程序窗口(Activity)的运行上下文环境(Context)的创建过程分析
基于上面调用时序图,看看上述几个方法的实现
LoadedApk.makeApplication方法:

public Application makeApplication(boolean forceDefaultAppClass,Instrumentation instrumentation) {ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);app = mActivityThread.mInstrumentation.newApplication(cl, appClass, appContext);}

Instrumentation newApplication方法:

 public Application newApplication(ClassLoader cl, String className, Context context)throws InstantiationException, IllegalAccessException, ClassNotFoundException {return newApplication(cl.loadClass(className), context);}static public Application newApplication(Class<?> clazz, Context context)throws InstantiationException, IllegalAccessException, ClassNotFoundException {Application app = (Application)clazz.newInstance();app.attach(context);return app;}

Application attach方法:

/* package */ final void attach(Context context) {attachBaseContext(context);mLoadedApk = ContextImpl.getImpl(context).mPackageInfo;}

最终发现原来ContextWrapper 中的mBase是在LoadedApk.makeApplication创建的,

ContextImpl appContext = ContextImpl.createAppContext(mActivityThread, this);

attachBaseContext方法中的base参数其实ContextImpl的实例。
现在转移到分析ContextImpl类
看看ContextImpl.createAppContext方法:

  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);}private ContextImpl(ContextImpl container, ActivityThread mainThread,LoadedApk packageInfo, IBinder activityToken, UserHandle user, int flags,Display display, Configuration overrideConfiguration, int createDisplayWithId) {mOuterContext = this;Resources resources = packageInfo.getResources(mainThread);mResources = resources;}public Resources getResources() {return mResources;}

发现resources来自packageInfo,packageInfo是LoadedApk的一个实例,
packageInfo是在ActivityThread的handleBindApplication方法中被创建的

 private void handleBindApplication(AppBindData data) {data.info = getPackageInfoNoCheck(data.appInfo, data.compatInfo);}public final LoadedApk getPackageInfoNoCheck(ApplicationInfo ai,CompatibilityInfo compatInfo) {return getPackageInfo(ai, compatInfo, null, false, true, false);}

来看看LoadedApk中的getResources方法是怎么实现的:

    public Resources getResources(ActivityThread mainThread) {if (mResources == null) {mResources = mainThread.getTopLevelResources(mResDir, mSplitResDirs, mOverlayDirs,mApplicationInfo.sharedLibraryFiles, Display.DEFAULT_DISPLAY, this);}return mResources;}public LoadedApk(ActivityThread activityThread, ApplicationInfo aInfo,CompatibilityInfo compatInfo, ClassLoader baseLoader,boolean securityViolation, boolean includeCode, boolean registerPackage) {setApplicationInfo(aInfo);}private void setApplicationInfo(ApplicationInfo aInfo) {final int myUid = Process.myUid();mAppDir = aInfo.sourceDir;mResDir = aInfo.uid == myUid ? aInfo.sourceDir : aInfo.publicSourceDir;mSplitAppDirs = aInfo.splitSourceDirs;mSplitResDirs = aInfo.uid == myUid ? aInfo.splitSourceDirs : aInfo.splitPublicSourceDirs;mOverlayDirs = aInfo.resourceDirs;mSharedLibraries = aInfo.sharedLibraryFiles;mDataDir = aInfo.dataDir;mLibDir = aInfo.nativeLibraryDir;}

最终resources是调用ActivityThread.getTopLevelResources方法创建的:

    /*** Creates the top level resources for the given package. Will return an existing* Resources if one has already been created.*/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());}

ResourceManager根据APK的相关路径信息,读取到资源,最终生成Resources。

小结一下:Application调用getResources方法,最终获取到的是LoadedApk对象中根据APK相关路径创建的Resources对象。LoadedApk对象最早的创建是在ActivityThread.handleBindApplication时。
handleBindApplication是在绑定自定义Application时被调用。

分析了半天和VirtualApp的独立式资源加载方案有啥关系???
看段VirtualApp的代码

private AppBindData mBoundApplication;private final class AppBindData {String processName;ApplicationInfo appInfo;List<ProviderInfo> providers;Object info;}private void bindApplicationNoCheck(String packageName, String processName, ConditionVariable lock) {AppBindData data = new AppBindData();mBoundApplication = data;Context context = createPackageContext(data.appInfo.packageName);mBoundApplication.info = ContextImpl.mPackageInfo.get(context);mInitialApplication = LoadedApk.makeApplication.call(data.info, false, null);mirror.android.app.ActivityThread.mInitialApplication.set(mainThread, mInitialApplication);}

VirtualApp在运行第三方App时,会执行bindApplicationNoCheck去将第三方自定义Application绑定起来,

mInitialApplication = LoadedApk.makeApplication.call(data.info, false, null);

dataInfo.makeApplication,这里就是开始调用创建Application, 那data.info几时被创建的?继续看上面贴出来的bindApplicationNoCheck,

 mBoundApplication = data;Context context = createPackageContext(data.appInfo.packageName);mBoundApplication.info = ContextImpl.mPackageInfo.get(context);

data.info 是 mBoundApplication.info,  mBoundApplication.info是从context读取出来的,而context是调用createPackageContext方法,这个方法参数是packageName->第三方应用包名。

   @Overridepublic Context createPackageContext(String packageName, int flags)throws NameNotFoundException {return createPackageContextAsUser(packageName, flags,mUser != null ? mUser : Process.myUserHandle());}@Overridepublic Context createPackageContextAsUser(String packageName, int flags, UserHandle user)throws NameNotFoundException {LoadedApk pi = mMainThread.getPackageInfo(packageName, mResources.getCompatibilityInfo(),flags | CONTEXT_REGISTER_PACKAGE, user.getIdentifier());if (pi != null) {ContextImpl c = new ContextImpl(this, mMainThread, pi, mActivityToken,user, flags, mDisplay, null, Display.INVALID_DISPLAY);if (c.mResources != null) {return c;}}}

context其实是ContextImpl的一个实例,在创建context时,LoadedApk对象也被创建出来。

小结:VirtualApp在bindApplication时会先根据第三方应用包名创建context,在创建context时,LoadedApk对象也被创建出来,然后基于创建的LoadedApk对象执行makeApplication方法,又回到前文分析如和attachBaseContext。

总结: VirtualApp通过调用createPackageContext创建ConextImpl实例,在创建ConextImpl实例时创建LoadedApk实例,LoadedApk.getResources根据第三方应用相关信息生成Resources实例。自定义Application通过loadedApk.getResources去查找资源。

VirtualApp - 独立式 Resource hook相关推荐

  1. 独立式环境与宿主式环境————《标准C语言指南》读书笔记01

    独立式环境与宿主式环境----<标准C语言指南>读书笔记01 在编写和转换一个C程序之前,需要考虑它的执行环境,因为这关系到源文件的内容(程序应当如何编写),也关系到转换后的程序能否正常执 ...

  2. 独立式键盘的编程方法 按键的去抖动原理和基本方法

    独立式键盘的编程方法 按键的去抖动原理和基本方法 A P3.2  开始 按此键则灯开始流动(由左向右) B P3.3  停止 按此键则停止流动 所有灯为灭 C P3.4  向左 按此键则灯反向流动 由 ...

  3. 系统无法执行指定的程序。_使用Rust编写操作系统(一):独立式可执行程序

    创建一个不连接标准库的Rust可执行文件,将是我们迈出的第一步.无需底层操作系统的支撑,这将能让在裸机(bare metal)上运行Rust代码成为现实. 简介 要编写一个操作系统内核,我们需要不基于 ...

  4. 2021-2025年中国独立式梳妆浴缸行业市场供需与战略研究报告

    独立式梳妆浴缸市场的企业竞争态势 该报告涉及的主要国际市场参与者有Doctorgimo.Shor-Line.Surgicalory.Groomer's Best.Master Equipment.Pe ...

  5. FF300R08W2P2B11A 汽车用EasyPACK 模块 2 个独立式

    FF300R08W2汽车用EasyPACK 2B EDT2模块紧凑.灵活,一体式隔离设计,适用于混合动力和电动汽车的主逆变器.EDT2 IGBT允许750V的阻断电压和300A的IcN.FF300R0 ...

  6. 可燃气体泄漏监控报警系统_独立式可燃气体泄漏探测报警系统软件整体解决方案

    可燃气体泄漏监控报警系统_独立式可燃气体泄漏探测报警系统软件整体解决方案 华盛恒辉解决方案 华盛恒辉可燃气体泄漏监控系统软件开发可以来这里,这个首肌开始是义乌扒,中间是一义三散,最后一个是思奇思思,按 ...

  7. VirtualHook: 基于VirtualApp的Java代码hook工具

    VirtualHook: 基于VirtualApp的Java代码hook工具 nabla 2017-4-1 10:16  21442 转自 https://bbs.pediy.com/thread-2 ...

  8. 一个案例带你了解独立式键盘设计原理

    单片机与4个独立按键S1~S4以及8只LED指示灯构成一个独立式键盘系统.4个按键接在P1.0~P1.3引脚,P3口接8只LED指示灯,控制8只LED指示灯的亮和灭,原理图如下.当按下S1按键时,P3 ...

  9. 独立式键盘实验c语言,单片机设计报告—独立式键盘控制的4级流水灯.doc

    单片机设计报告-独立式键盘控制的4级流水灯 单片机结题报告 独立式键盘控制的4级流水灯 班 级 姓 名 吴泽宇 学 号 2008405340 一.任务目的: 通过采用单片机制作一个独立式键盘控制的4级 ...

最新文章

  1. 10.02 T3 打表找递推式+十进制快速幂 九校联考凉心模拟DAY1T1
  2. LInux命令行参数
  3. RHEL修改最大文件打开数,关于epoll socket Too many open files问题的解决
  4. Mybatis参数传递及返回类型
  5. 51单片机auxr寄存器_51—52系列单片机特殊功能寄存器一览表
  6. 使用pymongo需要手动关闭MongoDB Connection吗?
  7. centos安装stress安装失败_Linux压力测试工具Stress的使用指南
  8. sas 服务器版安装文件,SAS软件各个版本,包括服务器版本的切磋了解
  9. C语言32个关键字详解
  10. xmind转excel脚本(简化版)
  11. 冒泡排序程序java_冒泡排序Java程序
  12. CentOS8环境中使用Graghics Magick将CMYK图片生成适用于chrome、Edge、QQ浏览器浏览的sRGB图片
  13. DHCP server和DHCP relay配置
  14. 推荐几张系统维护光盘
  15. 微信,微博,qq账号合并的大工程啊
  16. Binary Exponential Backoff
  17. 7 轮面试后,还是挂了 | Google 中国面经分享
  18. Sublime Text 3 使用心得(转)
  19. 严重: Allocate exception for servlet CustomerServlet java.lang.ClassNotFoundException: cn.itcast.cstm.
  20. Docker容器域名解析失败

热门文章

  1. liunx下如何使用unzip 如何压缩文件
  2. 正版软件的价格(微软系列)
  3. vba 保存word里面的图片_1分钟批量处理100张图片,有Word在
  4. Linux的PS修改名称,ps命令的10个例子
  5. 山寨版鸟巢体育馆图片网络曝光(组图)
  6. 怎么搭建一款合同管理系统
  7. 概要设计怎么写?全面而具体的描述
  8. 轧钢测径仪软件分析图 钢材质量直观体现
  9. Linux实验整理——vi编辑器使用、文件显示和处理命令
  10. 小发猫物联网平台搭建与应用模型