最后
我坚信,坚持学习,每天进步一点,滴水穿石,我们离成功都很近!
以下是总结出来的字节经典面试题目,包含:计算机网络,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.java
和BuildConfig.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(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
]
[外链图片转存中…(img-Fu8IW98U-1715711174482)]
[外链图片转存中…(img-ScDSt95w-1715711174482)]
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!