热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android插件化之资源动态加载

这篇文章主要介绍了Android插件化之资源动态加载的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下

Android插件化之资源动态加载

一.概述

Android插件化的一个重要问题就是插件资源访问问题,先列出会面对的问题

1.如何加载插件资源
2.如何处理插件资源与宿主资源的处突:插件化资源问题要做到的效果是,如果我们要获取的资源在插件中找得到,则加载优先加载插件的,如果找不到,则到宿主资源中找。这样能做到动态更新的效果。
3.如何确保插件和宿主使用到的是被修改过的资源。

二.原理分析

在做一件事之前必须先弄清楚原理,所以,这里先要弄清楚Android的资源体系原理。

1.资源链

 

Context:一个apk里面其context的个数为application+Activity+service的总和,因为他们都是继承context的,然而context只是一个抽象类,其真正的实现类是ContextImpl,那。拿Activity来说,在Activity的启动流程中,会在ActivityThread的performLaunchActivity()方法中调用Activity的attach方法把ContextImp实例传给Activity(即赋值给Activity内的成员变量mBase)。


Resources:ContextImpl内有一个Resources的成员变量mResources,代表的是应用的资源,我们平时在调用getResources()方法获取到的是该Resources。

AssetManager:Resources内部的一个重要成员是AssetManager(mAssets),其指向的是apk的资源路径,资源的获取最终都是通过它来得到的。这里需要注意的是AssetManager并不是Resources独立持有的,也就是说系统在获取资源的时候不一定是通过Resources获取的,有时候是直接通过AssetManager来获取,比如TypedArray,之前就踩过这个坑。 

2.Android是如何构造一个应用的资源的,并且是如何传递给我们使用的,这个要讲的东西非常的多,可以看另一篇文章,这里主要讲资源插件化。

三.问题的解决方案

1.加载插件资源

资源的加载最后是通过AssetManager内的一个方法addAssetPath(String path)


该方法接收的参数是插件apk的路径,内部会调用native方法把插件apk对应的资源加载进来。然而该方法是hide的,我们不能直接调用,所有只能通过反射。

 

这样就成功构造出一个指向插件资源的AssetManager。当然这时候还不能使用,还要调用AssetManager的ensureStringBlocks()方法来初始化其内部参数,同样得使用反射。


2.如何解决插件资源与宿主资源的处突

如果使用到的资源,插件和宿主都同时存在,则使用插件的资源;如果使用到的资源只有插件有,则使用插件的;如果使用到的资源只有宿主有的,则使用宿主的。

AssetManager的addAssetPath()方法调用native层AssetManager对象的addAssetPath()方法,通过查看c++代码可以知道,该方法可以被调用多次,每次调用都会把对应资源添加起来,而后来添加的在使用资源是会被首先搜索到。可以怎么理解,C++层的AssetManager有一个存放资源的栈,每次调用addAssetPath()方法都会把资源对象压如栈,而在读取搜索资源时是从栈顶开始搜索,找不到就往下查。所以我们可以这样来处理AssetManager并得到Resources

 

其中dexPath2为宿主apk路径,dexPath为插件apk路径,superRes为宿主资源,resources为融合插件与宿主的资源。

3. 如何确保插件和宿主使用到的是被修改过的资源:
这是很重要的一步,之前我们已经成功获取资源并对其进行修饰,现在要做的是用它替换掉Android为我们生成的那个资源,这就是hook的思想。

使用到资源的地方归纳起来有两处,一处是在Java代码中通过Context.getResources获取,一处是在xml文件(如布局文件)里指定资源,其实xml文件里最终也是通过Context来获取资源的只不过是他一般获取的是Resources里的AssetManager。所以,我们可以在Context对象被创建后且还未使用时把它里面的Resources(mResources)替换掉。之前说过,整个应用的Context数目等于Application+Activity+Service的数目,Context会在这几个类创建对象的时候创建并添加进去。而这些行为都是在ActivityTHread和Instrumentation里做的。

以Activity为例,步骤如下:

a: Activity对象的创建是在ActivityThread里调用Instrumentation的newActivity方法

ActivityThread:

 

Instrumentation:

 

b: Context对象的创建是在ActivityThread里调用createBaseContextForActivity方法
ActivityThread: 


c: Activity绑定Context是在ActivityThread里调用Activity对象的attach方法,其中appContext就是上面创建的Context对象
ActivityThread:

 

d: Activity的onCreate()方法的回调是在ActivityThread里调用Instrumentation的callActivityOnCreate()方法
ActivityThread:

 

替换掉Activity里Context里的Resources最好要早,基于上面的观察,我们可以在调用Instrumentation的callActivityOnCreate()方法时把Resources替换掉。那么问题又来了,我们如何控制callActivityOnCreate()方法的执行,这里又得使用hook的思想了,即把ActivityThread里面的Instrumentation对象(mInstrumentation)给替换掉,同样得使用反射。步骤如下

a: 获取ActivityThread对象

ActivityThread里面有一个静态方法,该方法返回的是ActivityThread对象本身,所以我们可以调用该方法来获取ActivityTHread对象

 

然而ActivityThread是被hide的,所以得通过反射来处理,处理如下: 


b: 获取ActivityThread里的Instrumentation对象 


c: 构建我们自己的Instrumentation对象,并从写callActivityOnCreate方法
在callActivityOnCreate方法里要先获取当前Activity对象里的Context(mBase),再获取Context对象里的Resources(mResources)变量,在把mResources变量指向我们构造的Resources对象,做到移花接木。

 

MyInstrumentation:

 

d: 最后,使ActivityThread里面的mInstrumentation变量指向我们构建的MyInstrumentation对象。 


代码 


四.应用
资源动态加载的一个应用当然就是Android插件化方面的使用。还有一个应用就是换肤功能,只需要在在工程里添加这些代码(当然还要处理一些逻辑),然后用户想要给应用换皮肤,主题等,即可从后台下载插件apk,放在指定文件夹就可以关系应用的资源,起到换肤的效果。当然,资源动态加载还有其他应用方法,自己琢磨咯!!!

五.存在问题

1.兼容性问题,因为hook要使用反射,从而来获取系统hide或类的私有属性。把它们隐藏是因为它们的不稳定性,如果哪天Google觉得那个变量的名称起的不吉利给改了,那就报错了。当然解决方法还是有的,就是为不同的API写不同的代码。

2.R方面的问题。当我们添加了一个资源(如在String.xml里添加了一个String),则系统会为我们在R里面为该资源生成一个int型的id与之对应,使用的时候是根据该id找到对应的资源。资源id是按照资源名称的字典顺序来递增的。拿String来说。
假如我们的String.xml里声明了名称为za,zb的资源

 

则会在R里面生成相应的id 


基于上面的观察,我们会发现一个问题:举个例子
宿主资源情况为:存在za(id=0x7f060004)  zb(id=0x7f060005)
插件资源情况为:存在za(id=0x7f060004)  zab(id=0x7f060005)   ab(0x7f060006)

这时候在宿主里获取资源zb,则根据上面所说,会根据id=0x7f060005先存在插件资源,这时候得到的是zab而不是zb,这就出错了。
解决方案有,在插件中如果有添加新的资源,则其命名要安装字典排序在原有的资源下递增。当然也有其他方案,自己琢磨吧。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 本文介绍了使用kotlin实现动画效果的方法,包括上下移动、放大缩小、旋转等功能。通过代码示例演示了如何使用ObjectAnimator和AnimatorSet来实现动画效果,并提供了实现抖动效果的代码。同时还介绍了如何使用translationY和translationX来实现上下和左右移动的效果。最后还提供了一个anim_small.xml文件的代码示例,可以用来实现放大缩小的效果。 ... [详细]
  • android listview OnItemClickListener失效原因
    最近在做listview时发现OnItemClickListener失效的问题,经过查找发现是因为button的原因。不仅listitem中存在button会影响OnItemClickListener事件的失效,还会导致单击后listview每个item的背景改变,使得item中的所有有关焦点的事件都失效。本文给出了一个范例来说明这种情况,并提供了解决方法。 ... [详细]
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文讲述了如何通过代码在Android中更改Recycler视图项的背景颜色。通过在onBindViewHolder方法中设置条件判断,可以实现根据条件改变背景颜色的效果。同时,还介绍了如何修改底部边框颜色以及提供了RecyclerView Fragment layout.xml和项目布局文件的示例代码。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • ZSI.generate.Wsdl2PythonError: unsupported local simpleType restriction ... [详细]
  • XML介绍与使用的概述及标签规则
    本文介绍了XML的基本概念和用途,包括XML的可扩展性和标签的自定义特性。同时还详细解释了XML标签的规则,包括标签的尖括号和合法标识符的组成,标签必须成对出现的原则以及特殊标签的使用方法。通过本文的阅读,读者可以对XML的基本知识有一个全面的了解。 ... [详细]
  • Google Play推出全新的应用内评价API,帮助开发者获取更多优质用户反馈。用户每天在Google Play上发表数百万条评论,这有助于开发者了解用户喜好和改进需求。开发者可以选择在适当的时间请求用户撰写评论,以获得全面而有用的反馈。全新应用内评价功能让用户无需返回应用详情页面即可发表评论,提升用户体验。 ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • 本文介绍了设计师伊振华受邀参与沈阳市智慧城市运行管理中心项目的整体设计,并以数字赋能和创新驱动高质量发展的理念,建设了集成、智慧、高效的一体化城市综合管理平台,促进了城市的数字化转型。该中心被称为当代城市的智能心脏,为沈阳市的智慧城市建设做出了重要贡献。 ... [详细]
  • 本文介绍了数据库的存储结构及其重要性,强调了关系数据库范例中将逻辑存储与物理存储分开的必要性。通过逻辑结构和物理结构的分离,可以实现对物理存储的重新组织和数据库的迁移,而应用程序不会察觉到任何更改。文章还展示了Oracle数据库的逻辑结构和物理结构,并介绍了表空间的概念和作用。 ... [详细]
author-avatar
江西小毒i哈
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有