内容来源:2017 年 12 月 3 日,科大讯飞应用研发经理程坤在“IAS2017互联网架构峰会”进行《讯飞输入法Android架构演进与实践》演讲分享。IT 大咖说(微信id:itdakashuo)做为独家视频合做方,经主办方和讲者审阅受权发布。java
阅读字数:3031 | 8分钟阅读git
本次演讲将分享讯飞输入法Android版从最初开发到逐步发展成熟的过程当中所面临的各类挑战以及经验,还有架构的逐步演进过程。最后提到了团队在组件化架构中的一些实践。安全
讯飞输入法初期采用的是简单MVC,2012年3月份进行了分层重构,2014年12月份作了多进程的架构,最后2015年12月份开始尝试组件化相关的工做。微信
当架构与业务发展不匹配的时候就须要考虑改变当前架构,让架构去适应业务。不管是组织架构仍是软件架构都须要面对业务问题,而如何解决这一问题是架构的核心目标。架构
软件架构也要与组织架构相匹配,它们之间应该是一一映射的关系,也就是说系统设计的架构所对应的设计结构和组织结构是相等的。框架
架构的演进的关键在于平衡性,架构设计人员常常会追求完美,好比设计很是好的扩展性,追求极致的架构,一股脑的将最新最酷的技术放到架构中。这些作法是否是业务所需求的其实并不必定 ,因此架构的可靠性、扩展性包括安全性等是要作必定平衡的,考虑时间和成本上的问题。工具
讯飞输入法的项目于2010年7月份启动,当时的开发人员仅有两人。在2010年10月份的语音云发布会上讯飞输入法要做为演示型产品展现,所以对产品的要求是很是高的。能够看到开发的时间其实只有短短的三个多月,并且当时不少功能都是独创并没有产考。组件化
基于产品初期的这些挑战,咱们在开发的时候实际上是没有使用架构,而是优先考虑如何快速稳定的实现功能。下图展现的是开发前的设计图,上面是展示层,包含各类功能模块,右边是业务逻辑,中间是拼音手写和语音输入的引擎,最下面是数据存储。性能
根据项目开发过程当中的经验来看,当你不能判断产品发布出去后是否能存活或者可否达到预期的状况下,不要花太多的精力去作一个很是好架构,如何快速稳定的发布产品才是核心目的,所以咱们的建议是尽可能复用。测试
在产品的快速发展时期咱们的主要工做就是补齐功能、优化效果,这时的开发人员也由原来的2人增长到了4人。开发过程当中也出现了一些问题,因为当时都是各写各的代码,致使代码的重用性不好,新功能开发成本高,维护起来困难。
基于这方面的问题咱们向产品人员提出了放慢产品发布节奏的要求,所以有了三个月的时间进行产品的重构。
此次重构对产品进行了抽象分层,主要目的是为了复用。下面两层与业务无关,工具层包含经常使用的工具类,框架封装的是业务无关的通用业务能力。服务层和业务层则是和业务相关的,好比服务层的日志应用了框架层日志的能力,并融合了业务上的策略。
分层架构后开发效率得到极大的提高,将原先比较差或很差维护的模块从新进行了梳理和优化,同时还封装了不少了公共初始模块。
产品通过迭代后功能愈来愈多,代码也愈来愈复杂,这时传统的分层架构已没法知足需求。所以咱们将现有的10人团队,分红了业务组和架构组,架构组主要负责性能和稳定性上的优化。
在架构组进行优化的过程当中,咱们将原先的架构调整成多进程架构。原先的输入法只有一个进程,启动的速度很是缓慢,进程崩了输入法就没法使用。所以咱们将输入法分红5个进程,将用户不经常使用的功能放在单独的进程中,用完后马上杀掉,也就是即用即走。另外一点就是隔离,把原前后台的一些功能,好比日志、下载、推送等单独剥离出来独立成一个进程,那么当这部分出现问题的时候就不会影响到主进程。
多进程的调用是很是麻烦的,所以咱们摒弃了原先的单例模式,简化了调用。
产品发展后期团队已经达到了20多人,管理起来很不方面,所以整个团队被分红了4个业务团队,4个架构团队。这时须要考虑的是什么样架构才能匹配各个团队的例行开发,同时还要支持更快的产品迭代。
为了保证快速迭代的过程当中的产品稳定性,咱们开始往组件化的架构上发展。
目前开源的组件化框架很是多,要想实现这样的框架其实没有什么成本。当时咱们的关注点在并行开发上,作到团队之间是隔离的,开发人员开发完功能后能够自行上线、测试、验证。另外一个关注点是动态更新,保证上线的时候框架支持动态更新能力。
这个过程咱们发现组件化架构坑实在是太多了,尚未或者准备作这方面的人员要想清楚是否必定要使用组件化架构。这里有两个概念,一个是并行开发,一个是并行发布,能够根据自身的状况选择作到哪一点为止。并行开发相对来讲比较容易的,可是并行发布难度就不同了,它涉及的不只是客户端,还有服务端、大数据、版本的管理和兼容等各方面的问题。
较早的组件化框架有Atlas/ACDD、DynamicLoadApk、DynamicApk、Small,最近又出了两个新的框架VirtualAPK、Replugin。虽然考察了众多的框架,可是咱们综合考虑后仍是决定本身动手实现,这是因为输入法业务有其独特性。输入法不一样于普通的App,它在键盘方面有着很是高的要求,而这偏偏是其余开源框架没法知足的。
因为安卓系统的碎片化问题兼容性上的处理很是麻烦,好比会出现手机在切换到横屏状态时输入法显示一半的状况,后来发现是由于屏幕切换的时候资源没有刷新,获取到的仍是原来的屏幕宽度,因而咱们在ActivityThread内作了Hook。Hook自己的机制就是利用java的反射机制将API固有的实现替换成咱们本身的。
启动性能的问题主要出如今键盘启动变慢,空间不足致使崩溃上。框架的启动相比于原先作的事情更多了,花费的时间也更多。另外老用户在进行升级的时候,组件启动过程当中会有一个配置文件,列出组件清单配置组件启动的优先级,而后对它进行解压再压缩最后拷贝到相应的地方。能够看到由于要腾出另外的空间将组件提取出来进行解压优化,这样对于老款的ROM空间不够的手机就很不友好。
基于以上的问题咱们将原先的清单文件改为class文件,将应用启动相关的部分归为一个Dex,不相关的归为另外的Dex,这样启动速度就能获得提高,由于在安装时Dex就作了优化。
咱们在进程的相互调用间添加了封装层,像使用本地能力同样使用跨进程的能力,包括远程接口转本地接口、远程拉起、重连和状态恢复。另外为了保证各组件能力可以正常运行,让组件自适应选择进程。
这里的壳工程无任何代码,仅有脚本及配置文件,且只有惟一分支,通常不作修改。另外壳工程仅集成打包、集成调试时使用,不使用repo、git submodule。
业务组件中的Bundle可独立编译调试,打包产物有:测试apk、组件apk和aar,这些产物都会被上传到Nexus私服。