2024年安卓最全Android组件化,android高级工程师面试题及答案详解

结尾

最后,针对上面谈的内容,给大家推荐一个Android资料,应该对大家有用。

首先是一个知识清单:(对于现在的Android及移动互联网来说,我们需要掌握的技术)

泛型原理丶反射原理丶Java虚拟机原理丶线程池原理丶
注解原理丶注解原理丶序列化
Activity知识体系(Activity的生命周期丶Activity的任务栈丶Activity的启动模式丶View源码丶Fragment内核相关丶service原理等)
代码框架结构优化(数据结构丶排序算法丶设计模式)
APP性能优化(用户体验优化丶适配丶代码调优)
热修复丶热升级丶Hook技术丶IOC架构设计
NDK(c编程丶C++丶JNI丶LINUX)
如何提高开发效率?
MVC丶MVP丶MVVM
微信小程序
Hybrid
Flutter

接下来是资料清单:(敲黑板!!!


1.数据结构和算法

2.设计模式

3.全套体系化高级架构视频;七大主流技术模块,视频+源码+笔记

4.面试专题资料包(怎么能少了一份全面的面试题总结呢~)

不论遇到什么困难,都不应该成为我们放弃的理由!共勉~

如果你看到了这里,觉得文章写得不错就给个赞呗?如果你觉得那里值得改进的,请给我留言。一定会认真查询,修正不足。谢谢。

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

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

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

“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_声明对外暴露接口,场景是:从首页模块发送数值给消息模块,我的模块可以从消息模块获取数值。接下来介绍两种通信的方式。

Arouter方案

在 export_biz_msg 组件下声明 IMsgProvider,此接口必须实现 IProvider 接口:

interface IMsgProvider: IProvider {

fun onCountFromHome(count: Int = 1)

}

然后在 biz_msg 组件里实现这个接口:

@Route(path = RouterPath.MSG_PROVIDER)

class MsgProviderImpl: IMsgProvider {

override fun onCountFromHome(count: Int) {

// 这里只是对数据进行分发,有监听计数的对象会收到

MsgCount.instance.addCount(count)

}

override fun init(context: Context?) {

// 对象被初始化时调用

}

}

在 biz_home 首页组件中发送计数:

val provider = ARouter.getInstance().build(RouterPath.MSG_PROVIDER)

.navigation() as IMsgProvider

provider.onCountFromHome(count)

可以看到其实和页面跳转的方式基本雷同,包括获取 Fragment 实例的方式也是这种。ARouter把所有通信的方式都用一种api实现,让使用者上手非常容易。

手动注册方案

手动注册的方法其实也挺方便而且不需要其他框架支持,只是多了一步。我们也是在 export_biz_msg 声明一个接口

interface IMsgService {

fun getMsgCount(): Int

}

同样也是在 biz_msg 实现这个接口:

class MsgServiceImpl: IMsgService {

override fun getMsgCount(): Int{

// 获取消息模块内count值

return MsgCount.instance.getCount()

}

}

这里增加了一步手动注册:

class MsgApp: BaseApp {

override fun onCreate(application: Application) {

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

MsgServiceUtil.setMsgService(MsgServiceImpl())

}

}

object MsgServiceUtil {

private lateinit var msgService:

fun setMsgService(msgService: IMsgService) {

this.msgService = msgService

}

fun getMsgCount(): Int{

return msgService.getMsgCount()

}

}

我们写一个工具类 MsgServiceUtil 用来注册 IMsgService 服务,同时在biz_msg组件初始化的时候调用 setMsgService 真正注册进去。

在biz_me组件内调用:

MsgServiceUtil.getMsgCount()

这里涉及消息组件的初始化,这个初始化时机就是应用Application创建的时机,下一节就是介绍如何进行Application生命周期的分发。

关于 Fragment 的获取,实际上如果采用ARouter方式,那么Fragment获取和Activity跳转基本一致,只需要用@Route 注解到Fragment类上。如果不用ARouter,那么就和手动注册下沉接口的通信方式一摸一样。

Application生命周期分发


当 app 壳工程启动Application初始化时要通知到其他组件初始化一些功能。这里提供一个简单的方式。

首先我们在 module_common 公共库内声明一个接口 BaseApp:

interface BaseApp {

fun onCreate(application: Application)

}

然后每个组件都要创建一个 App 类实现此接口,比如biz_home组件:

class HomeApp: BaseApp {

override fun onCreate(application: Application) {

// 初始化都放在这里

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

}

}

剩下最后一步就是从 app 壳工程分发 application 的生命周期了,这里用到反射技术:

val moduleInitArr = arrayOf(

“com.sun.module_common.AppCommon”,

“com.sun.biz_home.HomeApp”,

“com.sun.biz_msg.MsgApp”,

“com.sun.biz_me.MeApp”

)

class App: Application() {

override fun onCreate() {

super.onCreate()

initModuleApp(this)

}

private fun initModuleApp(application: Application) {

try {

for(appName in moduleInitArr) {

val clazz = Class.forName(appName)

val module = clazz.getConstructor().newInstance() as BaseApp

module.onCreate(application)

}

}catch (e: Exception) {

e.printStackTrace()

}

}

}

我们只需要知道的每个实现 BaseApp 接口的类的全限定名并写到moduleInitArr数组里,然后通过反射获取 Class 对象从而获取构造函数创建实体对象,最后调用 BaseApp 的 onCreate 方法将 application 传入,每个Application生命周期的方法都可以通过这种方式传递。

个人觉得这种方式已经能满足了,只是每增加一个组件除了要实现 BaseApp 接口外,还要在壳工程moduleInitArr数组内增加类的全限定名,修改壳工程会有些频繁。若想要无侵入式得分发Application分发生命周期,可以看下 AppLifecycleMgr


  • 组件化项目如果使用到 Butterknife,可能会造成 R 文件找不到资源,需要配置Butterknife插件,使用R2来替换。但现在已经不推荐用Butterknife了,推荐使用官方的ViewBinding。

总结


本文的案例请查看 ModularLearning

组件化有三大优势:提高编译速度、提升协作效率、功能重用。

本文重点讲了组件化学习的几个重点:

  • 组件化架构。每个组件都要严格区分边界,可以分为业务组件、业务基础组件、基础组件。

小结

有了这么多优秀的开发工具,可以做出更高质量的Android应用。

当然了,“打铁还需自身硬”,想要写出优秀的代码,最重要的一点还是自身的技术水平,不然用再好的工具也不能发挥出它的全部实力。

在这里我也分享一份大佬自己收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

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

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

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

优势:提高编译速度、提升协作效率、功能重用。

本文重点讲了组件化学习的几个重点:

  • 组件化架构。每个组件都要严格区分边界,可以分为业务组件、业务基础组件、基础组件。

小结

有了这么多优秀的开发工具,可以做出更高质量的Android应用。

当然了,“打铁还需自身硬”,想要写出优秀的代码,最重要的一点还是自身的技术水平,不然用再好的工具也不能发挥出它的全部实力。

在这里我也分享一份大佬自己收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。

总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习

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

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

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

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值