【Android】【架构】【美团外卖平台化】

2019-08-10  本文已影响0人  徐乙_

背景

美团外卖有多个流量入口,外卖App,美团App,微信小程序等
其中外卖作为美团App的一个频道接入,其功能和外卖App大体相似
早期由2个团队维护,如果可以由一个团队维护,人力成本就可以下降一半,这是极其重要的
所以平台化可以理解为App融合技术,一份代码,多平台运行
我所在的部门同样有2个功能相似的App,存在不少的两端代码拷贝问题,所以学习此文希望可以从中得到借鉴

早期失败尝试

复用公共组件,逐项解决差异化

image.png
  1. UI样式可以在宿主复写一套同名,进行覆盖
  2. 建立代理Fragment统一基类
  3. 2个宿主通过适配器,输出一套公共的数据
  4. 其他差异通过接口隔离
    显然这样会产生大量适配代码,代码冗余。复杂度高。无法定义规范,难以维护,后来者容易打破约束

页面组件化
即拆分一个页面成多个单元,设想这些小单元更容易复用一些
但是实际上底层库差异过大,代码逻辑还是无法有效复用

优雅设计模式

image.png

借鉴MVP Clean,这是一个优秀的页面架构,可以把页面拆分成比较细的粒度
你可以理解为把一个页面分成了6-7个组成,分别是UI、数据、逻辑、功能等
按照这些维度来逐项适配
这样适配的灵活性更高,也更规范
但是其复杂度更甚,而且依然有维护困难的弊端

最终方案:平台化

统一底层库是必然的,而且这也没有什么不合适的,底层库都是公共的一些能力,基本没有特殊定制的需求
同时要进行App融合,建立一个外卖平台App,分别以AAR的形式接入到外卖App壳子和美团App外卖频道

image.png

维护2端的差异,通过flavor即可。flavor可用于多渠道打包,在这里公共的部分单独定义,差异的部分放到2个不同的flavor里
通过这种方式,全新的视野、思路,完美解决了代码复用的问题

优化:多Project+多Module+多P

组件化
各个业务模块建立不同的module
页面通信通过基于scheme原理的框架

image.png

功能的调用通过接口下沉+反射实例化框架

image.png

模块内的页面维度的隔离
为了在模块内部实现更精细的解耦,每个页面都会被隔离
隔离方式不是采用Module,这样成本太高
而是通过文件夹隔离

image.png

所以需要修改Gradle中打包类和资源的路径

sourceSets {
    main {
        def dirs = ['p_widget', 'p_theme',
                    'p_shop', 'p_shopcart',
                    'p_submit_order','p_multperson','p_again_order',
                    'p_location', 'p_log','p_ugc','p_im','p_share']
        dirs.each { dir ->
            java.srcDir("src/$dir/java")
            res.srcDir("src/$dir/res")
        }
    }
}

但是这样一来,无法通过flavor来适配2个平台的差异了,还是修改Gradle

image.png
productFlavors {
    wm {}
    mt {}
}

sourceSets {
    def dirs = ['p_restaurant', 'p_goods_detail', 'p_comment', 'p_compose_order',
                'p_shopping_cart', 'p_base', 'p_product_set']
    main {
        manifest.srcFile 'src/p_restaurant/main/AndroidManifest.xml'
 dirs.each { dir ->
            java.srcDir("src/${dir}/main/java")
            res.srcDir("src/${dir}/main/res")
        }
    }
    wm {
        dirs.each { dir ->
            java.srcDir("src/${dir}/wm/java")
            res.srcDir("src/${dir}/wm/res")
        }
    }
    mt {
        dirs.each { dir ->
            java.srcDir("src/${dir}/mt/java")
            res.srcDir("src/${dir}/mt/res")
        }
    }
}

此时的问题每个Page之间如何通信?这里就较为简单,在模块内,抽出公共的base,再在此基础上建立多个Page

image.png

三级工程结构每一级也可以自由地升级、降级

组件化过程:如何拆分业务模块?

康威定理:代码结构可以遵循公司的组织结构,并且根据组织结构实时进行调整
在外卖子频道比如商超快速发展的情况下,很可能脱离了外卖的【页面->商家->下单】流程,建立自己的流程
这个时候仍然遵循外卖的业务拆分逻辑就不合适了
所以要参照康威定理,如何拆分由商超团队自行考量

外卖内核

商超是外卖的子频道,但是不能保证它未来是否会升级,和外卖平级
为了这种情况进行兼容,设计了如下内核

image.png

内层是Model,第二层是CRUD服务,最外层是业务模块
假如未来商超升级,升级最内层即可
假如未来商超Model一致,CRUD服务不一致,那么就抽象服务层,由商超和外卖定义具体的实现

尾声:编译打包优化

1、子库的依赖方式
由aar和源码依赖两种方式
每次都需要在Gradle中改是compile aar还是compile project
这点很不灵活,所以开发了一个简单的插件,进行一键切换
这点携程的Trip.com App也是这么做的
不光携程,无数个公司都是这么做的
但是我们组应该应用不了,因为我们都是远程依赖。。。
2、一键打包
外卖App可以直接远程依赖,使用Jenkins即可自动化打包
而美团平台有限制,只能接入aar,所以需要打各个业务线aar,然后打一个总的aar
所以通过脚本以及此模型实现一键打包

image.png

可优化的点

1、组件化做的还是太过初级
可以学习下Atlas架构
2、动态能力弱
可以考虑插件化

收获

其平台化思想值得借鉴
多个相似的App可以融合到一个App内
通过flavor屏蔽差异
同时使用三级隔离+组件化,细粒度解耦项目

后记

学习自
https://tech.meituan.com/2018/03/16/meituan-food-delivery-android-architecture-evolution.html

有什么写得错误、让人费解或遗漏的地方,希望可以不吝赐教,我会马上更改

上一篇下一篇

猜你喜欢

热点阅读