2024年Android最新大规模项目完全 gradle 化的一次落地总结,2024年最新大厂面试参考指南1.0

最后

我坚信,坚持学习,每天进步一点,滴水穿石,我们离成功都很近!
以下是总结出来的字节经典面试题目,包含:计算机网络,Kotlin,数据结构与算法,Framework源码,微信小程序,NDK音视频开发,计算机网络等。

字节高级Android经典面试题和答案


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

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

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

ant 硬伤带来的问题

原来采用 eclipse 的目录结构,现在通过 sourceSets 来重新指向目录,所以产生了新build_common.gradle文件,如下:

apply plugin: ‘com.android.library’

android {

useLibrary ‘org.apache.http.legacy’

compileSdkVersion rootProject.ext.compileSdkVersion

buildToolsVersion rootProject.ext.buildToolsVersion

defaultConfig {

minSdkVersion 14

targetSdkVersion 27

}

sourceSets {

main {

manifest.srcFile ‘AndroidManifest.xml’

java.srcDirs = [‘src’]

resources.srcDirs = [‘src’]

aidl.srcDirs = [‘src’]

renderscript.srcDirs = [‘src’]

res.srcDirs = [‘res’]

assets.srcDirs = [‘assets’]

}

instrumentTest.setRoot(‘tests’)

debug.setRoot(‘build-types/debug’)

release.setRoot(‘build-types/release’)

}

compileOptions {

sourceCompatibility rootProject.ext.JAVA_SOURCE_VERSION

targetCompatibility rootProject.ext.JAVA_TARGET_VERSION

}

}

通过上面操作解决了大部分通用模块,其余特殊的模块通过 apply 这个共用 gradle 文件并重写相关目录实现。

R 文件合并时的问题

老项目大都是 eclipse 迁移过来,包同名的 R 文件比较多,以前 ant 构建没有 aar 的概念,构建包含android.jar的 jar 包都是主动 classpath 追加的,现在迁移到 gradle 后有十几个原来的 jar module 需要迁移成 aar 的模块,这样就能回归到标准构建的 android 代码模块。所以需要新加AndroidManifest.xml清单文件,而清单文件中的package="cn.company.app.base"值必须要特别注意,因为 android 构建多模块在最后合并打包时会先进行资源合并,所以如果 package 的值和别的模块的清单文件值相同就会导致 gradle 构建失败,提示合并清单文件重复冲突。

出现上面问题的原因就是 aar 模块项目构建中间产物gradleBuild/intermediates/classes/debug/下的R.javaBuildConfig.java文件的包名就是其AndroidManifest.xml清单文件中的 package 值。

aar 包丢失 aidl 文件问题

原来通过 ant 构建时指定源码目录下的 aidl 文件也会被自动打入 jar 包中,而换成 gradle 的 aar 后默认 gradle 打包 aar 不会包含 aidl 文件,所以当这些 aar 应用给别的 module 时就会构建提示 aidl 文件缺失,因此需要让 aidl 文件打包进 aar 包,做法如下:

android {

aidlPackageWhiteList ‘cn/app/xx/Behavior.aidl’, \

‘cn/app/xx/aa/ApplyTo.aidl’

sourceSets {

//…

}

}

so 库构建问题

这个问题比较坑,做过大型项目的小伙伴应该都能理解。项目超级大,里面一些 so 是使用第三方现成的,一些 so 是我们自己构建生成的,而我们自己构建生成的 so 都比较复杂,尤其是构建参数,并且不同的 so 是不同的业务团队负责的。在整体迁移 ant 到 gradle 构建时强制其他团队进行配合整改是基本不现实的,因为这些 so 进行迁移构建对测试团队的压力很大,所以评估后我选择了 so 构建继续保持原来的 ndk-build 方式,不过这里也有坑。

一开始我想将我们 ant 里的 ndk-build 构建直接迁移到 gradle 的externalNativeBuild.ndkBuild形式,但是比较坑的是我们 gradle 是定制版的(dex分包),而我们这个版本的externalNativeBuild.ndkBuild恰巧只支持 path 配置,不支持参数配置,所以挺坑,升级 gradle 插件代价太大,所以这种方式就走不通了,只能将来 gradle 升级后使用。

接着想着要不将现有的 mk 构建迁移到 cmake,评估发现对测试影响太大,最好能先保持现状,所以我就走了一条歪路,也有坑,那就是自己写个 gradle 脚本通过 ndk-build 传递参数构建,结果上网搜了一下,随便复制一个 doLast 那种挂载在构建 task 上,以为万事大吉了,结果在构建机上的包老是出现一会儿 so 有编译进去,一会儿没有,然后排查发现项目根目录的gradle.properties文件中有如下配置:

org.gradle.daemon=true

org.gradle.parallel=false //开启并行构建

org.gradle.configureondemand=true

org.gradle.caching=true

由此怀疑是并行构建的问题,所以看了下网上思路的实现,一想确实有锅,不能只 doLast,还得挂在在一些 task(ndk合并)前面,只有这样才能保证并行构建每次取到正常结束的 so,所以我为了更加完美,就写了如下ndk_cmd_build.gradle脚本:

/**

  • 由于 gradle plugin 版本不够新,external native build 暂时用不了 Application.mk,

  • 所以实现一个通用的 ndk-build cmd 构建。

  • 支持并行构建、flavour、buildtype。

  • task 名为 compile${targetName}NdkBuildCmd,其中 t a r g e t N a m e = " {targetName} = " targetName="{productFlavorName}${buildTypeName}"。

*/

def runBefore(String dependentTaskName, Task task) {

Task dependentTask = tasks.findByPath(dependentTaskName)

if (dependentTask != null) {

dependentTask.dependsOn task

}

}

def mustRunAfter(String dependentTaskName, Task task) {

Task dependentTask = tasks.findByPath(dependentTaskName)

if (dependentTask != null) {

dependentTask.mustRunAfter(task)

}

}

project.afterEvaluate {

def ndkBuildCmdArgs = project.hasProperty(“ndkBuildCmdArgs”) ? project.ndkBuildCmdArgs : []

def buildTypes = android.buildTypes.collect { type -> type.name }

def productFlavors = android.productFlavors.collect { flavor -> flavor.name }

if (!productFlavors) {

productFlavors.add(‘’)

}

productFlavors.each { productFlavorName ->

buildTypes.each { buildTypeName ->

def flavorNameCapitalized = “${productFlavorName.capitalize()}”

def buildNameCapitalized = “${buildTypeName.capitalize()}”

def targetName = “ f l a v o r N a m e C a p i t a l i z e d {flavorNameCapitalized} flavorNameCapitalized{buildNameCapitalized}”

def ndkCmdBuildTaskName = “compile${targetName}NdkBuildCmd”

def ndkCmdBuildTask = tasks.create(name: ndkCmdBuildTaskName, type: Exec) {

group = “other”

description = “ndk build with cmd style for ${targetName}.”

doFirst {

println(“${project.name} start ndk build use cmd…”)

def ndkDir = System.getenv(“ANDROID_NDK_HOME”)

if (ndkDir == null) {

Properties properties = new Properties()

properties.load(new FileReader(“$rootDir/local.properties”))

ndkDir = properties.getProperty(“ndk.dir”)

}

println(“#command is# ${ndkDir}/ndk-build ${ndkBuildCmdArgs}”)

if (ndkDir != null) {

if (org.gradle.internal.os.OperatingSystem.current().isWindows()) {

commandLine(“${ndkDir}\ndk-build.cmd”, *ndkBuildCmdArgs)

} else {

commandLine(“${ndkDir}/ndk-build”, *ndkBuildCmdArgs)

}

} else {

commandLine “echo”, “ndk path is null, please check you environment(example: export ANDROID_NDK_HOME=/opt/android-ndk-r10d, the key must named as ANDROID_NDK_HOME)!”

}

}

if (ndkBuildCmdArgs.size() == 0) {

enabled = false

}

}

//runBefore(“pre${targetName}Build”, ndkCmdBuildTask)

runBefore(“compile${targetName}Ndk”, ndkCmdBuildTask)

//按照源码里几个 task 都处理下!

mustRunAfter(“compile${targetName}Renderscript”, ndkCmdBuildTask)

mustRunAfter(“merge${targetName}JniLibFolders”, ndkCmdBuildTask)

mustRunAfter(“transformNativeLibsWithIntermediateJniLibsFor${targetName}”, ndkCmdBuildTask)

mustRunAfter(“transformNativeLibsWithMergeJniLibsFor${targetName}AndroidTest”, ndkCmdBuildTask)

mustRunAfter(“transformNativeLibsWithMergeJniLibsFor${targetName}”, ndkCmdBuildTask)

mustRunAfter(“transformNativeLibsWithStripDebugSymbolFor${targetName}”, ndkCmdBuildTask)

mustRunAfter(“transformNativeLibsWithSyncJniLibsFor${targetName}”, ndkCmdBuildTask)

}

}

def ndkCmdBuildCleanTask = tasks.create(name: “ndkCmdBuildClean”, type: Delete) {

group = “build”

description = “clean the build of ndk build with cmd.”

def ndkBuildCmdClean = project.hasProperty(“ndkBuildCmdClean”) ? project.ndkBuildCmdClean : []

if (ndkBuildCmdClean.size() == 0) {

enabled = false

}

doFirst {

delete ndkBuildCmdClean

}

}

runBefore(“clean”, ndkCmdBuildCleanTask)

}

如上实现了一个全新的构建 task,叫做compile${targetName}NdkBuildCmd,so 的构建都通过他来处理,需要构建项目如下配置即可:

apply from: ‘…/…/build_common.gradle’

apply from: ‘…/…/ndk_cmd_build.gradle’

ext {

ndkBuildCmdArgs = [‘-C’, file(‘jni’).absolutePath, ‘-j’, Runtime.runtime.availableProcessors(), ‘NDK_DEBUG=0’]

总结

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的14套腾讯、字节跳动、阿里、百度等2021最新面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。

2020面试真题解析
腾讯面试真题解析

阿里巴巴面试真题解析

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

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

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

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

]

[外链图片转存中…(img-Fu8IW98U-1715711174482)]

[外链图片转存中…(img-ScDSt95w-1715711174482)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值