2024年安卓最新如何规范的进行 Android 组件化开发?,2024年最新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行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

示例

这里介绍如何在外部 moduleuser-impl 跳转至用户组件中的关于界面。

准备工作

user-impl 中创建路由类,编写关于界面的路由和服务路由及跳转至关于界面方法:

object UserRoute {

// 关于界面

const val ABOUT = “/user/about”

// 用户组件服务

const val USER_SERVICE = “/user/service”

/**

  • 跳转至关于界面

*/

fun toAbout(): RouteNavigation {

return RouteNavigation(ABOUT)

}

}

在关于界面使用路由:

@Route(path = UserRoute.ABOUT)

class AboutActivity : MyBaseActivity() {

}

user-api 中定义跳转界面方法:

interface IUserService : IServiceProvider {

/**

  • 跳转至关于界面

*/

fun toAbout(): RouteNavigation

}

user-impl 中实现跳转界面方法:

@Route(path = UserRoute.USER_SERVICE)

class UserServiceImpl : IUserService {

override fun toAbout(): RouteNavigation {

return UserRoute.toAbout()

}

}

界面跳转

user-impl 中可以直接跳转到关于界面:

UserRoute.toAbout().navigation(this)

假设 module-a 需要跳转到关于界面,那么先在 module-a 中配置依赖:

dependencies {

implementation project(‘:user-api’)

}

module-a 中使用 provider 跳转到关于界面:

RouteUtil.getServiceProvider(IUserService::class.java)

?.toAbout()

?.navigation(this)

module依赖关系

此时各个 module 的依赖关系如下:

common:基础库、第三方库

user-api:common

user-impl:common、user-api

module-a:common、user-api

App壳:common、user-api、user-impl、module-a、…

tool

该路径下放 module 内部使用的工具方法,一般一个类就够了,比如:UserTool

object UserTool {

/**

  • 该用户是否是会员

  • @param gradeId 会员等级id

*/

fun isMembership(gradeId: Int): Boolean {

return gradeId > 0

}

}

cache

该路径下放 module 使用的缓存方法,一般一个类就够了,比如:UserCache

object UserCache {

// 搜索历史记录列表

var searchHistoryList: ArrayList

get() {

val cacheStr = CacheTool.userCache.getString(SEARCH_HISTORY_LIST)

return if (cacheStr == null) {

ArrayList()

} else {

JsonUtil.parseArray(cacheStr, String::class.java) ?: ArrayList()

}

}

set(value) {

CacheTool.userCache.put(SEARCH_HISTORY_LIST, JsonUtil.toJson(value))

}

// 搜索历史记录列表

private const val SEARCH_HISTORY_LIST = “user_search_history_list”

}

注意:

  1. 缓存Key的命名必须用组件名作为前缀,防止缓存Key重复。
  1. CacheTool.userCache 并不是指用户组件的缓存,而是用户的缓存,即当前登录账号的缓存,每个账号会单独存一份数据,相互之间没有干扰。与之对应的是 CacheTool.globalCache,全局缓存,所有的账号会共用一份数据。

两种module的区别


module-api 中放的都是外部组件需要的,或者说外部组件和 module-impl 都需要的,其他的都应当放在 module-impl 中,对于外部组件需要的但是能通过 provider 方式提供的,都应当把具体的实现放在 module-impl 中,module-api 中只是放一个接口方法。

下表列举项目开发中哪些东西能否放 module-api 中:

| 类型 | 能否放 module-api | 备注 |

| — | — | — |

| 功能界面(Activity、Fragment、Dialog) | 不能 | 通过 provider 方式提供使用 |

| 基类界面 | 部分能 | 外部 module 需要使用的可以,其他的放 module-impl 中 |

| adapter | 部分能 | 外部 module 需要使用的可以,其他的放 module-impl 中 |

| provider | 部分能 | 只能放接口类,实现类放 module-impl 中 |

| tool | 部分能 | 外部 module 需要使用的可以,其他的放 module-impl 中 |

| api、route、cache | 不能 | 通过 provider 方式提供使用 |

| entity | 部分能 | 外部 module 需要使用的可以,其他的放 module-impl 中 |

| event | 部分能 | 对使用 EventBus 及类似框架的项目,外部组件需要的可以,其他还是放 module-impl 中 |

| 对于使用了 LiveEventBus 的项目不能,通过 provider 方式提供使用 | | |

| 资源文件和资源变量 | 部分能 | 需要在 xml 文件中使用的可以, 其他的通过 provider 方式提供使用 |

注意:如果仅在 module-impl 中存在工具类,则该工具类命名为 xxTool。如果 module-apimodule-impl 都存在工具类,则 module-api 中的命名为 xxToolmodule-impl 中的命名为 xxTool2

组件单独调试


在开发过程中,为了查看运行效果,需要运行整个App,比较麻烦,而且可能依赖的其他组件也在开发中,App可能运行不到当前开发的组件。为此可以采用组件单独调试的模式进行开发,减少其他组件的干扰,等开发完成后再切换回 library 的模式。

在组件单独调试模式下,可以增加一些额外的代码来方便开发和调试,比如新增一个入口 Actvity,作为组件单独运行时的第一个界面。

示例

这里介绍在 user-impl 中进行组件单独调试。

在项目根目录下的 gradle.properties 文件中新增变量 isDebugModule,通过该变量控制是否进行组件单独调试:

组件单独调试开关,为ture时进行组件单独调试

isDebugModule = false

user-implbuild.gradle 的顶部增加以下代码来控制 user-implApplicatonLibrary 之间进行切换:

if (isDebugModule.toBoolean()) {

apply plugin: ‘com.android.application’

} else {

apply plugin: ‘com.android.library’

}

user-implsrc/main 的目录下创建两个文件夹 releasedebugrelease 中放 library 模式下的 AndroidManifest.xmldebugapplication 模式下的 AndroidManifest.xml、代码和资源,如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LRO54CaZ-1625141825138)(//upload-images.jianshu.io/upload_images/19542624-42350721a4d958ed.png?imageMogr2/auto-orient/strip|imageView2/2/w/376/format/webp)]

user-implbuild.gradle 中配置上面的创建的代码和资源路径:

android {

sourceSets {

if (isDebugModule.toBoolean()) {

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

main.java.srcDirs += ‘src/main/debug’

main.res.srcDirs += ‘src/main/debug’

} else {

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

}

}

}

注意:完成上述配置后,在 library 模式下,debug 中的代码和资源不会合并到项目中。

最后在 user-implbuild.gradle 中配置 applicationId

android {

defaultConfig {

if (isDebugModule.toBoolean()) {

applicationId “cc.tarylorzhang.demo”

}

}

}

注意:如果碰到65536的问题,在 user-implbuild.gradle 中新增以下配置:

android {

defaultConfig {

if (isDebugModule.toBoolean()) {

multiDexEnabled true

}

}

}

以上工作都完成后,将 isDebugModule 的值改为 true,则可以开始单独调试用户组件。

命名规范


module名

组件名如果是单个单词的,直接使用该单词 + apiimpl 的后缀作为 module 名,如果是多个单词的,多个单词小写使用 - 字符作为连接符,然后在其基础上加 apiimpl 的后缀作为 module 名。

示例

用户组件(User),它的 module 名为 user-apiuser-impl;会员卡组件(MembershipCard),它的 module 名为 membership-card-apimembership-card-impl

包名

在应用的 applicationId 的基础上增加组件名后缀作为组件基础包名。

在代码中的包名 module-apimodule-impl 都直接使用基础包名即可,但是在 Android 中项目 AndroidManifest.xml 文件中的 package 不能重复,否则编译不通过。所以 module-impl 中的 package 使用基础包名,而 module-impl 中的 package 使用基础包名 + api 后缀。

package 重复的时候,会报 Type package.BuildConfig is defined multiple times 的错误。

示例

应用的 applicationIdcc.taylorzhang.demo,对于用户组件(user),组件基础包名为 cc.taylorzhang.demo.user,则实际包名如下表:

| | 代码中的包名 | AndroidManifest.xml中的包名 |

| — | — | — |

| user-api | cc.taylorzhang.demo.user | cc.taylorzhang.demo.userapi |

| user-impl | cc.taylorzhang.demo.user | cc.taylorzhang.demo.user |

对于多单词的会员卡组件(MembershipCard),其组件基础包名为 cc.taylorzhang.demo.membershipcard

资源文件和资源变量

所有的资源文件:布局文件、图片等全部要增加组件名作为前缀,所有的资源变量:字符串、颜色等也全部要增加组件名作为前缀,防止资源名重复。

示例
  • 用户组件(User),关于界面布局文件命名为:user_activity_about.xml

  • 用户组件(User),关于界面标题字符串命名为:user_about_title

  • 会员卡组件(MembershipCard),会员卡详情界面布局文件,文件名为:membership_card_activity_detail

  • 会员卡组件(MembershipCard),会员卡详情界面标题字符串,文件名为:membership_card_detail_title

类名

对于类名没必要增加前缀,比如 UserAboutActivity,因为对资源文件和资源变量增加前缀主要是为了避免重复定义资源导致资源被覆盖的问题,而上面的包名命名规范已经避免了类重复的问题,直接命名 AboutActivity 即可。

全局管理App环境


App 环境一般分为开发、测试和生产环境,不同环境下使用的网络请求地址大概率是不一样的,甚至一些UI都不一样,在打包的时候手动修改很容易有遗漏,产生不必要的 BUG。应当使用 buildConfigField 在打包的时候将当前环境写入 App 中,在代码中根据读取环境变量,根据不同的环境执行不同的操作。

示例

准备工作

App 壳 的 build.gradle 中给每个buildType 都配置 APP_ENV

android {

buildTypes {

debug {

buildConfigField “String”, “APP_ENV”, ‘“dev”’

}

release {

buildConfigField “String”, “APP_ENV”, ‘“release”’

}

ctest {

initWith release

buildConfigField “String”, “APP_ENV”, ‘“test”’

matchingFallbacks = [‘release’]

}

}

}

注意:测试环境的 buildType 不能使用 test 作为名字,Android Studio 会报 ERROR: BuildType names cannot start with 'test',这里在 test 前增加了一个 c

commontool 路径下创建一个App环境工具类:

object AppEnvTool {

/** 开发环境 */

const val APP_ENV_DEV = “dev”

/** 测试环境 */

const val APP_ENV_TEST = “test”

/** 生产环境 */

const val APP_ENV_RELEASE = “release”

/** 当前App环境,默认为开发环境 */

private var curAppEnv = APP_ENV_DEV

fun init(env: String) {

curAppEnv = env

}

/** 当前是否处于开发环境 */

val isDev: Boolean

get() = curAppEnv == APP_ENV_DEV

/** 当前是否处于测试环境 */

val isTest: Boolean

get() = curAppEnv == APP_ENV_TEST

/** 当前是否处于生产环境 */

val isRelease: Boolean

get() = curAppEnv == APP_ENV_RELEASE

}

Application 中初始化App环境工具类:

class DemoApplication : Application() {

override fun onCreate() {

super.onCreate()

// 初始化App环境工具类

AppEnvTool.init(BuildConfig.APP_ENV)

}

}

使用App环境工具类

这里介绍根据App环境使用不同的网络请求地址:

object CommonApi {

// api开发环境地址

private const val API_DEV_URL = “https://demodev.taylorzhang.cc/api/”

// api测试环境地址

private const val API_TEST_URL = “https://demotest.taylorzhang.cc/api/”

// api生产环境地址

private const val API_RELEASE_URL = “https://demo.taylorzhang.cc/api/”

// api地址

val API_URL = getUrlByEnv(API_DEV_URL, API_TEST_URL, API_RELEASE_URL)

// H5开发环境地址

private const val H5_DEV_URL = “https://demodev.taylorzhang.cc/m/”

// H5测试环境地址

private const val H5_TEST_URL = “https://demotest.taylorzhang.cc/m/”

// H5生产环境地址

private const val H5_RELEASE_URL = “https://demo.taylorzhang.cc/m/”

// H5地址

val H5_URL = getUrlByEnv(H5_DEV_URL, H5_TEST_URL, H5_RELEASE_URL)

private fun getUrlByEnv(devUrl: String, testUrl: String, releaseUrl: String): String {

return when {

AppEnvTool.isDev -> devUrl

AppEnvTool.isTest -> testUrl

else -> releaseUrl

}

}

}

打包

通过不同的命令打包,打出对应的App环境包:

打开发环境包

./gradlew clean assembleDebug

打测试环境包

./gradlew clean assembleCtest

最后

在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

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

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

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

_URL)

private fun getUrlByEnv(devUrl: String, testUrl: String, releaseUrl: String): String {

return when {

AppEnvTool.isDev -> devUrl

AppEnvTool.isTest -> testUrl

else -> releaseUrl

}

}

}

打包

通过不同的命令打包,打出对应的App环境包:

打开发环境包

./gradlew clean assembleDebug

打测试环境包

./gradlew clean assembleCtest

最后

在这里我和身边一些朋友特意整理了一份快速进阶为Android高级工程师的系统且全面的学习资料。涵盖了Android初级——Android高级架构师进阶必备的一些学习技能。

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题(含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-IyFtan7Q-1715105233742)]

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值