详述Android插件化原理

        本文基于singwhatiwanna的开源DL插件框架讲述,修改并重构了一些内容,任总的blog原理讲得比较浅,这里我基于自己的理解再详细讲一点东西,也算是一个记录吧~

预备知识:

        一. Java ClassLoader

        作用:

    加载Class文件到JVM,以供程序使用的。我们知道,java程序可以动态加载类定义,而这个动态加载的机制就是通过ClassLoader来实现的。既然ClassLoader是用来加载类到JVM中的,那么ClassLoader又是如何被加载呢?难道它不是java的类?没有错,在这里确实有一个ClassLoader不是用java语言所编写的,而是JVM实现的一部分,这个ClassLoader就是bootstrapclassloader(启动类加载器),这个ClassLoaderJVM运行的时候加载java核心的API以满足java程序最基本的需求,其中就包括用户定义的ClassLoader,这里所谓的用户定义是指通过java程序实现的ClassLoader,一个是ExtClassLoader,这个ClassLoader是用来加载java的扩展API的,也就是/lib/ext中的类,一个是AppClassLoader,这个ClassLoader是用来加载用户机器上CLASSPATH设置目录中的Class的,通常在没有指定ClassLoader的情况下,程序员自定义的类就由该ClassLoader进行加载。当运行一个程序的时候,JVM启动,运行bootstrapclassloader,该ClassLoader加载java核心APIExtClassLoaderAppClassLoader也在此时被加载),然后调用ExtClassLoader加载扩展API,最后AppClassLoader加载CLASSPATH目录下定义的Class,这就是一个程序最基本的加载流程。文字太多,下面列个list供大家参考:

        核心classLoader:         

            1.bootstrapclassLoader(启动类加载器),加载java核心API,包括用户自定义的classLoader以及另外两个核心classLoader;

    2.ExtClassLoader,加载java扩展API,也就是/lib/ext中的类;
    3.AppClassLoader,加载程序员自定义类,也就是我们工程classPath下设置的class。

        Java程序加载过程:

            1.当运行一个程序的时候,JVM启动

    2.bootstrapclassloader,该ClassLoader加载java核心APIExtClassLoaderAppClassLoader也在此时被加载

    3.ExtClassLoader加载扩展API

    4.最后AppClassLoader加载CLASSPATH目录下定义的Class

  二.Android ClassLoader

        Android ClassLoader种类: 
        DexClassLoader :可以加载文件系统上的 jar dex apk
›         PathClassLoader :可以加载 /data/app 目录下的 apk ,这也意味着,它只能加载已经安装的 apk
›         URLClassLoader :可以加载 java 中的 jar ,但是由于 dalvik 不能直接识别 jar ,所以此方法在 android 中无法使用, 尽管还有这个类
            

        Android开发和普通的java开发不同的地方是把class文件再重新打包成dex类型的文件,这种重新打包会对Class文件内部的各种函数表、变量表等进行优化dex文件是一种经过android打包工具优化后的Class文件,因此加载这样特殊的Class文件就需要特殊的类装载器,所以android中提供了DexClassLoader类。加载流程如下:

           1. 通过PacageMangager获得指定的apk的安装的目录,dex的解压缩目录,c/c++库的目录

           2.创建一个 DexClassLoader实例

           3.加载指定的类返回一个Class

           4. 然后使用反射调用这个Class

        三. Android Resources

         

   应用程序的每一个Activity组件都关联有一个ContextImpl对象,这个ContextImpl对象就是用来描述Activity组件的运行上下文环境的。Activity组件是从Context类继承下来的,而ContextImpl同样是从Context类继承下来的。我们在Activity组件调用的大部分成员函数都是转发给与它所关联的一个ContextImpl对象的对应的成员函数来处理的,其中就包括用来访问应用程序资源的两个成员函数getResources和getAssets。

   ContextImpl类的成员函数getResources返回的是一个Resources对象,有了这个Resources对象之后,我们就可以通过资源ID来访问那些被编译过的应用程序资源了。ContextImpl类的成员函数getAssets返回的是一个AssetManager对象,有了这个AssetManager对象之后,我们就可以通过文件名来访问那些被编译过或者没有被编译过的应用程序资源文件了。事实上,Resources类也是通过AssetManager类来访问那些被编译过的应用程序资源文件的,不过在访问之前,它会先根据资源ID查找得到对应的资源文件名。

Resources类有一个成员函数getAssets,通过它就可以获得保存在Resources类的成员变量mAssets中的AssetManager,例如,ContextImpl类的成员函数getAssets就是通过调用其成员变量mResources所指向的一个Resources对象的成员函数getAssets来获得一个可以用来访问应用程序的非编译资源文件的AssetManager。

   我们知道,Android应用程序除了要访问自己的资源之外,还需要访问系统的资源。系统的资源打包在/system/framework/framework-res.apk文件中,它在应用程序进程中是通过一个单独的Resources对象和一个单独的AssetManager对象来管理的。这个单独的Resources对象就保存在Resources类的静态成员变量mSystem中,我们可以通过Resources类的静态成员函数getSystem就可以获得这个Resources对象,而这个单独的AssetManager对象就保存在AssetManager类的静态成员变量sSystem中,我们可以通过AssetManager类的静态成员函数getSystem同样可以获得这个AssetManager对象。

   AssetManager类除了在Java层有一个实现之外,在C++层也有一个对应的实现,而Java层的AssetManager类的功能就是通过C++层的AssetManager类来实现的。Java层的每一个AssetManager对象都有一个类型为int的成员变量mObject,它保存的便是在C++层对应的AssetManager对象的地址,因此,通过这个成员变量就可以将Java层的AssetManager对象与C++层的AssetManager对象关联起来。

   C++层的AssetManager类有三个重要的成员变量mAssetPaths、mResources和mConfig。其中,mAssetPaths保存的是资源存放目录,mResources指向的是一个资源索引表,而mConfig保存的是设备的本地配置信息,例如屏幕密度和大小、国家地区和语言等等配置信息。有了这三个成员变量之后,C++层的AssetManager类就可以访问应用程序的资源了。

  

    以上知识是理解安卓插件化非常重要的原理知识,因为插件化的本质就是通过安卓的DexClassLoader去加载apk里的类,再通过AssetManager去加载资源文件,这两玩意儿都进入内存之后,我们的宿主就可以通过接口的方式来调用我们的插件类和资源文件了。


插件加载技术详细介绍

         

    1.  插件类的加载    

              宿主程序会到文件系统比如 SD 卡去加载 apk或者jar(经过测试,必须是jvm可以解压的后缀格式) ,然后通过一个 proxyA ctivity作为壳子,去加载 apk 中的 activity 。大致细节如下:
›            插件 Activity 本身无法启动(生命周期,资源等问题),是通过宿主提供的 ProxyActivity 来加载的;
›            当我们发 Intent 去启动插件当 Activity 时实质启动的时 ProxyActivity;
›            为了封装细节所以封装了 DXIntent;
›            所有插件实现了 IDXPlugin 接口;
           › PrxoyActivity 接管了所有插件 Activity。

    2. 获取AssetsManager

             
›             加载 的方法是通过反射,通过调用 AssetManager 中的 addAssetPath 方法,我们可以将一个 apk 中的资源加载到 Resources 中,由于 addAssetPath 是隐藏 api 我们无法直接调用,所以只能通过反射,下面是它的声明,通过注释我们可以看出,传递的路径可以是 zip 文件也可以是一个资源目录,而 apk 就是一个 zip ,所以直接将 apk 的路径传给它,资源就加载到 AssetManager 中了,然后再通过 AssetManager 来创建一个新的 Resources 对象,这个对象就是我们可以使用的 apk 中的资源 了。

      3. 插件加载入内存的流程

       
        

插件类分析

        DXPluginBean

             封装了每个 Plugin 也就是 apk 的数据
       ›     维护在 DXPluginManger 类的 Map

        DXPluginManager

›            插件管理核心类
           › 加载插件
›            启动插件
›            插件维护
 

        IDXPlugin

›             把每个插件的 Activity 抽象成一个“插件”
›             IDXPlugin 实现了 Activity 的主要方法
            › onAttach 方法是插件专用的回调方法,当插件 Activity Proxy 加载当时候 proxy 的引用赋值给 that

        DXIntent      

             › pluginPackgeName 跳转的Plugin的包名,也就是Manifest里的packageName

             pluginClassName 跳转的Plugin中指定的ActivityName,可以传null,则默认时跳转main Activity

        DXPluginBaseActivity / DXPluginBaseFragmentActivity

›             所有插件 Activity 继承这两个 Activity
›             Activity 实现 IDXPlugin 接口
›             onAttach 方法中获得 proxyActivity 的引用
›             所有 activity 继承方法中需要对插件本身启动还是在宿主中被启动进行判断

       DXProxyActivity / DXProxyFragmentActivity           

            › 宿主 Activity
›            host 中调用插件 Activity 的跳转,本质就是这两个 Activity 之间的跳转
›            为插件提供真正的 Context
›           为减少重复代码将插件的初始化放在 DXPluginInitializer 类中     

       DXPluginInitializer

›            修复 theme 带来的崩溃问题
›            启动插件 Activity流程:

             1 通过反射获得插件Activity的默认构造函数

             2 通过反射new出一个插件并强转成IDXPlugin

             3 回调onAttach方法传入Prxoy的引用

             4 调用onCreate方法调起插件

       DXXMLManager

             通过配置文件可实现直接通过插件名称调用插件

        

        Service的部分和Activity差不多的原理,不再赘述~


         最后来一张UML看下类关系:

         


  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
### 回答1: Trivy是一种容器安全扫描工具,它可以帮助用户检测容器中的漏洞。Trivy可以扫描各种容器格式,如Docker,OCI,AppC,Kubernetes,Amazon ECR,GCR,Azure ACR等。它可以检测容器映像中的漏洞,并可以提供关于如何修复这些漏洞的建议。 ### 回答2: Trivy是一个开源的漏洞扫描工具,主要用于容器镜像和应用程序的安全扫描。它的工作原理如下: 1. Trivy获取容器镜像或应用程序的元数据信息,包括镜像标签、层信息、基础操作系统等。 2. Trivy使用漏洞数据库,例如CVE(通用漏洞和披露程序)、NVD(国家漏洞数据库)等,对容器镜像或应用程序进行漏洞匹配。 3. Trivy对比漏洞数据库中的漏洞信息与容器镜像或应用程序中的组件版本信息,检测是否存在已知漏洞。 4. Trivy还分析镜像中的软件配置文件、依赖库和运行时环境等,以检测存在的安全问题,例如默认密码、密钥泄露、权限设置等。 5. 检测完成后,Trivy将检测结果以易于阅读的格式呈现给用户,包括漏洞的等级、影响范围、修复建议等。 6. Trivy还支持自动扫描,可以与CI/CD系统集成,在容器镜像构建或应用程序部署过程中进行实时的安全扫描。 总的来说,Trivy通过分析容器镜像或应用程序的组件信息和配置,匹配已知漏洞数据库中的漏洞信息,以及检测潜在的安全问题,为用户提供全面的安全扫描服务。它的工作原理简单明了,可以帮助用户及时发现和修复潜在的安全风险,提高系统的安全性。 ### 回答3: Trivy是一种开源的漏洞扫描器,用于检测容器镜像中的安全漏洞。它的工作原理如下: 1. 镜像分析:Trivy通过分析Docker或OCI容器镜像的元数据和文件系统来确定目标镜像的构建情况。它解析镜像的配置文件,查找运行时所需的软件包和其版本信息。 2. 漏洞数据库:Trivy从漏洞数据库中获取最新的安全漏洞信息。这些漏洞数据库包含已公开披露的漏洞、CVE编号(通用漏洞与披露)以及修复建议。 3. 漏洞扫描:Trivy使用镜像分析的结果和漏洞数据库的信息进行对比,检查每个软件包的版本是否受已知漏洞影响。如果镜像中的软件包版本与漏洞数据库中的匹配,Trivy将报告该漏洞。 4. 报告生成:Trivy将扫描结果以易于阅读的格式生成报告,包括漏洞名称、CVE编号、严重程度、受影响的软件包和版本等信息。报告还可能显示安全漏洞的描述、修复建议和参考链接。 总之,Trivy通过分析容器镜像的元数据和文件系统,并与漏洞数据库进行匹配来检测其中的安全漏洞。它可以有效地帮助开发人员和运维团队在构建和部署容器时识别潜在的安全风险。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值