2024年Android最新如何学好设计,做好架构? 核心思想才是关键(2),2024年最新Android自学资料

本文介绍了Android开发的关键知识点结构,强调了系统化学习的重要性,提供了面试题库和不同公司的面试经验。同时,文章深入探讨了设计原则如开闭原则、迪米特法则等,并阐述了如何通过设计模式提升代码质量和架构设计。最后,鼓励IT从业者和新人加入技术交流社区,共同学习成长。
摘要由CSDN通过智能技术生成

学习福利

【Android 详细知识点思维脑图(技能树)】

其实Android开发的知识点就那么多,面试问来问去还是那么点东西。所以面试没有其他的诀窍,只看你对这些知识点准备的充分程度。so,出去面试时先看看自己复习到了哪个阶段就好。

虽然 Android 没有前几年火热了,已经过去了会四大组件就能找到高薪职位的时代了。这只能说明 Android 中级以下的岗位饱和了,现在高级工程师还是比较缺少的,很多高级职位给的薪资真的特别高(钱多也不一定能找到合适的),所以努力让自己成为高级工程师才是最重要的。

这里附上上述的面试题相关的几十套字节跳动,京东,小米,腾讯、头条、阿里、美团等公司19年的面试题。把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节。

由于篇幅有限,这里以图片的形式给大家展示一小部分。

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

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

return userInfo

}

fun post(){

val userInfo = getUserInfo()

//将个人信息提交至网络

http.request(userInfo.age,userInfo.name,…)

}

三步操作被拆至三个函数 互不影响,从根本上杜绝因改动带来的一系列问题。所以使用面向对象语言开发时,不要急着写代码,要优先考虑下模块、类、函数...的设计是否足够单一

2.2 开闭原则

一句话概括开闭原则:对扩展开放,修改关闭。它即充分诠释抽象、多态特性,又是多数行为型设计模式的基础,遍布于各大优秀框架之中,是最重要的一条设计原则,仅这一条原则就能把你的设计能力提高40%

举个例子让大家感受一下:

需求:通过SQLite做CRUD操作

class SQLiteDao{

public void insert() {

//通过SQLite做insert

}

public void delete() {

//通过SQLite做insert

}

}

SQLiteDao dao = new SQLiteDao();

dao.insert();

以上是最简单粗暴的写法,但存在一个致命问题,如果某一天想替换SQLite业务层基本要动一遍,改动就存在出错的可能,并且需要做大量的重复操作

面对以上问题可以利用抽象、多态特性基于开闭原则做出重构,代码如下:

interface IDao{

void insert();

void delete();

}

class SQLiteDao implements IDao{

@Override

public void insert() {

//通过SQLite做insert

}

@Override

public void delete() {

//通过SQLite做insert

}

}

class RoomDao implements IDao{

@Override

public void insert() {

//通过Room做insert

}

@Override

public void delete() {

//通过Room做delete

}

}

//扩展点

IDao dao = new SQLiteDao();

dao.insert();

  • 定义功能接口IDao

  • 定义类SQLiteDao、RoomDao并实现IDao的功能

  • 业务层基于接口IDao进行编程

重构后,当需要将SQLite替换至Room时,只需将注释扩展点SQLiteDao替换成RoomDao即可,其他地方完全不用改动。这就是所谓的扩展开放,修改关闭

业务不断迭代情况下,唯一不变的就是改变,这种背景下我们能做的只有在代码中基于开闭原则多留扩展点以不变应万变。

2.3 迪米特法则

基本概念:不该有直接依赖关系的模块不要有依赖。有依赖关系的模块之间,尽量只依赖必要的接口。

迪米特法则很好理解并且非常实用,违背迪米特法则会产生什么问题?还以2.1面向过程代码举例:

class Wallet{

/**

  • 余额

*/

int balance;

/**

  • 存钱

*/

void saveMoney(int money){

balance += money;

}

/**

  • 花钱

*/

void spendMoney(int money){

balance -= money;

}

}

Wallet的设计违背了迪米特法则,毕竟外部只需要savespend功能,将balance暴漏使用者就有权限直接修改其值,可能会对整个Wallet功能造成影响。此时应基于迪米特法则Wallet进行改造,将balance通过封装特性增加private修饰符

迪米特法则单一设计原则很像,前者符合松耦合后者符合高内聚

2.4 接口隔离原则

基本概念:接口的调用者不应该依赖它不需要的接口。

乍一看与迪米特法则很相似。先来看下什么样的接口违背接口隔离原则

interface Callback{

/**

  • 点击事件回调方法

*/

void clickCallback();

/**

  • 滚动事件回调方法

*/

void scrollCallback();

}

接口Callback包含点击、滚动两个回调方法,面临的问题有两个:

  • 某些特定场景使用者只需要依赖点击回调,那滚动回调便成了多余,把外部不需要的功能暴露出来就存在误操作的可能。

  • 点击滚动本来就是两种特性,强行揉到一块只能让接口更臃肿,进而降低其复用性

根据接口隔离原则改造后如下:

interface ClickCallback{

/**

  • 点击事件回调方法

*/

void clickCallback();

}

interface ScrollCallback{

/**

  • 滚动事件回调方法

*/

void scrollCallback();

}

基于单一设计原则点击滚动拆分成两个接口,将模块间隔离的更彻底。并且由于粒度更细,所以复用性也更高

接口隔离原则迪米特法则目的很相似,都可以降低模块间依赖关系。但接口隔离更侧重于设计单一接口,提升复用性并间接降低模块间依赖关系,而迪米特法则是直接降低模块间依赖关

2.5 里氏替换原则

基本概念:

设计子类的时候,要遵守父类的行为约定。父类定义了函数的行为约定,子类可以改变函数的内部实现逻辑,但不能改变函数原有的行为约定。

里氏替换非常简单并且很容易遵守,在使用继承时,允许复写父类方法,但不要改变其功能。比如自定义View,子类的onMeasure中一定要调用setMeasureaDimission()方法(或者直接使用super),否则会影响父类方法功能(会抛异常),也既违背了里氏替换原则。

2.6 依赖倒置原则

控制反转: 提及依赖倒置便不得不提控制反转,一句话概括:将复杂的程序操作控制权由程序员交给成熟的框架处理,程序员->成熟的框架为反转,框架应暴露出扩展点由程序员实现 想详细了解可至 关于Android架构,你是否还在生搬硬套? 2.1章节查看

什么是依赖倒置?

高层模块(使用者)不应依赖低层模块(被使用者),它们共同依赖同一个抽象,抽象不要依赖具体实现细节,具体实现细节依赖抽象。

其实核心点就是基于接口而非实现编程2.2数据库案例也符合依赖倒置原则,高层模块(业务层)不依赖于低层模块(SQLiteDao/RoomDao),而是依赖于抽象(IDao),可见依赖倒置也是开闭原则扩展而来。 区别是依赖倒置更侧重于指导框架的设计,框架层应该尽量将更多的细节隐藏在内部,对外只暴露抽象(抽象类/接口),指导框架设计这方面核心就是控制反转

3. 设计模式只是设计原则的产物而已


设计模式共有23种,详细描述都能出一本书出来。本小结仅会分享一些通用的思路,个人认为还是比较硬核的,毕竟设计主要还是思想,而非生搬硬套

3.1 设计模式该怎么去学?

本小节会分析几个常见的设计模式核心思想以及设计背景,用于抛砖引玉

工厂模式

基本概念:用于创建复杂对象

创建复杂对象常规写法如下:

class B{

}

class D{

void test(){

B b = …(创建B的过程很复杂)

}

}

在使用的地方直接创建,如果直接new倒也没啥问题,但如果创建过程过于复杂,当修改创建过程时就会影响到test(),进而存在一些未知的隐患。

这一问题可通过迪米特法则进行改造:

class FactoryB{

static B createB(){

…(B创建过程)

return b;

}

}

class D{

void test1(){

B b = FactoryB.create();

}

}

B的创建本身就于调用者无关,将创建过程转移到类FactoryB中,根本上避免了创建过程对调用者的影响。改造后就是一个标准的简单工厂模式,所以简单工厂模式的核心思想就是迪米特法则

观察者模式

基本概念:当一个对象发生改变时需要通知到另一个对象

粗暴写法:

/**

  • 观察者

*/

class Observer{

/**

  • 接收通知

*/

void receive(){

//具体逻辑

}

}

/**

  • 被观察者

*/

class Observable{

/**

  • 发送通知

*/

void send(){

Observer observer = new Observer();

observer.receive();

}

}

Observable(被观察者)内部直接持有Observer(观察者),在合适的时机发出通知,但这种写法有两个很明显的问题:

  • 扩展性差:当存在多个观察者Observer1,Observer2...时,Observable需要逐个手动创建发出通知

  • 耦合性强:Observable直接持有Observer对象,而Observer可能暴露出一些Observable不需要的属性/方法,存在误操作的风险

面对以上两个问题可以利用开闭原则接口隔离原则进行改造:

interface IObserver{

/**

  • 接收通知

*/

void receive();

}

class Observable{

/**

  • 观察者集合

*/

private final List observers = new ArrayList<>();

/**

  • 发送通知

*/

void send(){

for (IObserver observer : observers){

observer.receive();

}

}

}

以上是一个标准的观察者模式。通过接口隔离原则设计IObserver接口保证其单一性,避免模块之间依赖关系过强造成的安全隐患,解决了耦合性强问题。通过开闭原则维护一个observers,当新增观察者时只需添加到observers即可,符合扩展开放、修改关闭,解决类扩展性差问题。

所以开闭原则,接口隔离原则是观察者模式扩展性强,耦合性低的根本原因呐

以上三个案例足以表明设计模式的核心就是设计原则呐,所以学会设计模式的窍门就是先掌握设计原则

3.2 “盐加少许” 只可意会

据我所知有一部分小伙伴觉得用设计模式很酷,以至于拿着锤子看什么都是钉子,很简单的代码还非要用几个设计模式包装下。还有另一部分小伙伴对设计模式理解不够深刻,把握不好应用场景,经常生搬硬套做出多余的设计。以上两种现象 不但解决不了任何问题反而会降低代码的可读性

究竟什么场景下需要用设计模式呢?关于这个问题我只能回答合适的场景,因为它根本没有一个固定答案。就如同老师傅做饭时讲的少许盐、少许油一样,因为不同的食材需要的油盐不一样,所以不好去量化,只能根据自己的经验去放。回归到代码中也是一样的,我们在实践中需要不断思考,尝试去发现开发中的痛点,设计模式就是用来解决这些痛点的,所以只有理清背景才能将设计模式用的恰到好处

3.3 自创设计模式

有一说一23种设计模式我也不是全懂,但由于我懂设计原则我一样可以写出易维护的代码,甚至自创设计模式

在接触LiveData之前,其实我已经有意无意感受到了数据驱动的思想,在传统的MVP模式下我会在View层事先写好对应UI渲染逻辑,Presenter由接口进行驱动(这其实也是数据驱动UI)。 当LiveData/DataBinding走进视线并且大家开始讨论数据驱动UI时,那一刻我仿佛找到了组织,自己的想法终于得到了验证。之所以我能够有意无意遵守数据驱动UI是因为我原本就掌握了控制反转思想

阅读Retrofit源码前我甚至不知道门面模式的存在,但我依旧能理解ApiService奥妙之所在,无非就是想将Retrofit的实现尽量屏蔽在其内部,尽可能降低模块间依赖关系,符合迪米特法则同时也是门面模式的一种写法。

说了这么多还是想告诉大家:设计原则才是根本

4. 如何做好架构?


掌握设计原则可以写出扩展性强、复用性高...的代码

掌握设计模式可以设计出易用性强、安全性高...成熟的框架

掌握设计原则、设计模式,可以设计出容错率更高的架构

那什么是架构?

架构是一个很笼统的概念,上至框架选型下至业务代码都能称为架构的一部分,比喻到盖房子 设计图,打地基,选料…都能称之为架构,总之能够提升项目稳定性以及开发效率就是好架构。好的架构不是一蹴而就,而是根据面临的问题不断添砖加瓦

架构是如何衍变的?

  • 远古时代,基于Activity和XML开发,XML这种结构可以天然的将视图与Activity隔离,看起来很美妙,我也很开心…
  • 随着业务的发展,Activity代码不断壮大,各种逻辑全都揉到一块,常常改一处崩多处。我觉得不能再拖了,得赶紧基于单一设计原则将代码进行模块化。模块化后效果很明显,莫名其妙的bug少了很多…
  • 某一天网络请求时发现参数一直对不上,各种排查才发现原来是修改某个View时对应的数据却忘记改了,这个问题真的很头痛。偶然间发现LiveData、DataBinding,这玩意基于控制反转+观察者设计 改变数据就能修改UI,那我肯定毫不犹豫引入到项目中啊。从此我再也不用担心数据UI一致性问题了…
  • 数年后,项目工程逐渐庞大,编译一次都要好几分钟,找个文件找半天还容易改错,令大家苦不堪言。听说Android可以依据单一原则将代码拆分至多个module中并可以单独运行,试了试果然可以…
  • 最近有同事经常跟我抱怨:“每个Activity都有好多重复代码啊,而且一不留神容易错写、忘写”,这让我想到了模版设计模式,将通用功能封装在内部并暴露一些抽象方法(钩子方法),新来的同事也变得开心的了,基于这套模板他可以无障碍开发…
  • 未完待续…

以上是一个简单架构的衍变过程,选用的每一个库都是基于设计原则,设计模式拓展出来用来解决开发痛点的。但是团队开发人员水平可能参次不齐,不一定能领悟到架构的含义,仅从口头上约束可能作用不大,此时一般会通过模板模式将通用信息做封装,在内部协调好各模块间关系,并暴露出对应的泛型、抽象方法(钩子),这样开发人员在使用模板类的时候就会被强制遵守现有的规则。

tips

最后

由于题目很多整理答案的工作量太大,所以仅限于提供知识点,详细的很多问题和参考答案我都整理成了 PDF文件

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

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

模式`,将通用功能封装在内部并暴露一些抽象方法(钩子方法),新来的同事也变得开心的了,基于这套模板他可以无障碍开发…

  • 未完待续…

以上是一个简单架构的衍变过程,选用的每一个库都是基于设计原则,设计模式拓展出来用来解决开发痛点的。但是团队开发人员水平可能参次不齐,不一定能领悟到架构的含义,仅从口头上约束可能作用不大,此时一般会通过模板模式将通用信息做封装,在内部协调好各模块间关系,并暴露出对应的泛型、抽象方法(钩子),这样开发人员在使用模板类的时候就会被强制遵守现有的规则。

tips

最后

由于题目很多整理答案的工作量太大,所以仅限于提供知识点,详细的很多问题和参考答案我都整理成了 PDF文件

[外链图片转存中…(img-7BmSO3WD-1714974330908)]

[外链图片转存中…(img-r9eiLsQf-1714974330908)]

[外链图片转存中…(img-x5yCinPi-1714974330909)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

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

  • 19
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值