内容来源:2018 年 04 月 14 日,高级Android工程师陈家伟在“2018互联网开发者大会”进行《漫谈Android组件化及Web化》演讲分享。IT 大咖说(微信id:itdakashuo)做为独家视频合做方,经主办方和讲者审阅受权发布。web
阅读字数:3326 | 9分钟阅读微信
嘉宾演讲视频及PPT:t.cn/Rr62oSm网络
本次分享主要讲述Android组件化和Android web化的原理与实践。框架
安卓的动态化主要包含三个部分,分别是组件化、插件化、模块化。模块化很容易理解,指的是为了解耦将某一个功能拆分红独立的模块,最多见的模块有网络模块和下载模块。插件化和组件化的概念就比较模糊,不一样的框架所作的定义都不同,它们之间的边界也不太明显。ide
上图是某App对插件化和组件化的理解图示,左面表示的是组件化,右边表示的是插件化。组件化的机器人是由多个组件构成,插件化的机器人是一个总体能够进行分发。模块化
这张示意图乍看没什么问题,可是其实仍是存在漏洞,好比当组件化的机器人某一部分变的足够大的时候,该部分其实能够脱离出来成为新的机器人,而当插件化机器人功能愈来愈弱小的时候也能够演变从一个组件。总的来讲组件化和插件化的边界并非很明显,只是根据站的角度和处理问题的方法不一样而产生的概念性上的定义。工具
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。
上文提到的这三点其实就是组件化和插件化共同目的,只不过他们在实现手段上有所不一样。通常组件是和主工程一块儿打包,插件则能够独立打包、另外组件须要借助打包完成更多工做。
前面提到过插件化要解决的其中一个问题就是Dex加载。在Java中能够经过ClassLoader加载class文件,安卓方面则提供了BaseDexClassLoader。BaseDexClassLoader内部有不少DexFile,它是Dex的文件描述,要想实现类加载,能够直接将插件的DexFile前插到BaseDexClassLoader。这种方式就是单类加载器。
安卓中系统类由BootClassLoader加载,PathClassLoader继承自BootClassLoader,加载的是App类。Altlas为了实现类加载,将PathClassLoader替换为了本身的ClassLoader——DelegateClassLoader,这时全部类的加载都会通过DelegateClassLoader,且每一个插件都由独立的PluginClassLoader加载,这些类的查找则由DelegateClassLoader完成。这种方式是多类加载器。
安卓在打包的时候会为每一个资源分配一个32位Int型的ID,采用16进制表示。0x后面是相似PPTTEEEE的形式,TT表明类别,EEEE表明条目,安卓中全部打包资源ID的PP都是7F。
安卓中的资源加载有两种方式,第一种是资源隔离。指的是每一个插件由不一样的Resources对象加载资源(安卓中经过Resources对象获取资源),这是为了不因为资源ID相同形成的资源冲突问题。
第二个资源加载方式是资源分区,它经过修改安卓的打包工具,使得能够变动资源ID的PP,已达到避免ID重复的目的。
对比这两种方式能够发现,资源隔离的好处在于不须要修改打包工具, 毕竟打包工具是使用C++写的,维护起来也比较麻烦(aapt2已经支持资源分区)。资源分区的优点在于可以资源共用,由于它能够共用一个Resources对象,同时还能够避免资源冲突。
四大组件的加载一样有两种方式。一种是经过合并manifest信息方式,好比手动拷贝或者打包,将插件Menifest信息合并入主APP。另外一种是事先声明空的四大组件,再经过Hock掉系统的入口来启动四大组件。
虽然动态框架在国内很流行,但国外对此却不是很热衷,他们更多的仍是使用React Native。
国内的动态框架主要是研究如何经过反射调用或者Hock掉系统API来达到目的,不过系统API的调用其实存在着风险,由于每一个版本的私有API的变更都是挺大的。为此Google也提供了一种动态框架——Instant,它经过Google Play Service加载,即有插件化也有组件化的特色,经过aapt2来完成资源分区,对于四大组件的加载采用预埋、代理的方式启动Activity service。
通常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 Native web实现的核心是多类加载器、资源隔离以及context替换。
由于要保证命名和混淆规则不能出现同一个类名,因此没法使用单类加载器。多类加载器因为采用不一样ClassLoader加载插件,所以不用顾虑命名是否重复。
Context替换指的是将Fragment中的Context替换成咱们自定义的Context。Fragment中全部的类和资源都是经过Context访问,而经过自定义Context就能达到动态加载Activity外部插件的目的。