现代化 Android 开发:组件化与模块化的抉择

简介: 本文为现代化 Android 开发系列文章第四篇。

项目初始的时候,一般都是使用一个分层架构,接入各种框架,然后就写业务代码。但如果项目慢慢变大,那就会出现很多项目管理的问题,诸如:

1.代码复用与抽象问题

2.编译速度问题

3.版本迭代速度问题


所以组件化、模块化、动态化、插件化、跨平台等各种技术就会被提及被重视。

组件化


我们已经度过了洪荒时期的 Android 开发,因为官方已经给出了非常优秀的 Jetpack 组件库,所以我们目前更多的是制作业务型的组件与抽象,去解决某一类场景或者大家都可能用到而写起来又繁琐又容易出错的场景。


写组件也不是一件容易的事情。


首先,开发组件得意识到某些场景是可以抽象的,如果抱着代码能跑起来就行了的态度去编程,那基本不可能想到去抽象的,也是最容易陷入代码陷阱无法自拔的。即使是最简单的抽取,很多时候也是宁愿不厌其烦的复制粘贴,也不愿意去抽取一个公共方法。然后等别人开发出了组件后,除了感叹真香,也就没什么其它感悟了。


其次,开发组件得设计出好用而简洁的 API,很多童鞋,除了数据实体类和各种 Manager 类,基本不会再定义新的数据结构了,就更别提设计接口供其它人使用了。


然后,开发组件得想好组件的配置,提升组件的灵活性,产品最喜欢复用以前的逻辑而再加上一点点的不同,如果不能够配置,那组件的局限性就大打折扣了。


最后,开发组件还得做好边缘情况的处理、异常的处理。组件会被各种人以各种姿势调用,所以各种想不到的场景都有可能出现,这也考验开发写代码对细节的把控。


所以我一直重视的是写组件的能力,是需要不断的训练的,也是需要不断的阅读优秀的组件库去掌握的。面试必问 Okhttp 的拦截器,可以你有真正在自己的组件里使用过类似的东西吗?


最举个例子? 假设现在我们要用 AudioRecord 写一个录制功能。最开始那可能直接在 Activity 里开撸了:

private var isRecording = false;
private suspend fun startRecording() {
    audioRecord = AudioRecord(...)
    val data = ByteArray(bufferSize)
    audioRecord.startRecording()
    isRecording = true
    File(applicationContext.filesDir, "recording.pcm").outputStream().use {outputStream ->
        while (isRecording) {
            val bytesRead = audioRecord.read(data, 0, bufferSize)
            if (bytesRead > 0) {
                outputStream.write(data, 0, bytesRead)
            }
        }
    }
    audioRecord.stop()
    audioRecord.release()
}

这样写下来,流程一通,然后就觉得大功告成了。


然后产品逻辑就变更了:要把音频数据实时上传到服务器。


改:把写文件的部分换成网络传输

过一会儿,老板又找来了,在没网络的时候录制就失败了,这不行,要在无网络的时候录制到本地。


改:判断下网络环境,加 ifelse

过一会儿,老板又找来了,我开始有网络,但是进入电梯后录制就失败了,所以一定要保存一份到文件中,如果有网络,那也往网络写一份。


改:从 ifelse 逻辑换成双写逻辑。

...


只针对上面的场景,整体流程是相当稳定的,只是写入的目标地不一样,那为何不抽象一个 AudioSink 的接口来表示写入的存储目标?

interface AudioSink {
    suspend fun write(buf: ByteArray, offset: Int, len: Int)
    fun close()
}

那整个流程就稳定为:

private suspend fun startRecording(sink: AudioSink) {
    audioRecord = AudioRecord(...)
    val data = ByteArray(bufferSize)
    audioRecord.startRecording()
    isRecording = true
    while (isRecording) {
        val bytesRead = audioRecord.read(data, 0, bufferSize)
        if (bytesRead > 0) {
            sink.write(data, 0, bytesRead)
        }
    }
    audioRecord.stop()
    audioRecord.release()
    sink.close()
}

如果要写文件,写个实现:

class NetworkAudioSink(api): AudioSink {
    ...
}

如果要写网络,写个实现:

class NetworkAudioSink(api): AudioSink {
    ...
}

如果要判断本地与网络,就写个代理:

class DelegateAudioSink(
    val origin: AudioSink
): AudioSink {
    ...
}

如果要同时写本地与网络,就是双写或多写:

class MultiAudioSink(
    val list: List<AudioSink>
): AudioSink {
    ...
}

这些类一写,那具体逻辑就是入口传参的变更了。当然实际情况应该考虑各种异常情况。如果你足够有经验,那在一开始就能把这些场景考虑到,并且把其用处和局限性同步给产品,帮助产品一开始做出最优选择。这才是所谓的经验。


如果回头来看上面的例子,发现都是很熟悉的东西,说是设计模式也行。如果将整个抽象做好,如果从音频切换到视频,那是否这一块可以完美复用?那是不是就可以更早的完成需求?——然后就可以更早的被裁掉~


组件化,就是对业务场景、功能场景的抽象。这是最需要大家去投入时间去做、去训练的。

模块化


一些复杂的组件,本身就可以构成一个个的模块,然后单独出来维护。但在谈论模块的时候,更多的是谈论业务模块。就是把不同业务放在不同的模块下。这对于巨型 App, 其意义当然是有的,毕竟可能是一个团队维护某一个模块。而对于小 App, 那意义就不是特别大了。


因为模块化会带来非常棘手的问题:模块之间的通信问题。ArouterTheRouter 都很大程度上是为了解决模块间的通信问题。而简单的模块化一般是考虑基础模块的抽取,但基础模块的定义总是模糊的,不经意间就抽取出一个巨无霸的基础模块。就跟很多喜欢用 Base 类的同学,最后就一个巨大的 Base 类。


所以一般小的 App, 搞模块化是大可不必的。巨型 App 是存在编译速度、多团队开发的问题,所以各大公司都会出品和分享自己的多模块化实践。小公司就容易跟风把工程搞得巨复杂,这就没意思了。


例如大公司拆分多模块,然后用 aar 包来加速构建速度,相对应的有私有 Maven 仓库,自动打包脚本,搞得很是复杂,对他们而言是很有必要。而对于小公司,其投入产出比,还不如花点钱,买台好一点的电脑来得香。

动态化、插件化


对于动态化和插件化,我只能感叹下,写出这些框架的大佬们,技术是真的强,都是真正的黑客。大概也只有国内的巨型 App 和奇怪的市场审核逻辑才能造就出这些需求。


有时间的话,看看这些库的源码也是很不错的,很涨见识。重要的还是看各大框架遇到的问题场景,以及是如何探索解决方案,最终是如何解决的。


不过问题始终是维护问题,随着 Android 版本的迭代,总归是需要有新的改动或者需要寻求新的方案,但国内的框架和库的成功往往和推动这个项目的人有关,没有一个稳定维护的机制,很容易受到人员变动的影响。

最后


再复杂的东西,也是由一个个的零件慢慢组装起来的。我们要有蓝图,然后为实现这个蓝图而拧螺丝。拧得多了,也许就有机会拧更大的螺丝。我在 emo 组件库上拧了 20 个螺丝,拧得多了,也算是一个不小的库了。不过目前我源码已经转 private 了,后面拧的螺丝,只想留给小团体孤芳自赏了。用行动证明一下,国内的开发者的开源库是多么的不稳定。

目录
相关文章
|
8天前
|
消息中间件 网络协议 Java
Android 开发中实现数据传递:广播和Handler
Android 开发中实现数据传递:广播和Handler
13 1
|
9天前
|
Linux 编译器 Android开发
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
在Linux环境下,本文指导如何交叉编译x265的so库以适应Android。首先,需安装cmake和下载android-ndk-r21e。接着,下载x265源码,修改crosscompile.cmake的编译器设置。配置x265源码,使用指定的NDK路径,并在配置界面修改相关选项。随后,修改编译规则,编译并安装x265,调整pc描述文件并更新PKG_CONFIG_PATH。最后,修改FFmpeg配置脚本启用x265支持,编译安装FFmpeg,将生成的so文件导入Android工程,调整gradle配置以确保顺利运行。
32 1
FFmpeg开发笔记(九)Linux交叉编译Android的x265库
|
11天前
|
Unix Linux Shell
FFmpeg开发笔记(八)Linux交叉编译Android的FFmpeg库
在Linux环境下交叉编译Android所需的FFmpeg so库,首先下载`android-ndk-r21e`,然后解压。接着,上传FFmpeg及相关库(如x264、freetype、lame)源码,修改相关sh文件,将`SYSTEM=windows-x86_64`改为`SYSTEM=linux-x86_64`并删除回车符。对x264的configure文件进行修改,然后编译x264。同样编译其他第三方库。设置环境变量`PKG_CONFIG_PATH`,最后在FFmpeg源码目录执行配置、编译和安装命令,生成的so文件复制到App工程指定目录。
43 9
FFmpeg开发笔记(八)Linux交叉编译Android的FFmpeg库
|
1天前
|
机器学习/深度学习 安全 数据处理
构建未来:基于Android的智能家居控制系统开发
【4月更文挑战第29天】 随着物联网技术的蓬勃发展,智能家居已成为现代技术革新的重要领域。本文将深入探讨基于Android平台的智能家居控制系统的设计和实现,旨在提供一种用户友好、高度集成且功能丰富的解决方案。通过利用Android设备的广泛普及和其强大的处理能力,结合最新的无线通讯技术和人工智能算法,我们旨在打造一个可靠、易用且具有高度可定制性的智能家居控制环境。文中不仅详细阐述了系统架构、关键技术选型以及界面设计,还对可能遇到的安全挑战进行了分析,并提出了相应的解决策略。
|
2天前
|
监控 Java Android开发
安卓应用开发:打造高效用户界面的五大策略
【4月更文挑战第29天】 在安卓应用开发的世界中,构建一个既美观又高效的用户界面(UI)对于吸引和保留用户至关重要。本文将深入探讨五种策略,这些策略可以帮助开发者优化安卓应用的UI性能。我们将从布局优化讲起,逐步过渡到绘制优化、内存管理、异步处理以及最终的用户交互细节调整。通过这些实践技巧,你将能够为用户提供流畅而直观的体验,确保你的应用在竞争激烈的市场中脱颖而出。
|
3天前
|
机器学习/深度学习 搜索推荐 Android开发
【专栏】安卓应用开发:构建高效用户界面的实用指南
【4月更文挑战第27天】本文介绍了构建高效安卓用户界面的指南,分为设计原则和技巧两部分。设计原则包括一致性、简洁性和可访问性,强调遵循安卓系统规范,保持界面简洁,考虑不同用户需求。技巧方面,建议合理布局、优化图标和图片、运用动画效果、提供个性化设置及优化性能。随着技术发展,未来安卓应用开发将融合更多智能化和个性化元素,开发者需持续学习新技术,提升用户体验。
|
6天前
|
数据库 Android开发 开发者
安卓应用开发:构建高效用户界面的策略
【4月更文挑战第24天】 在竞争激烈的移动应用市场中,一个流畅且响应迅速的用户界面(UI)是吸引和保留用户的关键。针对安卓平台,开发者面临着多样化的设备和系统版本,这增加了构建高效UI的复杂性。本文将深入分析安卓平台上构建高效用户界面的最佳实践,包括布局优化、资源管理和绘制性能的考量,旨在为开发者提供实用的技术指南,帮助他们创建更流畅的用户体验。
|
21天前
|
设计模式 API Android开发
安卓应用开发:打造高效的通知管理系统
【4月更文挑战第10天】 在移动应用的海洋中,通知管理是用户体验的冰山一角,却能决定应用的存亡。本文将详细探讨如何在安卓平台上构建一个既高效又灵活的通知管理系统,涵盖从基础理论到实际代码实现的全过程。我们将分析通知系统的工作原理,探索最新的安卓通知API特性,并提供一套实用的设计模式和最佳实践,以帮助开发者优化用户的通知体验。
|
23天前
|
XML 开发工具 Android开发
构建高效的安卓应用:使用Jetpack Compose优化UI开发
【4月更文挑战第7天】 随着Android开发不断进化,开发者面临着提高应用性能与简化UI构建流程的双重挑战。本文将探讨如何使用Jetpack Compose这一现代UI工具包来优化安卓应用的开发流程,并提升用户界面的流畅性与一致性。通过介绍Jetpack Compose的核心概念、与传统方法的区别以及实际集成步骤,我们旨在提供一种高效且可靠的解决方案,以帮助开发者构建响应迅速且用户体验优良的安卓应用。
|
26天前
|
编解码 监控 Android开发
安卓应用开发:构建高效用户界面的策略
【4月更文挑战第5天】在移动应用开发的世界中,一个流畅且直观的用户界面(UI)是吸引和保持用户的关键因素。本文将深入探讨如何在安卓平台上构建高效的用户界面,涵盖从布局优化、资源管理到性能监控的各个方面。我们的目标是为开发者提供实用的技术和策略,帮助他们创建快速、响应灵敏且美观的应用,从而在竞争激烈的市场中脱颖而出。
13 1

相关实验场景

更多