写点什么

一种清晰, 便于扩展 android 项目架构方案

用户头像
Android架构
关注
发布于: 14 小时前

作者将功能与项目的基础架构的代码分离开。整个工程分为corefeatures两个包,core下面放的是整个工程的基础代码。将业务进行分类组织在features目录下,这样便将代码按照功能模块的维度组织起来。某个功能(如 login)相关的代码都会在features.login下面。其好处:


  1. 代码的结构更加清晰,后面维护方便

  2. 后期如果业务起来了,想要做组件化,因为代码都在同一个目录下面,抽取出去也更加方便。

细化

再回到 Google 推荐的架构图,我们再将 Google 推荐的这个架构整合进项目中,于是,项目的结构便如下所示:


com.xxxx.app


  • core 整个 app 内所有模块共享,或公共的配置

  • data 全局的数据访问,如用户信息,app 配置等

  • model

  • AppConfigInfo

  • UserInfo

  • datasource

  • impl

  • AppConfigRemoteDataSource

  • AppConfigLocalDataSource

  • UserRemoteDataSource

  • UserLocalDataSource

  • IUserRemoteDataSource

  • IUserLocalDataSource

  • IAppConfigRemoteDataSource

  • IAppConfigLocalDataSource

  • AppConfigRepository

  • UserRepository

  • util

  • view

  • base

  • BaseActivity

  • BaseFragment

  • BaseViewModel

  • hybrid

  • features 下面是按业务来分组

  • login

  • ui

  • xxxActivity

  • xxxViewModelFactory

  • xxxViewModel 可直接调用core/data里的UserRepository

  • product

  • data 考虑到同一个模块中的数据层面可以共用,故放在外层

  • model

  • ProductListInfo

  • ProductDetailInfo

  • datasource

  • IProductLocalDataSource

  • IProductRemoteDataSource

  • impl

  • ProductLocalDataSourceImpl

  • ProductRemoteDataSourceImpl

  • ProductRepository

  • ui

  • detail

  • view 放置自定义 View

  • ProductLabelView

  • ProductDetailActivity

  • ProductDetailViewModel

  • list

  • rendermodel

  • view 放置自定义 View

  • ...

  • ProductListViewModel (和ProductDetailViewModel 都调用ProductRepository来获取数据)

  • ....

  • xxxViewModelFactory如果业务简单, ViewModelFactory可以写在这里, detail 模块和 list 可以共用

  • personal

  • data

  • ui

  • xxxViewModelFactory

  • xxxViewModel此处可能需要获取用户信息,则直接调用core/data里的UserRepository


以下对上面的结构进行说明

core 目录

服务于整个工程,一些基础代码。关于系统配置,用户信息等数据的操作全部放在这个里面。


  • data目录:DAO 相关的操作,model包下面的是 pojo, 数据的获取或持久化全部由xxxRepository去调用datasource包下面的数据源去实现

  • util目录:放置一些公共的工具类

  • view目录: 放置公共的自定义 view,这些自定义 view 脱离具体的业务联系,能够在各业务模块使用

  • base目录:用来放置一些基础的组件,如BaseActivity, BaseFragment, BaseViewModel

features 目录

按业务模块来划分不同的包,组织在该目录下。以下对代码的组织做一些说明

login 模块

在上面的目录中,由于涉及到用户登录相关的 DAO 操作都已经放到core目录下了, 假设可以满足要求,那么login模块下只有 UI 相关文件和ViewModel,xxxViewModelFactory

product 模块

这个模块是用来模拟某个模块下多个页面的场景。以商品列表页和详情页为例。由于 DAO 的操作可能会有重叠的场景,这里将它们的数据操作写在一起。UI 层面按功能再分为list, detail两个包。list, detail两个页面的 ViewModel 可以采用同一个 ViewModelFactory 来创建。


  • rendermodel包:这个目录下有一个需要单独说明一下,当我们从服务器拿到数据了如ProductInfo之后,将数据显示在页面上,我们显示在页面上的一些信息很有可能是需要根据ProductInfo的数据进行加工的。为此,我们定义一个ProductInfoRenderModel.java用来承载只需要显示在页面上的数据。ProductInfoRenderModel.java则放在rendermodel包下面。

personal 模块

personal模块中也会涉及到用户相关的信息,这也就是为什么一开始设计把用户相关信息的 dao 操作放到core目录下。personal模块下的xxViewModel如果要查用户相关的信息,可以直接调用core下面的UserRepository


至此,整个项目的大体架构便梳理完成了。采用这种方案将代码以功能模块进行划分,方便后期的维护。既使后续某个模块中进行了技术方案的改革,也能保证其影响的粒度最小。当然这里面主要是为了说明项目的主要结构,在实际项目中,除了这些,我们还会有adapter, 自己写的各种工具等等,这个就根据实际情况再自己分包了。接下来我们看一下涉及到的相关技术栈

技术栈

在这种项目架构中我们主要用到的技术栈有Jetpack中的ViewModel, ViewModelFactory, LiveData, ROOM,下面简单介绍一下这几种技术以及它们之间的整合。当然用于网络请求相关的我们可以用Okhttp, retrofit,此处就不介绍。

ViewModel

Jetpack组件中提供了ViewModel可以方便的将数据,对象与组件的生命周期绑定起来,方便进行组件间的数据共享,如一个activity中多fragment的情况。同时它可以有效的从架构层面上进行解藕,和 mvp 架构模式相比,可以大大减少接口/方法的个数。以登录为例,用户调用登录接口时需要调用 presenter.login 方法,login 成功后调用 view.loginSuccess方法。而采用ViewMode后,用户在登录时调用viewModel.login方法,登录成功后,更新ViewModel中的LiveData,然后在调用处观察LiveData做相应的行为就可以。

ViewModelProvider.Factory

用来创建ViewModel,ViewModel不可以自己创建,必须要借助ViewModelProvider.Factory来创建。在创建时通常为 ViewModel 指定数据仓库,如下:


public class LoginViewModelFactory implements ViewModelProvider.Factory {@NonNull@Overridepublic <T extends ViewModel> T create(@NonNull Class<T> modelClass) {if (modelClass.isAssignableFrom(LoginViewModel.class)) {return (T) new LoginViewModel(LoginRepository.getInstance(new LoginDataSource()));} else {throw new IllegalArgumentException("Unknown ViewModel class");


《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
浏览器打开:qq.cn.hn/FTe 免费领取
复制代码


}}}

LiveData

在数据发生变化时,需要通知给页面。通常可以采用接口的方工去做,但如果要观察的数据很多,就需要定义大量的接口,代码会十分冗余。为此, Google 提供了LiveData组件,它是一个可被观察的数据容器类,将数据包装起来,使数据成为被观察者,当该数据发生变化时,观察者能获得通知。


ViewModel是用来存储数据,LiveData的作用是在ViewModel发生变化时通知页面。因此, LiveData通常放在ViewModel中使用,用于包装ViewModel中那些需要被外界观察的数据。


我们来结合具体的例子(登录)看这三者的配合使用

示例

UI 层面(LoginActivity)

protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);binding = ActivityLoginBinding.inflate(getLayoutInflater());setContentView(binding.getRoot());...viewModel = new ViewModelProvider(this, new LoginViewModelFactory()).get(LoginViewModel.class);registerObserver();bindClickEvent();


}


private void registerObserver() {viewModel.getLoginResult().observe(this, new Observer<LoginResult>() {@Overridepublic void onChanged(LoginResult loginResult) {if (loginResult.success) {//登录成功}}});//出现异常 viewModel.exceptionLiveData.observe(this, new Observer<AppException>() {@Overridepublic void onChanged(AppException e) {Toast.makeText(LoginActivity.this, e.getBizMsg(), Toast.LENGTH_SHORT).show();}});


}


private void bindClickEvent() {binding.btnLogin.setOnClickListener(v-> {viewModel.login(phone, pwd, verifyCode, verifyKey);});}


  • 采用ViewBinding进行资源的绑定

  • 绑定ViewModel

用户头像

Android架构

关注

还未添加个人签名 2021.10.31 加入

还未添加个人简介

评论

发布
暂无评论
一种清晰, 便于扩展android项目架构方案