深入理解Android插件化技术

  • 时间:
  • 浏览:3

下面介绍如可通过hook的最好的办法 启动插件中的Activity,并能 防止以下一一一兩个多现象:

上图列出的是启动一一一兩个多Activity的主要过程,具体步骤如下:

合并式将会AssetManager中加入了所有插件和主工程的路径,因此生成的Resource都并能 同时访问插件和主工程的资源。因此将会主工程和各个插件总要独立编译的,生成的资源id会占据 相同的情况报告,在访问总要产生资源冲突。

插件化技术都并能 说是Android高级工程师所并能 具备的技能之一,从2012年插件化概念的提出(Android版本),到2016年插件化的百花争艳,都并能 说,插件化技术引领着Android技术的进步。本篇文章转载自腾讯bugly,人太好写得不错,转载分享给亲戚亲戚朋友。

插件和主工程的互相调用涉及到以下一一一兩个多现象:

插件调用主工程在构造插件的ClassLoader总要传入主工程的ClassLoader作为父加载器,什么都有有有插件是都并能 直接都并能 通过类名引用主工程的类。

Android系统通过Resource对象加载资源,下面代码展示了该对象的生成过程。

下面是比较出名的几次开源的插件化框架,按照再次出先的时间排序。研究它们的实现原理,都并能 大致看出插件化技术的发展,根据实现原理都并能 将这几次框架划分成了三代。

做完以上工作后,则都并能 在插件的Activity放在心的使用setContentView,inflater等最好的办法 加载布局了。

插件Activity构发明人人来后,为了并能保证其正常运行并能 做些额外的工作。

经过上述步骤后,便实现了插件Activity的启动,因此该插件Activity中并不并能 那些额外的防止,和常规的Activity一样。那现象来了,事先的onResume,onStop等生命周期如可办呢?答案是所有和Activity相关的生命周期函数,系统总要调用插件中的Activity。意味着在于AMS在防止Activity时,通过一一一兩个多token表示具体Activity对象,而这种 token正是和启动Activity时创建的对象对应的,而这种 Activity被亲戚亲戚朋友替上加了插件中的Activity,什么都有有有事先AMS的所有调用总要传给插件中的Activity。

ClassLoader调用loadClass最好的办法 加载类,代码如下:

VAInstrumentation类重写了execStartActivity最好的办法 ,相关代码如下:

四大组件中Activity的支持是最简化的,其他组件的实现原理要简单什么都有有有,简要概括如下:

人太好,关于类加载更完整性的内容,笔者也深入剖析过,都并能 查看下面的链接:类加载机制详解

通常亲戚亲戚朋友通过Context对象访问资源,光创建出Resource对象还缺陷,因此还并能 其他额外的工作。 对资源访问的不同实现最好的办法 只是需要 不同的额外工作。以VirtualAPK的防止最好的办法 为例。

第一步:创建Resource

Android开发饱含其他特殊的类,是由系统创建的,因此由系统管理生命周期。如常用的四大组件,Activity,Service,BroadcastReceiver和ContentProvider。 仅仅构发明人人那些类的实例是没用的,还并能 管理组件的生命周期。其中以Activity最为简化,不同框架采用的最好的办法 只是尽相同。下面以Activity为例完整性介绍插件化如可支持组件生命周期的管理。 大致分为三种最好的办法 :

理解ProxyActivity代理最好的办法 主要注意两点:

和代码加载类似于 ,插件和主工程的资源关系总要三种防止最好的办法 :

因此,若果将插件apk的路径加入到AssetManager中,便并能实现对插件资源的访问。

防止最好的办法 有什么都有有有种,以VirtualAPK为例,核心思路如下:

execStartActivity中会先去防止隐式intent,将会该隐式intent匹配到了插件中的Activity,将其转上加显式。事先通过markIntentIfNeeded将待启动的的插件Activity替上加了预先在AndroidManifest中占坑的StubActivity,并将插件Activity的信息保存到该intent中。其饱含个dispatchStubActivity函数,会根据Activity的launchMode选着具体启动哪个StubActivity。VirtualAPK为了支持Activity的launchMode在主工程的AndroidManifest中对于每段启动模式的Activity都预埋了多个坑位。

VirtualAPK在初始化总要调用hookInstrumentationAndHandler,该最好的办法 hook了系统的Instrumentaiton类,由上文可知该类和Activity的启动息息相关。

上一步欺骗了系统,让系统以为被委托人启动的是一一一兩个多正常的Activity。当来到图 3.2的第10步时,再将插件的Activity换回来。此时调用的是VAInstrumentation类的newActivity最好的办法 。

该段代码将主守护线程池池中的Instrumentation对象替上加了自定义的VAInstrumentation类。在启动和创建插件activity时,该类总要偷偷做其他手脚。

上述代码是在Activity创建时被调用的(上端会介绍如可hook Activity的创建过程),在activity被构发明人人来后,并能 替换其中的mResources为插件的Resource。将会独立式时主工程的Resource那末访问插件的资源,什么都有有有将会不做替换,会产生资源访问错误。

通过给插件apk生成相应的DexClassLoader便都并能 访问其中的类,这边又有三种防止最好的办法 ,有单DexClassLoader和多DexClassLoader三种形态学 。

对于多DexClassLoader形态学 来说,都并能 用下面的模型来标识。



对于每个插件总要生成一一一兩个多DexClassLoader,当加载该插件中的类时并能 通过对应DexClassLoader加载。事先 不同插件的类是隔离的,当不同插件引用了同一一一兩个多类库的不同版本时,我太满 出现象,RePlugin采用的只是此方案。

Android中常用的有三种类加载器,DexClassLoader和PathClassLoader,它们都继承于BaseDexClassLoader。相关源码如下:

区别在于调用父类构造器时,DexClassLoader多传了一一一兩个多optimizedDirectory参数,这种 目录并能 是内部内部结构存储路径,用来缓存系统创建的Dex文件。而PathClassLoader该参数为null,那末加载内部内部结构存储目录的Dex文件。什么都有有有亲戚亲戚朋友都并能 用DexClassLoader去加载内部内部结构的apk,用法如下:

第二步:hook主工程的Resource

对于合并式的资源访问最好的办法 ,并能 替换主工程的Resource,下面是具体替换的代码。

VirtualAPK通过替换了系统的Instrumentation,hook了Activity的启动和创建,省去了手动管理插件Activity生命周期的繁琐,让插件Activity像正常的Activity一样被系统管理,因此插件Activity在开发时和常规一样,即能独立运行又能作为插件被主工程调用。

资源id是由8位16进制数表示,表示为0xPPTTNNNN。PP段用来区分包空间,默认只区分了应用资源和系统资源,TT段为资源类型,NNNN段在同一一一兩个多APK中从0000递增。如下表所示:

具体实现时,将会AssetManager并总要一一一兩个多public的类,并能 通过反射去创建,因此每段Rom对创建的Resource类进行了修改,什么都有有有并能 考虑不同Rom的兼容性。

缺点

对于单DexClassLoader来说,其模型如下:



将插件的DexClassLoader中的pathList合并到主工程的DexClassLoader中。事先 做的好处时,都并能 在不同的插件以及主工程间直接互相调用类和最好的办法 ,因此都并能 将不同插件的公共模块抽出来倒入一一一兩个多common插件中直接供其他插件使用。Small采用的是这种 最好的办法 。

将会AndroidManifest中预埋的StubActivity并那末具体的实现类,什么都有有有此总要占据 ClassNotFoundException。事先在防止异常时取出插件Activity的信息,通过插件的ClassLoader反射构造插件的Activity。

腾讯的qq空间热修复技术正是利用了DexClassLoader的加载机制,将并能 替换的类上加到dexElements的前面,事先 系统会使用先找到的修复过的类。

该最好的办法 人太好并能很好的实现启动插件Activity的目的,因此将会开发式侵入性很强,dynamic-load-apk事先的插件化方案很少继续使用该最好的办法 ,只是通过hook系统启动Activity的过程,让启动插件中的Activity像启动主工程的Activity一样简单。

关于双亲委托更完整性的资料,亲戚亲戚朋友也都并能 访问我博客事先的介绍:classloader双亲委托模式

合并式的资源防止最好的办法 ,会引入资源冲突,意味着在于不同插件中的资源id将会相同,什么都有有有防止最好的办法 只是使得不同的插件资源拥有不同的资源id。

ProxyActivity代理的最好的办法 最早是由dynamic-load-apk提出的,其思想很简单,在主工程放在一一一兩个多ProxyActivy,启动插件中的Activity总要先启动ProxyActivity,在ProxyActivity中创建插件Activity,并同步生命周期。下图展示了启动插件Activity的过程。



具体的过程如下:

其他插件框架在防止Activity时思想大都差太满,无非是这三种最好的办法 之一将会两者的结合。在hook时,不同的框架将会会选着不同的hook点。如3200的RePlugin框架选着hook了系统的ClassLoader,即图3.2中构造Activity2的ClassLoader,在判断出待启动的Activity是插件中的时,会调用插件的ClassLoader构造相应对象。另外RePlugin为了系统稳定性,选着了尽量少的hook,因此它并那末选着hook系统的startActivity最好的办法 来替换intent,只是通过重写Activity的startActivity,因此其插件Activity是并能 继承一一一兩个多类似于 PluginActivity的基类的。不过RePlugin提供了一一一兩个多Gradle插件将插件中的Activity的基类上加了PluginActivity,用户在开发插件Activity时也是那末感知的。

这段代码主只是将Activity中的Resource,Context等对象替上加了插件的相应对象,保证插件Activity在调用涉及到Context的最好的办法 时并能正确运行。

注意下上述代码hook了几次地方,包括以下几次hook点:

替换了主工程context中LoadedApk的mResource对象。

将新的Resource上加到主工程ActivityThread的mResourceManager中,因此根据Android版本做了不同防止。

第三步:关联resource和Activity

什么都有有有思路是修改资源ID的PP段,对于不同的插件使用不同的PP段,从而区分不同插件的资源。具体实现最好的办法 有三种:

DexPathList的loadClass会去遍历DexFile直到找到并能 加载的类。

在介绍hook最好的办法 事先,先用一张图简要的介绍下系统是如可启动一一一兩个多Activity的。

都并能 看出ClassLoader加载类时,先查看自身是与非 将会加载过该类,将会那末加载过会首先让父加载器去加载,将会父加载器无法加载该类时才会调用自身的findClass最好的办法 加载,该机制很大程度上防止了类的重复加载。

都并能 说,插件化技术涉及得非常广泛,其中最核心的只是Android的类加载机制和反射机制,相关原理请亲戚亲戚朋友自行百度。

独立式时,各个插件的资源是互相隔离的,不过将会我太满 实现资源的共享,并能 拿到对应的Resource对象。

第一代:dynamic-load-apk最早使用ProxyActivity这种 静态代理技术,由ProxyActivity去控制插件中PluginActivity的生命周期。该种最好的办法 缺点明显,插件中的activity并能 继承PluginActivity,开发并能 小心防止context。而DroidPlugin通过Hook系统服务的最好的办法 启动插件中的Activity,使得开发插件的过程和开发普通的app那末那些区别,因此将会hook太满系统服务,异常简化且缺陷稳定。

第二代:为了同时达到插件开发的低侵入性(像开发普通app一样开发插件)和框架的稳定性,在实现原理上总要趋近于选着尽量少的hook,并通过在manifest中预埋其他组件实现对四大组件的插件化。另外各个框架根据其设计思想都做了不同程度的扩展,其中Small更是做成了一一一兩个多跨平台,组件化的开发框架。

第三代:VirtualApp比较厉害,并能完整性模拟app的运行环境,并能实现app的免安装运行和双开技术。Atlas是阿里今年开源出来的一一一兩个多结合组件化和热修复技术的一一一兩个多app基础框架,其广泛的应用与阿里系的各个app,其号称是一一一兩个多容器化框架。

插件化技术最初源于免安装运行apk的想法,这种 免安装的apk都并能 理解为插件。支持插件化的app都并能 在运行时加载和运行插件,事先 便都并能 将app中其他不常用的功能模块做成插件,一方面减小了安装包的大小,被委托人面都并能 实现app功能的动态扩展。我太满 实现插件化,主只是防止下面一一一兩个多现象:

这里要重点说一下DexClassLoader的DexPathList。DexClassLoader重载了findClass最好的办法 ,在加载类总要调用其内部内部结构的DexPathList去加载。DexPathList是在构造DexClassLoader时生成的,其内部内部结构饱含了DexFile。如下图所示: