更有甚者在 BaseActivity 中定义了一切能想得到的子类变量等等,它现在确实成为了“上帝”,方便且无所不能的上帝!
随着项目的发展,它已经庞大到无法继续添加代码了,于是你写了很多很多的帮助类来帮助这个上帝瘦下来:
不经意之间,你已经埋下了黑色炸弹
看起来,业务逻辑被帮助类消化解决了,BaseActivity 中的代码减少了,不再那么“胖”了,帮助类缓解了它的压力,但随着项目的成长,业务的扩大,同时这些帮助类也慢慢变多变大,这时候又要按照业务继续拆分它们,维护成本好像又增加了,那些混乱并且难以复用的程序又回来了,我们的努力好像都白费了。
当然,一部分人会根据不同的业务功能分离出不同的抽象类,但相对那种业务场景下,它们仍是万能的。
无论什么理由这种创造“上帝类”的方式都应该尽量避免,我们不应该把重点放在编写那些大而全的类,而是投入精力去编写那些易于维护和测试的低耦合类,如果可以的话,最好不要让业务逻辑进入纯净的Android世界,这也是我一直努力的目标。
Clean architecture and The Clean rule
这种看起来像“地壳”的环形图就是Clean Architecture,不同颜色的“环”代表了不同的系统结构,它们组成了整个系统,箭头则代表了依赖关系。
我们已经选用 MVP 作为框架开发的架构了,这里就不深入的细说 Clean Architecture 架构了,Clean Architecture 的一些优势我们将揉入框架中,我们在框架的设计时应该遵从以下三个原则:
- 分层原则
- 依赖原则
- 抽象原则
接下来我就分别阐述一下,我对这些原则的理解,以及背后的原因。
分层原则
首先,框架应不去限制应用的具体分层,但是从多人协作开发的角度来说,通常我会将 Android 分为三层:
- 外层:事件引导层(View)
- 中间层:接口适配层(一般由 Dagger2 生成)
- 内层:业务逻辑层
看上面的三层我们很容易的就联想到 MVP 结构,下面我就来说一说这三层所包含的内容。
事件引导层
事引导层,它在框架中作为 View 层的另一展现,它主要负责 View 事件上的走向,例如 onClick、onTouch、onRefresh 等,负责将事件传递至业务逻辑层。
接口适配层
接口适配层的目的是连接业务逻辑与框架特定代码,担任外层与内层之间的桥梁,一般我们使用 Dagger2 进行生成。
业务逻辑层
业务逻辑层是框架中最重要的一部分,我们在这里解决所有业务逻辑,这一层不应该包含事件走向的代码,应该能够独立使用 Espresso 进行测试,也就是说我们的业务逻辑能够被独立测试、开发和维护,这是我们框架架构的主要好处。
依赖规则
依赖规则与 Clean Architecture 箭头方向保持一致,外层”依赖“内层,这里所说的“依赖”并不是指你在gradle中编写的那些 Dependency 语句,应该将它理解成“看到”或者“知道”,外层知道内层,相反内层不知道外层,或者说外层知道内层是如何定义抽象的,而内层却不知道外层是如何实现的。如前所述,内层包含业务逻辑,外层包含实现细节,结合依赖规则就是:业务逻辑既看不到也不知道实现细节。
对于项目工程来讲,具体的依赖方式完全取决于你。你可以将他们划入不同的包,通过包结构来管理它们,需要注意的是不要在内部包中使用外部包的代码。使用包来进行管理十分的简单,但同时也暴露了致命的问题,一旦有人不知道依赖规则,就可能写出错误的代码,因为这种管理方式不能阻止人们对依赖规则的破坏,所以我更倾向将他们归纳到不同的 Android module 中,调整 Module 间的依赖关系,使内层代码根本无法知道外层的存在。
抽象原则
所谓”抽象原则”,就是指从具体问题中,提取出具有共性的模式,再使用通用的解决方法加以处理。
例如,在我们开发中往往会碰到切换无网络、无数据界面,我们在框架中定义一个 ViewLayoutState`接口,一方面业务逻辑层可以直接使用它来切换界面,另一方面我们也可以在 View 层实现该接口,来重写切换不同界面的样式,业务逻辑层只是通知接口,它不清楚实现细节,也不用知道是如何实现的,甚至不知道面的载体是一个 Activity 或是一个 View。
这很好演示了如何使用抽象原则,当抽象与依赖结合后,就会发现使用抽象通知的业务逻辑看不到也不知道 ViewLayoutState
的具体实现,这就是我们想要的:业务逻辑不会注意到具体的实现细节,更不知道它何时会改变。抽象原则很好的帮我们做到了这一点。
Build this library
上面介绍了这么多设计准则,现在就来介绍下 Library 的设计,Library 只分为以下三个模块:
- Instance
- Util
- Base
这里是关于我自己的Android 学习,面试文档,视频收集大整理,有兴趣的伙伴们可以看看~
Util、Instance
Util、Instance 本质上的定位都为工具、辅助类,一种为“即用即走”的 static 工具类,例如判断文字是否为空等,一种为“长时间使用”的 instance 形式,例如 Activity 管理栈等。
Base
Base 主要工作是赋予了 BaseActivity 与 BaseFragment 很多不同的能力,上面我们提到了要避免创造“上帝”,但是在项目开发过程中很难避免这种情况,在 Library 中我们将 BaseView 所有能力抽取了出来,BaseActivity 与 BaseFragment 将只负责 View 的展示。
BaseActivity
BaseActivity 主要功能被分为:
- ActivityMvp 提供上下文
- ViewResult 提供跨界面刷新
- ActivityToolbarBase 提供顶部栏
- ViewLayoutState 提供切换界面
- LifecycleCallbackStrategy 生命周期回调管理
我们这里可以看到 BaseActivity 实现出的全部能力都与 View 相关,可能这会感到奇怪,不是有实现 ViewResult 跨界面刷新这个业务能力吗?我们来看下它是如何实现的。
/**
- 全局刷新
*/
@Override
public void resultAll() {
presenter.resultAll();
}
/**
- 部分刷新
- @param resultData
*/
@Override
public void result(Map<String, String> resultData) {
presenter.result(resultData);
}
这里可以看到,我们委托了 presenter 去实现,保证了 BaseActivity 只存在 View 相关的操作。
BaseListActivity
public abstract class ActivityListBase extends ActivityBase implements ActivityRecyclerMvp {
private RecyclerView rvIndexRecycler = null;
private SmartRefreshLayout srlRefresh = null;
private MultiTypeAdapter adapter = null;
private PresenterListBase presenter = null;
@Override
protected final int getLayout() {
return R.layout.activity_recycler_base;
}
@Override
protected final void onBeforeInit(Bundle savedInstanceState, Intent intent) {
presenter = getPresenter();
presenter.onCreate(savedInstanceState);
}
@Override
protected final void onInitComponent() {
rvIndexRecycler = findViewById(R.id.rv_index_recycler);
srlRefresh = findViewById(R.id.srl_index_refresh);
onInitRecycler();
onInitListComponent();
}
@Override
protected final void onInitViewListener() {
onInitRefresh();
}
@Override
protected final void onLoadHttpData() {
presenter.getData(PresenterListBase.INIT);
}
/**
- 初始化刷新布局
*/
protected final void onInitRefresh() {
srlRefresh.setOnLoadMoreListener(new OnLoadMoreListener() {
@Override
public void onLoadMore(RefreshLayout refreshLayout) {
presenter.getData(PresenterListBase.LOAD_MORE);
}
});
srlRefresh.setOnRefreshListener(new OnRefreshListener() {
@Override
public void onRefresh(RefreshLayout refreshLayout) {
srlRefresh.setEnableLoadMore(true);
srlRefresh.setNoMoreData(false);
presenter.getData(PresenterListBase.REFRESH);
}
});
}
/**
- 初始化Recycler
*/
protected final void onInitRecycler() {
RecyclerView.LayoutManager layoutManager = getLayoutManager();
rvIndexRecycler.setLayoutManager(layoutManager);
rvIndexRecycler.setHasFixedSize(false);
adapter = new MultiTypeAdapter(presenter.providerData());
addRecyclerItem(adapter);
rvIndexRecycler.setAdapter(adapter);
}
}
PresenterViewListImpl
public abstract class PresenterViewListImpl implements PresenterListBase {
protected ActivityRecyclerMvp viewBase = null;
// 布局内容
protected List data = null;
// 布局起点
protected int pageStart = 1;
// 加载更多
protected final int pageSize = PAGE_MAX_SIZE;
// 加载数据类型
protected @LoadDataState
int loadState;
public PresenterViewListImpl(ActivityListBase activityListBase) {
viewBase = activityListBase;
data = new ArrayList<>();
}
@Override
public void onCreate(Bundle savedInstanceState) {
}
@Override
public void result(Map<String, String> resultData) {
RunTimeUtil.runTimeException(“未实现result接口”);
}
@Override
public void resultAll() {
RunTimeUtil.runTimeException(“未实现resultAll接口”);
}
@Override
public void getData(int state) {
loadState = state;
switch (loadState) {
case INIT: {
processPreInitData();
break;
}
case REFRESH: {
pageStart = 1;
break;
}
case LOAD_MORE: {
pageStart = pageStart + 1;
break;
}
}
// 加载网络数据
loadData(new OnLoadDataListener() {
@Override
public void loadDataComplete(T t) {
handleLoadData(loadState, t);
}
@Override
public void loadDataError(@StringRes int errorInfo) {
handleLoadDataError(loadState, errorInfo);
}
@Override
public void loadDataEnd() {
handleLoadDataEnd();
}
});
}
/**
- 开始加载
*/
protected final void processPreInitData() {
pageStart = 1;
viewBase.switchLoadLayout();
}
/**
- 处理加载完成的数据
- @param loadState
- @param t
*/
protected void handleLoadData(int loadState, T t) {
switch (loadState) {
case INIT: {
viewBase.switchContentLayout();
initView(t);
break;
}
case REFRESH: {
viewBase.finishRefresh();
initView(t);
break;
}
case LOAD_MORE: {
viewBase.finishRefreshLoadMore();
break;
}
}
}
/**
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数Android工程师,想要提升技能,往往是自己摸索成长或者是报班学习,但对于培训机构动则几千的学费,着实压力不小。自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点,真正体系化!
由于文件比较大,这里只是将部分目录大纲截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
最后
**要想成为高级安卓工程师,必须掌握许多基础的知识。**在工作中,这些原理可以极大的帮助我们理解技术,在面试中,更是可以帮助我们应对大厂面试官的刁难。
,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且后续会持续更新**
如果你觉得这些内容对你有帮助,可以添加V获取:vip204888 (备注Android)
[外链图片转存中…(img-8VmpT0wm-1712048985383)]
最后
**要想成为高级安卓工程师,必须掌握许多基础的知识。**在工作中,这些原理可以极大的帮助我们理解技术,在面试中,更是可以帮助我们应对大厂面试官的刁难。
[外链图片转存中…(img-jyYuO5Vu-1712048985384)]
[外链图片转存中…(img-zJeRzaj1-1712048985384)]