2024年Android最全Android组件化学习,2024年最新应届毕业生硬件工程师面试题及答案

本文讨论了面试中关于Handler机制的重要性,分享了组件化技术在Android开发中的应用,包括组件拆分、独立调试、页面跳转和数据通信的方法,以及美团、阿里等公司的面试题集锦。同时强调了系统化学习和团队协作对于技术提升的价值。
摘要由CSDN通过智能技术生成

最后

今天关于面试的分享就到这里,还是那句话,有些东西你不仅要懂,而且要能够很好地表达出来,能够让面试官认可你的理解,例如Handler机制,这个是面试必问之题。有些晦涩的点,或许它只活在面试当中,实际工作当中你压根不会用到它,但是你要知道它是什么东西。

最后在这里小编分享一份自己收录整理上述技术体系图相关的几十套腾讯、头条、阿里、美团等公司2021年的面试题,把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包含知识脉络 + 诸多细节,由于篇幅有限,这里以图片的形式给大家展示一部分。

还有 高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

【算法合集】

【延伸Android必备知识点】

【Android部分高级架构视频学习资源】

**Android精讲视频领取学习后更加是如虎添翼!**进军BATJ大厂等(备战)!现在都说互联网寒冬,其实无非就是你上错了车,且穿的少(技能),要是你上对车,自身技术能力够强,公司换掉的代价大,怎么可能会被裁掉,都是淘汰末端的业务Curd而已!现如今市场上初级程序员泛滥,这套教程针对Android开发工程师1-6年的人员、正处于瓶颈期,想要年后突破自己涨薪的,进阶Android中高级、架构师对你更是如鱼得水,赶快领取吧!

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

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

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

**提高协作效率:**每个组件都有专人维护,不用关心其他组件是怎么实现的,只需要暴露对方需要的数据。测试也不需要整个回归,只需要重点测试修改的组件即可。 **功能重用:**一次编码处处复用,再也不需要复制代码了。尤其是基础组件和业务基础组件,基本上调用者根据文档就可以一键集成和使用。

前面有提到非大型项目一般不会进行组件化,但是就像上面提到的功能重用,这个优势并不是只能用到大型项目 。我们可以在写需求或库时完全可以拥有组件化思想,把它们单独写成一个基础组件或业务基础组件。当第二个项目来的时候正好也需要这个组件,那我们就省去了拆出这个组件的时间(因为写需求的时候很可能会造成大量耦合,后续拆分要花费时间),比如登录组件,分享组件等等都是可以在一开始就写成组件的。

组件化需解决的问题

说来说去就是解耦,那么有哪些方面是需要解耦的呢?基础组件和业务基础组件不存在耦合

业务组件如何实现单独调试?

业务组件间没有依赖,如何实现页面跳转?

业务组件间没有依赖,如何实现数据通信?

壳工程Application生命周期如何下发?

接下来通过案例一一介绍如何去解决这些点,文中项目 ModularLearning

独立调试


其实当我们把业务组件拆分开后,独立调试也就快完成了,我们只需要再多加一点料就可以了。

单工程方案

所谓的单工程方案就是把所有组件都放到一个工程下,先看一下整体的目录:

ps:module_ 开头表示基础组件,fun_ 前缀表示业务基础组件,biz_前缀表示业务组件,export_前缀表示业务组件暴露接口。

单工程利弊分析:

  • 利:一个模块修改后只需要编译一下,依赖它的其他模块就能马上感知到变化。

  • 弊:没能做到完全的职责拆分,每个模块的开发者都有修改其他模块的权限。

首先在 gradle.properties 文件内声明一个变量:

// gradle.properties

isModule = true

复制代码

isModule 为 true 时表示组件可以作为 apk 运行起来,false 表示组件只能作为 library。我们根据需要改变这个值后同步下gradle即可。

然后在某个 module 的 build.gradle 文件内用这个变量做三个地方的判断:

// build.gradle

// 区分是应用还是库

if(isModule.toBoolean()) {

apply plugin: ‘com.android.application’

}else {

apply plugin: ‘com.android.library’

}

android {

defaultConfig {

// 如果是应用需要指定application

if(isModule.toBoolean()) {

applicationId “com.xxx.xxx”

}

}

sourceSets {

main {

// 应用和库的AndroidManifest文件区分

if(isModule.toBoolean()) {

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

}else {

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

}

}

}

}

由于library是不需要 Application 和启动Activity页,所以我们要区分这个文件,应用manifest指定的路径没有特定,随意找个路径创建即可。在应用AndroidManifest.xml里我们要设置启动页:

<manifest xmlns:android=“http://schemas.android.com/apk/res/android”

package=“com.sun.biz_home”>

<application

android:allowBackup=“true”

android:label=“@string/home_app_name”

android:supportsRtl=“true”

android:theme=“@style/home_AppTheme”>

library 的 AndroidManifest.xml 不需要这些:

<manifest xmlns:android=“http://schemas.android.com/apk/res/android”

package=“com.sun.biz_home”>

gradle 依赖 module 的方式主要有两种:

  • implementation: A implementation B,B implementation C, 但 A 不能访问到 C 的东西。

  • api:A api B,B api C,A能访问到C的东西。

一般来说我们只需要使用 implementation 即可,api 是会造成项目编译时间变长,而且会引入该模块不需要的功能,代码之间耦合变得严重了。不过 module_common 是统一了基础组件版本的公共库,所有组件都应需要依赖它并拥有基础组件的能力,所以基本每个业务组件和业务基础组件都应该依赖公共库:

dependencies {

implementation project(‘:module_common’)

}

而 common 组件依赖基础组件应该是用 api,因为把基础组件的能力传递给上层业务组件:

dependencies {

api project(‘:module_base’)

api project(‘:module_util’)

}

多工程方案

多工程就是每个组件都是一个工程,例如创建一个工程后 app 作为壳组件,它依赖 biz_home 运行,因此不需要 isModule 来控制独立调试,它本身就是一个工程可以独立调试。

多工程的利弊就是和单工程相反的:

  • 利:做到职责完全拆分,其他项目复用更加方便,直接一行依赖引入。

  • 弊:修改后需要上传到maven仓库,其他工程再次编译后才能感知到变化,多了上传和编译的时间。

多工程组件依赖需要用到maven仓库。把每个组件的aar上传到公司内网的maven仓库,然后像这样去依赖:

implementation ‘com.xxx.xxx:module_common:1.0.0’

我们在学习组件化的过程中可以搭建一个maven仓库并把aar上传到我们自己搭建的maven仓库,这里是一篇搭建maven仓库和上传aar的介绍。注意一点,我们在开发阶段用snapshot快照版本不更改版本号上传,在发布release正式版时增加版本号并上传。

我们把三方库统一放到 config.gradle 内管理:

ext {

dependencies = [

“glide”: “com.github.bumptech.glide:glide:4.12.0”,

“glide-compiler”: “com.github.bumptech.glide:compiler:4.12.0”,

“okhttp3”: “com.squareup.okhttp3:okhttp:4.9.0”,

“retrofit”: “com.squareup.retrofit2:retrofit:2.9.0”,

“retrofit-converter-gson” : “com.squareup.retrofit2:converter-gson:2.9.0”,

“retrofit-adapter-rxjava2” : “com.squareup.retrofit2:adapter-rxjava2:2.9.0”,

“rxjava2”: “io.reactivex.rxjava2:rxjava:2.2.21”,

“arouter”: “com.alibaba:arouter-api:1.5.1”,

“arouter-compiler”: “com.alibaba:arouter-compiler:1.5.1”,

// our lib

“module_util”: “com.sun.module:module_util:1.0.0”,

“module_common”: “com.sun.module:module_common:1.0.0”,

“module_base”: “com.sun.module:module_base:1.0.0”,

“fun_splash”: “com.sun.fun:fun_splash:1.0.0”,

“fun_share”: “com.sun.fun:fun_share:1.0.0”,

“export_biz_home”: “com.sun.export:export_biz_home:1.0.0”,

“export_biz_me”: “com.sun.export:export_biz_me:1.0.0”,

“export_biz_msg”: “com.sun.export:export_biz_msg:1.0.0”,

“biz_home”: “com.sun.biz:biz_home:1.0.0”,

“biz_me”: “com.sun.biz:biz_me:1.0.0”,

“biz_msg”: “com.sun.biz:biz_msg:1.0.0”

]

}

这样方便版本统一管理, 然后在根目录的 build.gradle 内导入:

apply from: ‘config.gradle’

最后在各自的模块引入依赖,比如在 module_common 中这么引入依赖即可。

dependencies {

api rootProject.ext.dependencies[“arouter”]

kapt rootProject.ext.dependencies[“arouter-compiler”]

api rootProject.ext.dependencies[“glide”]

api rootProject.ext.dependencies[“okhttp3”]

api rootProject.ext.dependencies[“retrofit”]

api rootProject.ext.dependencies[“retrofit-converter-gson”]

api rootProject.ext.dependencies[“retrofit-adapter-rxjava2”]

api rootProject.ext.dependencies[“rxjava2”]

api rootProject.ext.dependencies[“module_util”]

api rootProject.ext.dependencies[“module_base”]

}

个人觉得多工程适合"很大"的工程,每个业务组件可能都需要一个组开发,类似淘宝这样的app。但这只是针对业务组件来说的,业务基础组件和基础组件修改的频率不会很大,最好都是单工程上传至maven仓库来使用。本文的例子是为了方便所以把所有组件写到一起了,最好的方式就是把 fun_ 和 module_ 开头的组件都拆分成单工程独立开发,业务组件写到一个工程内。

页面跳转


做完组件之间的隔离后,暴露出来最明显的问题就是页面跳转和数据通信的问题。一般来说,页面跳转都是显示startActivity跳转,在组件化项目内就不适用了,隐式跳转可以用,但每个Activity都要写 intent-filter 就显得有点麻烦,所以最好的方式还是用路由框架。

实际上市面已经有比较成熟的路由框架专门就是为了组件化而生的,比如美团的WMRouter,阿里的ARouter等,本例使用 ARouter 框架,看下ARouter页面跳转的基本操作。

首先肯定是引入依赖,以 module_common 引入ARouter举例,build.gradle 应该添加:

android {

defaultConfig {

javaCompileOptions {

annotationProcessorOptions {

arguments = [AROUTER_MODULE_NAME: project.getName()]

}

}

}

compileOptions {

sourceCompatibility JavaVersion.VERSION_1_8

targetCompatibility JavaVersion.VERSION_1_8

}

}

dependencies {

api rootProject.ext.dependencies[“arouter”]

kapt rootProject.ext.dependencies[“arouter-compiler”]

}

kapt注解依赖没有办法传递,所以我们不可避免得需要在每个模块都声明这些配置,除了 api rootProject.ext.dependencies["arouter"] 这行。然后需要全局注册 ARouter,我是在 module_common 统一注册的。

class AppCommon: BaseApp{

override fun onCreate(application: Application) {

MLog.d(TAG, “BaseApp AppCommon init”)

initARouter(application)

}

private fun initARouter(application: Application) {

if(BuildConfig.DEBUG) {

ARouter.openLog()

ARouter.openDebug()

}

ARouter.init(application)

}

}

接着我们在 module_common 模块内声明一个路由表用作统一管理路径。

// RouterPath.kt

class RouterPath {

companion object {

const val APP_MAIN = “/app/MainActivity”

const val HOME_FRAGMENT = “/home/HomeFragment”

const val MSG_FRAGMENT = “/msg/MsgFragment”

const val ME_FRAGMENT = “/me/MeFragment”

const val MSG_PROVIDER = “/msg/MsgProviderImpl”

}

}

然后在MainActivity类文件上进行注解:

@Route(path = RouterPath.APP_MAIN)

class MainActivity : AppCompatActivity() {

}

任意模块只需要调用 ARouter.getInstance().build(RouterPath.APP_MAIN).navigation() 即可实现跳转。如果我们要加上数据传递也很方便:

ARouter.getInstance().build(RouterPath.APP_MAIN)

.withString(“key”, “value”)

.withObject(“key1”, obj)

.navigation()

然后在MainActivity使用依赖注入接受数据:

class MainActivity : AppCompatActivity() {

@Autowired

@JvmField

var key: String = “”

}

ARouter 基本能满足所有的跳转需求。

组件间通信


前面已经提到组件通信是通过”间接依赖“解耦的,也就是会出现第三个"组件"作为中间件。如果是按照顺序阅读本文的话,就会发现工程目录中有出现 export_ 前缀的模块,这个模块其实是暴露了组件对外的接口,比如首页模块biz_home对应的"下沉接口"模块就是 export_biz_home,外部想要访问 biz_home 的功能只需要依赖 export_biz_home。这种每一个组件对应一个"下沉接口"组件看起来有点冗余,但若是项目很大的时候,这种方式是比较清晰的。当然若项目较小,可以考虑把所有暴露的接口整合到一个”下沉接口“组件,也就是本文例子的 module_base,然后module_common依赖module_base,这样每个模块都能使用任意模块的向外暴露的功能了,如果觉得下沉接口过多或者你需要严格界定每个组件,那就不推荐用这种方式了。

我们现在假设都是在export_声明对外暴露接口,场景是:从首页模块发送数值给消息模块,我的模块可以从消息模块获取数值。接下来介绍两种通信的方式。

最后

代码真的是重质不重量,质量高的代码,是当前代码界提倡的,当然写出高质量的代码肯定需要一个相当高的专业素养,这需要在日常的代码书写中逐渐去吸收掌握,谁不是每天都在学习呀,目的还不是为了一个,为实现某个功能写出高质量的代码。

所以,长征路还长,大家还是好好地做个务实的程序员吧。

最后,小编这里有一系列Android提升学习资料,有兴趣的小伙伴们可以来看下哦~

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

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

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

目较小,可以考虑把所有暴露的接口整合到一个”下沉接口“组件,也就是本文例子的 module_base,然后module_common依赖module_base,这样每个模块都能使用任意模块的向外暴露的功能了,如果觉得下沉接口过多或者你需要严格界定每个组件,那就不推荐用这种方式了。

我们现在假设都是在export_声明对外暴露接口,场景是:从首页模块发送数值给消息模块,我的模块可以从消息模块获取数值。接下来介绍两种通信的方式。

最后

代码真的是重质不重量,质量高的代码,是当前代码界提倡的,当然写出高质量的代码肯定需要一个相当高的专业素养,这需要在日常的代码书写中逐渐去吸收掌握,谁不是每天都在学习呀,目的还不是为了一个,为实现某个功能写出高质量的代码。

所以,长征路还长,大家还是好好地做个务实的程序员吧。

最后,小编这里有一系列Android提升学习资料,有兴趣的小伙伴们可以来看下哦~

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值