ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Android开发基础技能练习场:从AGP到R8,打造可验证的基本功

Android开发基础技能练习场:从AGP到R8,打造可验证的基本功 1. 先把练习场的地图铺开我为什么要搭这么一套东西先说个现象。这几年我面试过的Android开发十个里有七八个能把业务代码写得挺顺一问到基础技能就含糊AGP和Android Studio的版本关系说不清、R8混淆规则全靠网上抄、setColor和setBackgroundColor到底有什么区别也得想半天。这不是个例是整个行业被高节奏业务推着走之后留下的通病——大家习惯了搜到答案就跑通很少停下来问一句为什么是这样。所以我给自己搭了一个名为RYG的Android开发基础技能练习场。名字的含义很简单Recruit Your Growth把零散的Android基础技能拆成一个个可以反复练、可以验证的关卡每一关对应一类真实开发中高频出现的问题。这个练习场不是再写一遍教程而是把知识点转成能跑起来的小项目和能自查的清单练完一个关卡你不仅知道答案还能跟别人讲清楚来龙去脉。我梳理练习场的技能地图时参考了一个很有用的信号各大平台的热搜词。热搜词里反复出现的主题就是真实开发者踩坑最密集的地方。我曾经拉过一段时间的Android相关搜索词发现高热度主要集中在几个方向技能层对应练习关卡典型热搜/痛点工具链与构建Android Studio调教、AGP版本匹配、Gradle报错排查android studio汉化、hedgehog支持agp8吗、could not load compiled classes代码安全与发布R8混淆规则、多形态产品构建android r8、谷歌android马甲包代码混淆UI基础颜色与透明度、协调布局、Banner联动、动态图标setcolor、透明度对照表、协调布局banner、动态图标主题Framework机制AMS、电话状态监听、OTA升级、启动优化android ams、phonestatelistener、android ota、android studio火焰图架构与组件MVVM、蓝牙、播放器SDK、剪贴板与文件访问mvvm代码示例、蓝牙、smartplayer集成、file:///storage/emulated/0进阶方向车载系统、多端适配、嵌入式调试android车载、harmonyos next、openocd、i2c-tools当一个话题被反复搜它大概率不是冷门偏题而是无数人卡过的真实瓶颈。把这些瓶颈编进练习场练的就是最有性价比的基本功。这套练习场适合三类人刚入行想建立完整知识体系的初级开发、准备跳槽想系统梳理基础的进阶开发、以及带团队想快速摸底组员能力的Leader。使用方式也很简单——每个关卡都包含四步跑通Demo、改参数观察现象、断掉某个依赖看会不会崩、最后用自己的话把原理讲出来。前两步是会用后两步是真懂。2. 练手第一步不是写代码而是把Android Studio这套家伙什儿调到顺手2.1 下载、汉化和插件仓库这些事看着小坑起来也耽误时间Android Studio的下载渠道其实只有一个官方源头就是Android开发者官网。网上搜android studio下载会出来一堆第三方站点有些还捆绑了奇怪的安装包我建议一律不碰。官网会按操作系统区分安装包Windows用户选.exe版本macOS用户注意区分Intel芯片和Apple Silicon芯片下错了装不上是小事装上了跑起来各种莫名其妙卡顿才麻烦。下载完成后第一件事不是新建项目而是先把界面语言调好。很多初学者英文界面看着吃力搜索android studio中文或者android studio汉化。新版Android Studio直接用插件方式支持中文打开Settings进入Plugins搜索Chinese (Simplified) Language Pack安装后重启就是中文界面。这个中文语言包是JetBrains官方出的质量有保障不要从乱七八糟的渠道下载汉化包之前有人从非官方渠道下载汉化插件结果IDE卡死还报一堆错。插件仓库这块新版Android Studio默认从JetBrains官方插件市场拉取插件也支持从本地磁盘安装自定义插件。你在Settings的Plugins面板里看到的Marketplace就是官方仓库。国内网络环境下插件市场偶尔加载不出来我一般会先在浏览器访问插件市场网页版确认插件兼容的IDE版本号再回到IDE里搜索安装。如果Marketplace一直转圈可以尝试修改IDE的DNS设置或者稍后再试但不要乱配置网络代理尤其是不要用那些来历不明的加速工具很多安全问题就是这么来的。2.2 AGP版本与Android Studio版本的对应关系别在这上面凭感觉热搜词里有一条很典型android studio hedgehog | 2023.1.1 patch 2支持agp8版本吗。答案是支持。Android Studio Hedgehog2023.1.1官方默认配套的AGP版本就是8.2.x所以Patch 2不仅支持AGP 8而且是专门为AGP 8系列调校的。这里的关键是理解Android Studio版本和AGP版本不是一回事。Android Studio是IDEAGP是Gradle插件它们各自有版本号但有官方约定的兼容边界。选错了组合轻则构建警告重则直接无法编译。我整理过一张常用对应表Android Studio版本默认AGP版本要求的JDK版本Giraffe2022.3.18.1.xJDK 17Hedgehog2023.1.18.2.xJDK 17Iguana2023.2.18.3.xJDK 17Jellyfish2023.3.18.4.xJDK 17Koala2024.1.18.5.xJDK 17AGP 8.x要求JDK 17这是很多项目升级时崩溃的第一个原因——项目里还在用JDK 11甚至JDK 8Gradle同步直接报错。练习场里我专门加了一个关卡新建一个项目手动把AGP版本降一个主版本号观察Gradle同步报什么错再升回来。这样折腾一次你对版本约束的理解比看十篇文章都深刻。2.3 一个高频Gradle报错could not load compiled classes for settings file热搜词里有一长串报错could not load compiled classes for settings file d:\android\coffee\setting...。这报错我前后遇到过五六次每次原因还不完全一样但排查链路是通用的。首次遇到时我一度以为是项目代码写错了后来发现根因往往是Gradle缓存损坏与项目路径问题叠加。完整的排查路径应该是这样的先看完整错误日志注意是settings file而非build.gradle说明问题出在Gradle初始化阶段跟业务代码无关。去项目根目录删除.gradle文件夹再去用户目录下的.gradle/caches清理对应版本的缓存重新同步。检查项目路径有没有中文、空格、特殊符号。热搜里那个路径是d:\android\coffee看起来正常但如果你路径里有中文Gradle在某些版本下会编译settings脚本失败。检查JDK版本是否匹配当前AGP要求不匹配就改Project Structure里的SDK location和Gradle JDK设置。如果以上都不行用命令行执行gradlew clean并加--stacktrace参数看完整堆栈定位到具体是哪一行的settings配置出问题。我在练习场里把这条报错做成了必练关卡因为排查Gradle构建问题的思路跟排查业务代码崩溃的思路是一模一样的先缩小范围、再复现、再验证。这套方法论练熟了构建问题就不再是玄学。3. 构建与代码安全的基本功R8不是混淆器那么简单3.1 R8到底在构建时对你的代码做了什么很多开发者的理解是R8就是混淆代码防止别人反编译这个理解太窄了。R8在Android构建链里承担的是压缩、优化、混淆、资源收缩四件事。压缩遍历所有代码入口把从未被引用的类、方法、字段从最终包里删掉。你依赖了一整个SDK但只用到一个工具类R8能帮你把用不到的几百个类丢掉。优化对字节码做等价变换比如移除无效的try-catch、合并重复代码、内联短方法。混淆把类名、方法名、字段名改成a、b、c这样的短名增加逆向阅读难度。资源收缩配合shrinkResources属性把未引用的资源文件从APK里移除。从AGP 8.0开始R8已经取代了原来的ProGuardminifyEnabled true就会自动启用R8。我在练习场里会让同学做一个实验同一个HelloWorld项目记录开启R8前后的APK体积、构建耗时、反编译后的代码效果。这个实验做下来你会发现R8的作用比想象中大很多但它的坑也比想象中多——因为压缩和混淆可能把你正常运行的代码优化崩了。3.2 keep规则为什么是混淆配置里最重要的部分混淆崩了最常见的原因就是反射、JNI、序列化、注解这类动态调用机制R8静态分析无法发现这些调用关系就会把目标类当作无用代码删除或重命名。解决手段就是写keep规则。我在练习场里给了一个通用模板可以直接抄# 保留实体类Gson/Jackson反序列化用 -keep class com.ryg.model.** { *; } # 保留枚举类枚举有values()和valueOf()反射逻辑 -keepclassmembers enum * { public static **[] values(); public static ** valueOf(java.lang.String); } # 保留JNI方法名 -keepclasseswithmembernames class * { native methods; } # 保留注解别把运行时注解干掉了 -keepattributes *Annotation* # 保留WebView JS接口 -keepclassmembers class * { android.webkit.JavascriptInterface methods; } # 保留Parcelable实现类系统要用CREATOR字段反射创建对象 -keep class * implements android.os.Parcelable { public static final android.os.Parcelable$Creator *; }写keep规则的核心原则是不确定会不会用到反射的地方宁可keep也不要冒险。keep多了只会让包大一点keep少了直接崩溃。3.3 多形态产品的混淆与构建差异热搜词里有谷歌android马甲包代码混淆。这里我不讨论那类灰色玩法单说技术本质同一套核心代码要做成多个皮肤包/多形态产品共享大部分逻辑但包名、图标、主题色、部分资源不同。这在正规的商业场景里也很常见比如一个App的海外版和国内版、免费版和Pro版。这种需求的标准姿势是Gradle的productFlavors配合维度做变体构建android { flavorDimensions version productFlavors { create(free) { dimension version applicationId com.ryg.free versionNameSuffix -free } create(pro) { dimension version applicationId com.ryg.pro versionNameSuffix -pro } } }每个flavor可以有自己的目录src/free/res和src/pro/res放各自的图标、颜色、字符串。混淆规则这里有个容易踩的坑不同flavor可能引入不同的第三方SDK各自的keep规则要放到对应flavor的proguard-rules-xxx.pro里而不是全部堆在主规则文件里。不然打包pro版本时把free版SDK的keep规则一起带进去包体积和崩溃风险都会上升。我还习惯在构建脚本里给多形态产品加一个版本身份打印启动时把BuildConfig.FLAVOR和BuildConfig.APPLICATION_ID打到日志里。这样测试人员报bug时我们第一眼就知道对方用的是哪个形态的包排查效率翻倍。4. UI层的高频基础关卡颜色、协调布局、动态图标细节里全是面试题4.1 setColor、透明度对照表这些小东西串起来能考倒一片人热搜词里setcolor和透明度对照表几乎是常青树。这两个词看似简单背后涉及的是Android颜色系统的完整理解。我先说结论Android里View.setTextColor()和View.setBackgroundColor()接收的都不是颜色而是int值这个int的四个字节分别代表Alpha、Red、Green、Blue顺序是ARGB。很多新手写布局时看到android:background#FF0000就以为是红加蓝写代码时又把Color.RED直接塞给背景结果发现颜色不对其实都是没搞懂透明度对应的前缀。透明度这块我给练习场整理过一个速查表透明度十六进制前缀常见业务场景100%不透明FF正常文字、图片90%E6弹窗遮罩80%CC二级遮罩、水印70%B3大图上的半透明引导60%99弱化文字50%80分割线、禁用态40%66更弱的禁用态30%4D阴影层20%33极淡的底色10%1A页面背景装饰0%全透明00占位透明色代码里设置颜色时我强烈建议用带透明度前缀的写法比如Color.parseColor(#CCFF0000)意思是80%透明度的红色。这里有个新手常犯的错Color.parseColor(#FF0000)是纯红Color.parseColor(#80FF0000)才是半透明红。前缀多写两位很多人一开始不习惯。另外要区分setColor和setTint前者是直接设置颜色值后者是给Drawable着色用的。如果你对一个BitmapDrawable调用setColor大概率没效果应该用setColorFilter或者setTint。这个知识点在面试里出现的频率极高我几乎每次都会追问一句setColor和setBackgroundColor有什么本质区别能答清楚的人不多。4.2 协调布局与Banner联动练的是嵌套滚动的核心理解android中协调布局banner是热搜词里我最喜欢的一道题因为它把CoordinatorLayout、AppBarLayout、CollapsingToolbarLayout、ViewPager2、NestedScrollView这些组件全串起来了练一次等于复习了整个Material体系。一个典型场景顶部是CollapsingToolbarLayout里面放一个BannerViewPager2实现的轮播图下滑时Banner随着Toolbar折叠收起页面上滑时Banner展开下面的内容区域是NestedScrollView。我给的练习模板长这样androidx.coordinatorlayout.widget.CoordinatorLayout com.google.android.material.appbar.AppBarLayout com.google.android.material.appbar.CollapsingToolbarLayout app:layout_scrollFlagsscroll|exitUntilCollapsed androidx.viewpager2.widget.ViewPager2 app:layout_collapseModeparallax / androidx.appcompat.widget.Toolbar app:layout_collapseModepin / /com.google.android.material.appbar.CollapsingToolbarLayout /com.google.android.material.appbar.AppBarLayout androidx.core.widget.NestedScrollView app:layout_behaviorstring/appbar_scrolling_view_behavior !-- 内容列表 -- /androidx.core.widget.NestedScrollView /androidx.coordinatorlayout.widget.CoordinatorLayout这里最容易踩的坑有三个。第一AppBarLayout的layout_scrollFlags如果漏了scroll标志整个折叠效果不生效如果漏了exitUntilCollapsedToolbar可能被完全滚出屏幕而不是固定在顶部。第二ViewPager2在CollapsingToolbarLayout里做轮播如果不处理触摸事件冲突左右滑动切图跟上下滑动手势会互相抢需要在onInterceptTouchEvent里判断滑动方向。第三Banner自动轮播的handler如果没在onPause里停止页面切后台后会一直发消息回来时发现Banner跳帧这个问题我见过无数人栽过。练习时我的建议是先跑通纯XML版本然后把ViewPager2换成RecyclerView的Banner实现再手动加一个下拉刷新观察onNestedPreScroll的回调顺序。这套下来嵌套滚动的分发机制你就彻底通透了。4.3 动态图标主题和Settings布局属于加分项里的基础分热搜词里还有android动态图标主题和android settings布局。前者指的是Android 13开始支持的Themed Icons——应用图标可以根据系统主题切换为单色模式。实现方式是在res/drawable里放一个ic_launcher_monochrome.xml用单色矢量图描述图标轮廓并在AndroidManifest.xml里给application配置android:monochromedrawable/ic_launcher_monochrome。vector xmlns:androidhttp://schemas.android.com/apk/res/android android:width108dp android:height108dp android:viewportWidth48 android:viewportHeight48 path android:fillColor#000000 android:pathDataM24,4C12.95,4 4,12.95 4,24s8.95,20 20,20 20,-8.95 20,-20S35.05,4 24,4z / /vector注意这个矢量图里fillColor随便写系统主题会覆盖它但pathData一定要是单色可识别的轮廓不能用渐变色或者多色图层。Settings布局熟悉之后你会在很多项目里用到PreferenceFragmentCompat来实现设置页而不是手写一堆LinearLayout。现在Jetpack的androidx.preference库已经支持PreferenceDataStore可以把设置项直接落到DataStore或Room里。练习场里我要求每个学员写一个音效开关推送开关清除缓存的设置页用Preference框架实现这个关卡练完你对SharedPreferences和DataStore的差异理解也会上一个台阶。5. Framework机制专项AMS、电话监听、OTA、火焰图把它们从黑盒变成灰盒5.1 AMS到底管了哪些事Activity启动流程别只背口诀热搜词android ams几乎每天都有人搜。AMS是ActivityManagerService的缩写它是Android系统里最核心的系统服务之一管着Activity、Service、ContentProvider、BroadcastReceiver这些组件的生命周期还管着进程调度、内存管理、任务栈。练习场里我不让大家去读AOSP源码只要求能按顺序说出一次startActivity的完整调用链app进程调用Activity.startActivity()最终通过Binder调用到系统进程的AMS.startActivity()。AMS检查调用者的权限和Intent匹配的Activity完成合法性校验。AMS通知ActivityThread暂停当前ActivityonPause。如果目标Activity所在进程不存在AMS向Zygote进程发送请求Zygote fork出新的app进程。新进程创建后通过ActivityThread入口初始化Application、启动主线程Looper。AMS通过Binder通知ActivityThread创建目标Activity执行onCreate、onStart、onResume。旧的Activity进入onStop状态整个启动流程完成。这套链路看起来简单但很多崩溃问题的排查思路都藏在里面。比如启动Activity时闪烁黑屏本质是第4步新进程创建耗时Window还是黑的起点Activity没走onPause可能是singleTask启动模式导致旧实例直接onNewIntent了。练这个关卡的验收标准是不看任何资料能画出一条startActivity到onResume的生命周期时间线并标出每个节点发生在新进程还是系统进程。5.2 PhoneStateListener为什么被废弃了新API怎么用android phonestatelistener这个热搜词很能说明问题——很多人还在用旧API。PhoneStateListener在Android 12API 31开始大部分回调方法被标记为废弃原因有两层一是电话状态属于个人敏感信息系统收紧了对电话状态的访问权限二是旧API的架构不好做精细化权限控制。Android 12系统会直接忽略掉未授权应用的大部分电话监听回调连READ_PHONE_STATE权限都拿不到完整的信号强度信息。替代方案是TelephonyCallback配合TelephonyManager.registerTelephonyCallback()使用val telephonyManager getSystemService(Context.TELEPHONY_SERVICE) as TelephonyManager val executor ContextCompat.getMainExecutor(this) if (Build.VERSION.SDK_INT Build.VERSION_CODES.S) { telephonyManager.registerTelephonyCallback(executor, object : TelephonyCallback(), TelephonyCallback.CallStateListener { override fun onCallStateChanged(state: Int) { // 处理通话状态 } } ) } else { // 旧设备走PhoneStateListener }这里面的逻辑是新API不仅改了名字还要求调用方指定Executor强制你明确回调发生的线程。练这个关卡时我还会额外让学员搜一下READ_PHONE_STATE权限在不同Android版本上的申请时机差异因为很多App因为这个权限在Play商店被拒。5.3 OTA升级机制全量包、增量包、A/B无缝升级android ota热搜词背后是越来越多的业务开始关注系统升级和App灰度更新。OTAOver-The-Air分两层系统级OTA和App内更新。App开发者重点关注的是App内更新机制系统级OTA是ROM开发者的事。练习场里我给的App内更新练习是这样的用Google Play In-app Updates国内可替换为各自厂商的升级SDK实现立即更新和柔性更新两种策略。核心API就两个// 查询是否有可用更新 appUpdateManager.appUpdateInfo.addOnSuccessListener { info - if (info.updateAvailability() UpdateAvailability.UPDATE_AVAILABLE) { // 提示用户更新 } } // 发起更新 appUpdateManager.startUpdateFlowForResult( info, activity, AppUpdateOptions.newBuilder(AppUpdateType.IMMEDIATE).build(), REQUEST_CODE )系统级OTA这里我建议只理解概念全量包是完整镜像体积大但无依赖增量包只包含新旧版本差异部分体积小但强依赖当前版本A/B无缝升级Seamless Updates是双分区方案系统在后台把新系统装到备用分区下次重启直接切换失败也能回滚。理解这些概念对App开发者最大的价值是当你遇到系统升级后App数据丢了升级后app被杀这类问题时能快速判断是不是系统OTA策略导致的。5.4 火焰图怎么抓、怎么读启动卡顿排查的基本功android studio 火焰图 指南常年进热搜说明大家卡顿问题都遇到过但不会用工具。Android Studio自带的CPU Profiler就能生成火焰图操作路径是Debug运行App打开Profiler窗口选择CPU点击Record复现卡顿场景停止录制然后从下拉框里选择Flame Chart。拿到火焰图先看三点底部是入口线程横向宽度代表采样时间占比栈顶是正在执行的函数。一个函数横条特别宽说明它占了大量CPU时间一个函数在火焰图里反复出现说明它被频繁调用。排查卡顿的经典姿势是先把时间轴缩放到卡顿发生的那几秒然后从最宽的栈顶往下找通常能定位到过度绘制、主线程IO、死循环或者频繁GC。这里我要说一个BlockCanary和火焰图的分工BlockCanary用于线上监控主线程有没有卡火焰图用于线下定位卡在哪。练习场里我会让学员在项目里故意写一段主线程循环做字符串拼接的代码然后用火焰图定位到它再手动改成StringBuilder重新抓图对比。这个过程会让人对CPU时间片有非常直观的感知。6. 架构与设备能力组合拳MVVM、蓝牙、播放器SDK、Android/data的地狱级访问6.1 MVVM不只是一张分层图而是一套可以抄的骨架热搜词里android studio mvvm代码示例几乎每天都有新增。我见过太多人把MVVM理解成ViewModelLiveData就完事了实际上MVVM的核心价值在于单向数据流UI层持有ViewModelViewModel暴露状态UI观察状态并渲染用户操作通过ViewModel的方法进入ViewModel调用数据层数据层返回后更新状态状态驱动UI刷新。给一个可以直接抄进练习场的最小骨架// 状态类作为UI的唯一数据源 data class LoginUiState( val isLoading: Boolean false, val username: String , val errorMessage: String? null ) class LoginViewModel : ViewModel() { private val _uiState MutableStateFlow(LoginUiState()) val uiState: StateFlowLoginUiState _uiState.asStateFlow() fun onUsernameChanged(input: String) { _uiState.update { it.copy(username input) } } fun login() { viewModelScope.launch { _uiState.update { it.copy(isLoading true, errorMessage null) } // 调用Repository val result userRepository.login(_uiState.value.username) result.onSuccess { _uiState.update { it.copy(isLoading false) } }.onFailure { e - _uiState.update { it.copy(isLoading false, errorMessage e.message) } } } } } class LoginActivity : AppCompatActivity() { private val viewModel: LoginViewModel by viewModels() override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // 用Lifecycle.repeatOnLifecycle收集状态流 lifecycleScope.launch { repeatOnLifecycle(Lifecycle.State.STARTED) { viewModel.uiState.collect { state - // 把state渲染到UI } } } } }这里我故意用了StateFlow而不是LiveData因为从2024年开始新项目用StateFlow是更主流的选择它跟Compose协同更好也没有主线程限制。练习场里会让学员分别用LiveData和StateFlow写一遍同样的登录页对比两者在postValue、背压、协程取消这几个场景下的行为差异。这个对比做完你对状态管理的理解会超过大多数工作三年的开发。6.2 蓝牙开发的权限适配堪称地狱级但很有规律android蓝牙是个大话题我练习场里只练两个方向经典蓝牙配对、传输文件和BLE低功耗蓝牙扫描、连接、收发特征值。权限适配是最大难点因为Android不同版本对蓝牙权限的要求完全不同Android版本需要的权限说明Android 11及以下BLUETOOTH、BLUETOOTH_ADMIN、ACCESS_FINE_LOCATION扫描BLE需要定位权限属于运行时权限Android 12BLUETOOTH_SCAN、BLUETOOTH_CONNECT、BLUETOOTH_ADVERTISE新增专属蓝牙权限扫描不再依赖定位但需要运行时申请Android 13基本同上但NEARBY_WIFI_DEVICES可能与蓝牙并发使用需要适配附近设备权限组练习场的蓝牙关卡要求是在Android 12和Android 13的设备上完成一次BLE扫描并连接的完整流程。代码层面记住一个关键点——Android 12以上普通蓝牙权限要跟定位权限分开处理BLUETOOTH_SCAN虽然不需要定位权限但如果你的扫描结果需要筛选距离还是得动态申请ACCESS_FINE_LOCATION。另外Android 14开始系统对附近设备权限有更细的运行时窗口做后台扫描会收到系统提醒。6.3 视频播放器SDK集成别在最基础的环节翻车android smartplayer 集成这个热搜词说明很多人在做监控、直播、播放类App时会集成第三方播放器SDK。SmartPlayer本身是一款商用播放器SDK集成的通用步骤基本适用于任何播放器SDK引入aar、初始化SDK、创建播放器实例、设置渲染Surface、设置播放URL、控制播放暂停、销毁播放器。练习场里我总结了三个最容易翻车的点第一Surface生命周期。视频渲染的Surface要等onSurfaceCreated回调之后才能传给播放器传太早会黑屏页面退出时要等播放器停止后再销毁Surface顺序反了会出现声音还在、画面没了的诡异问题。第二音频焦点。播放器如果不处理AudioManager.requestAudioFocusApp切后台再接个电话回来时视频声音可能继续播放直接被应用商店审核拒掉。第三硬解与软解的选择。硬解功耗低、性能好但兼容性不如软解。我的建议是写个codec_switch开关默认硬解遇到特定机型播放花屏时自动切软解。不要把所有机型都强制用同一个解码方式。6.4 两个小功能练手剪贴板复制和Android/data目录访问限制热搜词里有android复制和两条file:///storage/emulated/0/android/data/...的路径前者简单后者是很多App适配时的大坑。剪贴板这块Android 13之后系统会在应用读取剪贴板时弹出隐私提示如果你的App在后台读剪贴板会被系统直接拦截。正确姿势是在Activity可见且获得焦点时才读取val clipboard getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager if (hasWindowFocus()) { val data clipboard.primaryClip?.getItemAt(0)?.text }Android/data目录也就是应用专属外部存储目录的访问限制是从Android 11开始大幅收紧的。普通文件管理器访问/storage/emulated/0/Android/data/com.baidu.searchbox/这类路径时系统会提示此文件夹为空或直接拒绝访问。App内如果要读取自己Android/data目录下的文件可以通过getExternalFilesDir()获取路径但如果你要跟电脑传文件或者让用户手动选择文件就得用系统的文件选择器SAF即ACTION_OPEN_DOCUMENTval intent Intent(Intent.ACTION_OPEN_DOCUMENT).apply { addCategory(Intent.CATEGORY_OPENABLE) type */* } startActivityForResult(intent, REQUEST_CODE)这个知识点跟android copy文件到电脑类问题紧密相关。练习场里我要求学员写一个导出备份文件功能导出路径用MediaStore而不是Android/data导出后用系统文件管理器验证能不能看到。这关通过了你对存储分区和权限模型就有完整的实战认知。7. 练习场的进阶延伸车载系统、多端适配、嵌入式调试基础打牢后往哪走7.1 车载Android不是手机版换个壳而是另一套系统android 车载这个热搜词热度持续上升因为很多开发者都在关注车机方向的职业机会。安卓车载领域主要有两条线一条是Android Automotive OSAAOS这是原生车载系统运行在车机硬件上不包含电话、短信这类手机功能而是强化了多媒体、地图、语音助手和车辆控制接口另一条是Android Auto这是手机延伸到车机屏幕的投影方案。练习场里我建议先学AAOS的Car App Library——它提供了一套组件让你的媒体App能跟车机的正在播放媒体中心仪表盘联动。跟手机App最大的区别在两点一是车机是多屏交互中控屏、仪表盘、副驾屏可能同时显示同一个App的不同状态二是车辆状态必须考虑比如倒挡时中控要立刻切到倒车影像你的App必须响应CarPropertyManager的车辆状态变化。7.2 多端适配的版本底线怎么定热搜词里有一条很有意思a支持ios 11.0及以上android 4.0及以上harmonyos next 5.0及以上。这看起来很像是某个广告SDK或跨端方案写的兼容范围。这里我提醒一句Android 4.0API 14在2024年之后已经基本不可能作为新App的底线了Google Play要求新上架应用最低API级别不低于API 23Android 6.0从2025年开始要求更高。国内应用市场虽然没有统一强制的底线但目标API低于29的App在部分商店已经无法更新。跨端适配的决策逻辑应该是先定你的核心用户用什么设备再看技术栈能覆盖哪些系统。如果面向国内全量市场Android最低支持到API 23-26是比较合理的面向老年人设备可能得再往下放一点面向车机、POS机等专用设备反而要看设备的系统版本上限。鸿蒙NEXT那套5.0适配方案本质是另一套生态做原生Android的人可以先通过Flutter或者鸿蒙的兼容层去了解但不要指望Android代码直接跑在HarmonyOS NEXT上——两者从底层就已经分叉了。7.3 嵌入式调试与开放配件OpenOCD、i2c-tools这些词是什么来头android openocd和i2c-tools在android上使用这类热搜说明有一部分Android开发者已经走入硬件交互领域。OpenOCD是一个开源的片上调试器配合JTAG/SWD接口可以在命令行里烧录固件、设置断点、读写寄存器主要用于嵌入式开发。要是你的Android设备是开发板或者工控机可以用OpenOCD连接芯片调试底层系统。i2c-tools是Linux下操作I2C总线的一组命令行工具在Android上使用通常需要root权限因为有权限控制总线的访问。用法跟嵌入式Linux一样先扫描总线地址再读写寄存器# 查看总线和设备地址 i2cdetect -l # 扫描0号总线 i2cdetect -y 0 # 读取设备0x48的0x00寄存器 i2cget -y 0 0x48 0x00这些工具在Android上最常见的场景是调试传感器、PMIC电源芯片、触摸屏控制器这类外设。练习场里我把这块列为选修但对有兴趣往车载、IoT、智能硬件方向走的人这是性价比很高的加分项——毕竟懂App又懂硬件调试的人在车载领域非常稀缺。最后再分享一点做这套练习场的体会。我不主张把知识点塞得越全越好而是坚持每个知识点必须配触发条件和验证路径触发条件是这个技能在什么时候会被用到验证路径是你能做点什么实验来确认自己真的懂了。比如我记得R8会做压缩优化是记忆亲手跑一遍看到APK体积下降、再故意写个反射代码触发崩溃、最后用keep规则把它修好这是技能。面试题本质上也是这个逻辑的副产品——当你的练习过关次数足够多面试题就只是一次随堂测验而不是临时抱佛脚的猜题游戏。
返回列表