Android动态化介绍

动态化演进

安卓的动态化主要包含三个部分,分别是组件化、插件化、模块化。模块化很容易理解,指的是为了解耦将某一个功能拆分成独立的模块,最常见的模块有网络模块和下载模块。插件化和组件化的概念就比较模糊,不同的框架所做的定义都不一样,它们之间的边界也不太明显。



上图是某App对插件化和组件化的理解图示,左面表示的是组件化,右边表示的是插件化。组件化的机器人是由多个组件构成,插件化的机器人是一个整体可以进行分发。


这张示意图乍看没什么问题,但是其实还是存在漏洞,比如当组件化的机器人某一部分变的足够大的时候,该部分其实可以脱离出来成为新的机器人,而当插件化机器人功能越来越弱小的时候也可以演变从一个组件。总的来说组件化和插件化的边界并不是很明显,只是根据站的角度和处理问题的方法不同而产生的概念性上的定义。

Android动态化需要解决的问题


Android动态化需要解决4个问题,分别是Dex加载、资源加载、SO加载、四大组件加载。下文将介绍这四个问题所涉及的安卓的具体部分。


Dex是安卓编译后的产物,Java会被编译成class,安卓则对这些class文件进行压缩处理得到一个Dex。安卓的资源比较多,有图片、布局文件、动画等。SO是安卓的动态链接库,一般由C或者C++写成。安卓的四大组件Activity,Service,Content Provider,BroadcastReceiver必须在AndroidManifest.xml配置文件中声明才能够正常加载。

主流动态化框架


目前主流的动态化框架有Atlas、RePlugin、DroidPlugin、VirtualAPK,除开Atlas将自己定义为组件化框架外,其他三个都将自己定义为插件化框架。根据个人的观察发现,他们主要的区别在于对安卓的四大组件的处理上,Atlas是先定义这些组件再通过打包的方式处理。但是去年Atlas也做了一些插件化的处理,这使得目前的这四个框架都涉及到了插件化。


组件化和插件化


相信大家都经常遇到产品的某些需求要紧急上线的情况,但是App不同于H5开发那样可以随时发版,需要经过众多的渠道进行发布,而如果能够做到动态发版,对整个产品的演进和动态开发都会有比较好的推进作用。


另外减少包体积同样也很重要,一般同个App,iOS的包体积会比Android的更大,这是由于iOS无法进行本地代码的动态下发,而国内的安卓渠道审核相对比较松一些。还有一个需要关注的是热修复,它可以让我们即时的修复线上的BUG。


上文提到的这三点其实就是组件化和插件化共同目的,只不过他们在实现手段上有所不同。一般组件是和主工程一起打包,插件则可以独立打包、另外组件需要借助打包完成更多工作


动态加载原理

动态加载App思路之类加载


前面提到过插件化要解决的其中一个问题就是Dex加载。在Java中可以通过ClassLoader加载class文件,安卓方面则提供了BaseDexClassLoader。BaseDexClassLoader内部有很多DexFile,它是Dex的文件描述,要想实现类加载,可以直接将插件的DexFile前插到BaseDexClassLoader。这种方式就是单类加载器。



安卓中系统类由BootClassLoader加载,PathClassLoader继承自BootClassLoader,加载的是App类。Altlas为了实现类加载,将PathClassLoader替换为了自己的ClassLoader——DelegateClassLoader,这时所有类的加载都会经过DelegateClassLoader,且每个插件都由独立的PluginClassLoader加载,这些类的查找则由DelegateClassLoader完成。这种方式是多类加载器。


动态加载App思路之资源加载


安卓在打包的时候会为每个资源分配一个32位Int型的ID,采用16进制表示。0x后面是类似PPTTEEEE的形式,TT代表类别,EEEE代表条目,安卓中所有打包资源ID的PP都是7F。


安卓中的资源加载有两种方式,第一种是资源隔离。指的是每个插件由不同的Resources对象加载资源(安卓中通过Resources对象获取资源),这是为了避免由于资源ID相同造成的资源冲突问题。


第二个资源加载方式是资源分区,它通过修改安卓的打包工具,使得可以变更资源ID的PP,已达到避免ID重复的目的。


对比这两种方式可以发现,资源隔离的好处在于不需要修改打包工具, 毕竟打包工具是使用C++写的,维护起来也比较麻烦(aapt2已经支持资源分区)。资源分区的优势在于能够资源共用,因为它可以共用一个Resources对象,同时还可以避免资源冲突。


动态加载App之四大组件加载


四大组件的加载同样有两种方式。一种是通过合并manifest信息方式,比如手动拷贝或者打包,将插件Menifest信息合并入主APP。另一种是事先声明空的四大组件,再通过Hock掉系统的入口来启动四大组件。


Google玩转动态化


虽然动态框架在国内很流行,但国外对此却不是很热衷,他们更多的还是使用React Native。


国内的动态框架主要是研究如何通过反射调用或者Hock掉系统API来达到目的,不过系统API的调用其实存在着风险,因为每个版本的私有API的变动都是挺大的。为此Google也提供了一种动态框架——Instant,它通过Google Play Service加载,即有插件化也有组件化的特点,通过aapt2来完成资源分区,对于四大组件的加载采用预埋、代理的方式启动Activity service。


Web化介绍


一般App的活动页都是使用H5开发,因为H5可以进行动态更新。但是H5体验上还是不如Native,在动画以及一些高级功能方面也不够强。


而组件化也存在着问题,在最新发布的Android P版本中限制了对私有API的访问,一旦访问私有API 应用就会崩溃。虽然可以通过某些技术手段攻克这一限制,但是其实还有另一种方式——SPA(单页面应用)。 对于Web端的SPA,它只有一个HTML文件,然后通过JS渲染,以达到在一个HTML的进行页面跳转的目的。


下面来看下Android中的web化。


首先是React Native。React Native中每个页面都是一个View,且都在Activity中,它通过控制View的切换来进行页面跳转。


Android提供了一种布局容器——Fragment,Activity可以承载很多Fragment,通过切换Fragment也可以达到页面切换的效果。这就是Android Native的web化。


Android web核心原理


Android Native web实现的核心是多类加载器、资源隔离以及context替换。


因为要保证命名和混淆规则不能出现同一个类名,所以无法使用单类加载器。多类加载器由于采用不同ClassLoader加载插件,因此不用顾虑命名是否重复。


Context替换指的是将Fragment中的Context替换成我们自定义的Context。Fragment中所有的类和资源都是通过Context访问,而通过自定义Context就能达到动态加载Activity外部插件的目的。


©著作权归作者所有:来自51CTO博客作者mob604756ef1373的原创作品,如需转载,请注明出处,否则将追究法律责任

更多相关文章

  1. JDBC【2】-- JDBC工作原理以及简单封装
  2. 一篇文章带你了解JVM常见面试题有哪些
  3. 报表生成器FastReport .Net如何存储和加载报告
  4. 如何知道 window 的 load 事件已经触发
  5. 使用Rust编写推箱子游戏基础篇
  6. 粉丝福利 | 秒 get 支付宝同款扫码组件
  7. 数据搬运组件:基于Sqoop管理数据导入和导出
  8. Spark 如何摆脱java双亲委托机制优先从用户jar加载类?
  9. 前端用Ajax实现数据异步加载,无刷新分页(核心思路为 事件派发+事件

随机推荐

  1. 使用xmlhttp为网站增加域名查询功能详细
  2. Xml格式数据的生成和解析的代码详情
  3. 详细介绍JavaScript解析 JSON 及 XML的示
  4. Web设计中如何使用XML数据源对象详细介绍
  5. web.xml 组件加载顺序详解
  6. 详细介绍Spring使用多个xml配置文件的代
  7. 基于XML的桌面应用的图文代码详细介绍
  8. LINQ to XML 编程基础的图文代码详细介绍
  9. 详细介绍使用XML资源文件来定义颜色,样式
  10. 使用xmldom在服务器端生成静态html页面