Android 组件化,2024年最新聊聊 Android 开发的现状和思考

两年过去了,项目中的代码越来越多了,参与的人员越来越多了,由原来的一个团队扩展成多个团队合作开发,扯皮的事也越来越多了,编译时间也从原来的20s到现在的3分钟了。于是大家开始吐槽了:

  • 别的团队的同事又改了我这个类的代码,导致我这边出错

  • 登录模块和个人中心一两年没有动过了,能不能这一部份代码抽出去,打个aar,我们编译也能提速

  • 我主要做基础UI控件支持的,改下代码还要下载你们的项目,编译,太耗时了。我就想只调试我写的UI控件。

我们来总结以上项目迭代过程中的痛点:

  • 各业务线耦合太紧,开发过程涉及到相互依赖的功能时,会有出错风险

  • 部份稳定功能,长期没有迭代,但因为都在同一个库了,会拉长编译时间

  • 部份功能想单独运行

所有的问题最终抛到leader这里来,于是排了一堆的技改需求:

  • 项目按首页, 商品, 订单, 个人中心, 登录几个业务模块来分module开发,module之间不相互依赖, 同时收紧代码权限,这样不同团队之间就不会存在代码冲突的问题

  • 登录这个已经稳定的模块,近期不会有改动,可直接打成aar包供其它团队引入使用

  • 单独定义一个module, 命名为ui, 整个工程中的基础UI控件,全部都放到这里来

  • 定义一个module名为common, 为项目提供一些公共的业务资源,被其它业务模块所依赖

  • 定义一个统一的内部路由,供各个组件间进行相互跳转

于是,我们可以看到,在这一堆的技改完成后,整个工程便分为了多个module, 每一个的功能更加的单一,每个人/每个团队专注于自己的工作,也少了很多扯皮的事情,同时每一个团队都可以根据自己的业务发展实际情况,选择自己期望的技术栈。以上这些工作完成了,一个组件化的架构雏形便出来了。 我们再来细看一个组件化架构要解决哪些问题:

  • 每个组件既可以是一个组件,也可以是一个application,可以单独打包调试

  • 组件间的通信

  • 组件间资源冲突的问题

  • 组件间的跳转

module和app角色的切换


manifest文件

我们期望每一个业务子module,通过配置,可以单独运行起来,此时就涉及到manifest的配置了。默认情况下,每一个module都只会有一个manifest文件,在打包阶段,打包工具会将每个module中的manifest文件合并,形成整个项目的manifest文件。比如,对于个人中心module, 它里面有一个activity名为PersonaMainActivity当它做作一个app运行起来的时候, 该activity是应用的入口,为此,在对应的manifest中会有类似如下代码:

但是,项目在发版的时候,个人中心这个模块一定是作为一个子module引进来的,PersonalMainActivity绝对不可能是整个app的入口。为此我们需要定义两套不同的manifest文件,一套是当该组件做为app运行的时采用,另一套是当该组件做为module时采用。

我们在项目的根目录定义一个文件config.gradle, 里面定义一个变量moduleAsApp, 当该值为false时,代表该项目中所有的业务子module都作为组件被主module依赖,当它为true时,代表业务子module可作为独立app运行。然后在每一个业务子module中都引入该配置。

config.gradle文件:

ext {

moduleAsApp = false;

appId = [

“app” : “com.fred.routerapp”,

“order” : “com.fred.order”,

“product” : “com.fred.product”,

“personal” : “com.fred.personal”

]

packageNameForAPT = appId.app + “.apt”;

androidConfig = [

compileSdkVersion: 29,

buildToolsVersion: “29.0.3”,

minSdkVersion : 21,

targetSdkVersion : 29,

versionCode : 1,

versionName : “1.0”

]

}

再回到我们的个人中心module里面来,对这两种情况分别采用不同的manifest文件,如下:

sourceSets {

main {

if (moduleAsApp) {

manifest.srcFile ‘src/main/debug/AndroidManifest.xml’

} else {

manifest.srcFile ‘src/main/AndroidManifest.xml’

java {

// release 时 debug 目录下文件不需要合并到主工程

exclude ‘/debug/

}

}

}

}

对于module配置和applicationId

if (moduleAsApp) {

apply plugin: ‘com.android.application’

} else {

apply plugin: ‘com.android.library’

}

applicationId:

if (moduleAsApp) {

applicationId appId.personal

}

资源名冲突


在组件化开发过程中,可能会存在资源名冲突的问题,假如在product模块中有一个price_detail.xml用来显示价格相关的视图,在order模块中也有一个price_detail.xml,那便会出现资源名冲突。对于这种问题,可以统一命名方式,如加前缀,将product模块中的这些资源命名全部加上product_前缀,order模块中全部加上order_前缀,这样可以一定程序上避免。当然如果只是针对于布局xml文件可以在gradle文件中进行配置来约束

android {

resourcePrefix “personal”

}

这种方式并不是说在编译的时候会手动将你的文件名进行修改,加personal_的前缀。只是加了这个配置,如果你的命名不合法,编译器会给一个提示。

image.png

对于资源命名冲突这一块我们不仅需要关注布局文件,还有类似于color,anim, dimen等命名

组件间的通信


此处组件间的通信主要包括两方面的内容:一种是业务之间的通知消息,比如订单模块中,一个订单提交了,需要通知购物车刷新一个购物车中的商品列表,对于这种类型的消息通知,我们可以采用EventBus,RxBus这种消息总线来做。另一个通信则是组件间基础数据的打通。比如在订单模块中,用户下单时需要判断是否登录,从单一职责的原则上讲用户的登录信息是在个人中心模块中才有。那么个人中心模块如何向其它模块提供用户相关的数据呢?

还记得我们前面提到的commonmodule吗?它可以被其它的所有业务子模块依赖,于是我们在commonmodule中定义一个接口IAccountService

public interface IAccountService {

public boolean isLogin();

}

在个人中心模块有它的实现类

public class AccountServiceImpl implements IAccountService {

@Override

public boolean isLogin() {

return false;

}

}

common模块中有一个ServiceManager类,这个类是一个服务的管理者,它会持有一个AccountService, 如下:

public class ServiceManager {

private ServiceManager() {}

private IAccountService accountService;

private static class AppConfigurationHolder {

private static final ServiceManager instance = new ServiceManager();

}

public static ServiceManager getInstance() {

return AppConfigurationHolder.instance;

}

public void setAccountService(IAccountService as) {

this.accountService = as;

}

public IAccountService getAccountService() {

return this.accountService;

}

}

我们期望其它模块通过调用ServiceManager.getInstance().getAccountService()便可以拿到用户信息相关的服务。如果要达到此目的,那这个AcccountService在什么时候注入呢?我们在再看app的架构依赖

image.png

AccountService的实现在个人中心module,我们不确定用户在什么场景会调用这个服务。对于这种类型的服务,需要在app启动的时候便注入到app中。而AccountService只能在个人中心模块实例化,为此个人中心模块必须要能监听到应用的初始化时机,也就是Application的onCreate方法

监听Application的状态

common组件中定义一个接口, 其它的业务module都会实现这个接口。

public interface AppStateListener {

void onCreate();

void onLowMemory();

}

在个人中心模块会有一个类, 在onCreate中会new一个AccountService,并且注入到ServiceManager

public class PersonalAppStatusListener implements AppStateListener {

@Override

public void onCreate() {

ServiceManager.getInstance().setAccountService(new AccountServiceImpl(AppStateManager.getInstance().getApplication()));

}

@Override

public void onLowMemory() {

}

}

common模块中,会有一个类来维护所有的子业务module对Application状态的监听

public class AppStateManager {

private List lifeCycleListenerList;

private AppStateManager(){}

private Application application;

public void init(Context context) {

this.application = (Application) context;

initAppLifeCycleListener();

}

public void initAppLifeCycleListener() {

//定义所有模块的listener的类名

String [] names = Constants.moduleLifeCycleListener;

if (names != null && names.length > 0) {

lifeCycleListenerList = new ArrayList<>();

}

for (int i = 0; i < names.length; i ++) {

try {

Class clazz = Class.forName(names[i]);

if (AppStateListener.class.isAssignableFrom(clazz)) {

lifeCycleListenerList.add((AppStateListener) clazz.newInstance());

}

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (IllegalAccessException e) {

e.printStackTrace();

} catch (InstantiationException e) {

e.printStackTrace();

}

}

}

public Application getApplication() {

return this.application;

}

public void onCreate() {

if (lifeCycleListenerList != null) {

for (AppStateListener listener : lifeCycleListenerList) {

listener.onCreate();

}

}

}

public void onLowMemory() {

if (lifeCycleListenerList != null) {

for (AppStateListener listener : lifeCycleListenerList) {

listener.onLowMemory();

}

}

}

自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。

深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!

因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
img
img
img
img
img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
img

最后

分享一份NDK基础开发资料

详解:Linux网络虚拟化技术

分享内容包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
img
学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!**

由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新

如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-bPs0VlDS-1712608365075)]

最后

分享一份NDK基础开发资料

[外链图片转存中…(img-eRwN9JmJ-1712608365076)]

分享内容包括不限于高级UI、性能优化、架构师课程、NDK、混合式开发(ReactNative+Weex)微信小程序、Flutter等全方面的Android进阶实践技术;希望能帮助到大家,也节省大家在网上搜索资料的时间来学习,也可以分享动态给身边好友一起学习!

一个人可以走的很快,但一群人才能走的更远。不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎扫码加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
[外链图片转存中…(img-Yu13OXaw-1712608365076)]

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值