1. 这套综合练习卷到底在考什么
干了这么多年Android开发,我有个很深的感触:很多新人在网上刷了一堆知识点,背了一堆面试八股,真让他们从头做一个小功能的时候,反而容易卡壳。练习卷的意义不在于“背答案”,而是要你把日常开发里零零散散遇到的东西串成一条完整的线。
这份二星级的综合练习卷,定位很清楚:不是算法题,不是源码解析,而是考察一个工程师能不能独立把一个功能模块从头做到尾——从界面搭建、数据流转、状态管理,到权限适配、性能分析和代码安全。用考驾照来类比的话,一星级是考“倒车入库”单项,二星级就是“侧方停车加坡道起步”的组合动作,三星级才轮到路考。所以二星级练卷真正筛选的,是那些已经在项目里写过真实代码、却还没有系统梳理过自己技能树的工程师。
我仔细看了看网上围绕这份练习卷展开的技术讨论,发现热度最高的几个方向,也正是面试官最喜欢追问、项目里最常用到的知识点:Android Studio的版本与AGP兼容、AMS的组件调度机制、MVVM架构的工程落地、R8代码混淆的取舍、动态图标主题的实现、协调布局和Banner联动、透明色值的换算,等等。这篇文章我不打算逐题念答案,而是把这些高频技术点打散,按一个完整的练习卷应该覆盖的维度重新组装,结合我自己在真实项目里踩过的坑,给你一套可以直接照着练的路径。
这套东西适合谁?适合工作一两年、准备跳槽或者准备做中期述职的Android开发,也适合那些自学了大半年、简历上写了好几个Demo项目但心里没底的自学者。它不能帮你一夜之间成为架构师,但能帮你在“会写代码”和“能写对代码”之间的那道坎上,往前迈出一大步。
2. 试卷设计思路:为什么“看起来都会,做起来全错”
2.1 综合练习卷的能力维度拆解
一份优秀的二星级练习卷,不会只盯着某一个知识点反复考。它通常会从五个能力维度出题:UI搭建与动效、数据流转与架构、多线程与异步、组件通信与系统机制、性能与安全加固。这五个维度指向的其实是一种能力——把一个需求翻译成Android系统能够理解的语言。
比如UI维度,出题人不会让你单纯写一个列表,而是会给一个“带Banner轮播的首页Feed流”这种复合场景。这一下就牵扯出协调布局CoordinatorLayout和AppBarLayout的联动、Banner库的选型、RecyclerView的滑动性能优化、列表item的多种类型复用。每个点单独拿出来都不难,组合在一起就考验你对View体系的理解深度了。
再比如数据流转维度,练习卷会给你一个“用户登录并展示个人中心”的需求。这道题表面上考的是接口调用和界面刷新,实际上它想考察你的是:数据从网络层到ViewModel再到UI层,中间经过了哪些转换?生命周期变化时数据怎么保持?页面销毁后异步回调会不会崩溃?这套链路如果平时没有系统梳理过,很容易出现“Activity里直接开线程访问网络、回调里直接更新UI”这种能跑但隐患极大的写法。
2.2 为什么说“二星级”是分水岭
我把这一节单独拎出来说,是因为很多人在一星级练习卷上拿高分之后,就开始飘了,觉得Android开发不过如此。但实际上,一星级题目是“单点验证”,二星级题目是“链路验证”,这两者的差距非常明显。
单点验证考察的是“你知不知道这个API”,链路验证考察的是“你在真实工程里能不能把这个API用对”。举个例子:setColor这个方法,一星级题目可能只问你“如何动态设置TextView文字颜色”,二星级题目则会给一个更有味道的背景场景——“根据网络返回的十六进制颜色值,在深色模式下自动调整文字透明度”。这时候你要考虑的就不只是Color.parseColor了,还得思考Android 8.0以上对自适应图标的支持、深色主题下的对比度、透明度对照表怎么计算。同样是setColor,考出来的技术水平完全不同。
这就是为什么我说二星级是分水岭。能独立完成二星级综合练习卷的人,说明他已经具备“把需求拆成模块、把模块拆成类、把类拆成方法”的工程化思维。而不具备这种思维的人,就算代码写得再花哨,遇到真实项目里的边界条件、兼容性问题,还是会暴露出基本功不扎实的短板。
2.3 这份练习卷的考察形式与评分逻辑
从练习卷的题干风格来看,出题者更喜欢用“黑盒需求+白盒约束”的方式来出题。黑盒需求指的是只告诉你“要做出一个什么样的功能”,不告诉你内部实现;白盒约束指的是在需求下面给你列出一堆硬性条件,比如“必须使用MVVM架构”“不得使用第三方网络库”“最低支持Android 8.0”等。
这种出题方式的评分逻辑非常明确:功能跑通只是及格线,代码结构、异常处理、资源管理和性能表现才是拉分项。如果你只是把功能做出来了,但把所有网络请求都堆在Activity里、没有任何防重复点击处理、列表滑动时卡顿明显,那大概率只能拿一个及格偏下的分数。
我建议你在练习的时候,就按照这个评分逻辑来要求自己:每写完一个模块,先反问自己三个问题——如果用户连续点了十次按钮,我的代码会怎么样?如果网络请求在页面销毁后才返回,我的代码会怎么样?如果产品经理要求把这个逻辑换成另一个实现方式,我的代码需要改动多少?这三个问题能帮你筛掉绝大部分不合格的写法。
3. 核心知识点拆解与实操要点
3.1 环境选型:Android Studio Hedgehog与AGP 8.x的适配问题
练习卷上手第一步,就是环境。网上热度很高的一个问题——Android Studio Hedgehog | 2023.1.1 Patch 2到底支持AGP什么样的版本。这个问题背后其实藏着一个很多人搞不明白的对应关系。
Android Studio的版本号体系和AGP的版本号体系是独立的,但两者之间有严格的兼容范围。Hedgehog版本内置的Gradle插件版本是8.2,对应的AGP版本支持范围是8.0到8.2。也就是说,你在Hedgehog里创建新项目时,AGP版本选8.0、8.1或8.2都能正常跑起来,但如果你手动把AGP升到8.3或更高,就会遇到“Minimum supported Gradle version is X.X.X”之类的报错。另外,AGP 8.0之后有一个比较大的变化——它强制要求JVM 17,所以你的JDK版本必须配套升级,否则会出现“Unsupported class file major version 61”这类让人一头雾水的编译错误。
实际操作建议是这样的:在练习卷的项目配置文件里,把build.gradle的AGP版本和gradle-wrapper.properties里的Gradle版本列成一个对照表放在手边,AGP 8.0对应Gradle 8.0,AGP 8.1对应Gradle 8.0,AGP 8.2对应Gradle 8.2。每次升级任何一环,都必须同步检查另一环,不要单独升级。
3.2 架构落地:MVVM到底是怎么把代码变清楚的
二星级练习卷非常喜欢让你用MVVM来写代码。为什么?因为MVVM架构在合适的业务规模下,能把“界面逻辑”和“业务逻辑”干净地切开,让代码的可读性和可测试性上一个台阶。
我见过太多人把MVVM理解为“我用了ViewModel和LiveData,所以我是MVVM”。这是个很深的误解。MVVM的核心在于数据流向的清晰性——View只负责渲染和转发用户操作,ViewModel只负责持有和转换UI数据,Repository只负责从网络或本地取数据、把数据变成领域模型。三者之间是单向依赖的,谁都不该绕过下游去直接操作上游。
下面这个代码框架是我在实际练习项目里验证过多次的结构,可以直接参考:
// Repository层:只关心数据从哪来,不关心界面 class UserRepository(private val api: UserApi) { suspend fun fetchUserProfile(userId: String): Result<UserProfile> { return try { Result.success(api.getUser(userId).toDomain()) } catch (e: Exception) { Result.failure(e) } } } // ViewModel层:只关心业务状态,不关心View怎么画 class UserViewModel(private val repo: UserRepository) : ViewModel() { private val _uiState = MutableStateFlow<UserUiState>(UserUiState.Loading) val uiState: StateFlow<UserUiState> = _uiState.asStateFlow() fun loadUser(userId: String) { viewModelScope.launch { _uiState.value = UserUiState.Loading repo.fetchUserProfile(userId) .onSuccess { profile -> _uiState.value = UserUiState.Success(profile.toUiModel()) } .onFailure { e -> _uiState.value = UserUiState.Error(e.message ?: "加载失败") } } } } // View层/Activity或Fragment:只负责订阅状态、渲染界面 class ProfileFragment : Fragment() { private val viewModel: UserViewModel by viewModels { ... } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) lifecycleScope.launch { viewModel.uiState.collect { state -> when (state) { is UserUiState.Loading -> showLoading() is UserUiState.Success -> renderProfile(state.data) is UserUiState.Error -> showError(state.message) } } } } }这套结构在二星级题目里基本够用。如果你想把分数再往上拉,可以在Repository层加一层缓存策略——先读数据库缓存,再请求网络刷新,把数据通过Flow的flatMapLatest串联起来。这个设计能直接体现你对“慢网络、断网、弱网”这些真实场景的思考,是加分项。
3.3 系统机制:从AMS看组件生命周期的底层逻辑
练习卷里的系统机制题目,最常出现的考察点就是AMS,即ActivityManagerService。很多人背了Activity的七种生命周期方法,但遇到“为什么A页面跳B页面,A会执行onPause而不一定执行onStop”这种问题就懵了。根本原因是他们只记住了顺序,没有理解AMS在中间做了什么。
简单说,AMS是Android系统里所有组件运行状态的调度中心。每当你启动一个Activity,AMS负责做几件事:检查要启动的Activity在Manifest里是否注册了、看看当前进程需不需要创建、跟WindowManager确认窗口层级关系、把生命周期回调按顺序分发给你。所以Activity的生命周期方法不是你直接调用的,是AMS在合适的时机通过ActivityThread反射调进来的。
有了这个认知,再去理解一些看似奇怪的生命周期现象就顺了。比如为什么“A启动B”时A的onStop会在B的onResume之后才回调?因为AMS需要先确保B对用户可见了,再让前台Activity退居后台,这样做是为了避免画面闪烁。再比如为什么某些手机上按Home键和按返回键,Activity的状态变化顺序不一样?因为Home键是直接让整个Task退到后台,返回键则是先finish掉当前Activity,AMS分发的回调序列自然不同。
3.4 安全与包体:R8混淆不能只开不配
练习卷里有道高频题——如何对项目做代码混淆。说到混淆,就绕不开R8。从AGP 7.0开始,R8已经成为默认的代码压缩和混淆引擎,它把之前ProGuard的工作统一接管了。
R8的核心机制是三步走:代码裁剪,遍历所有入口点,把没被引用的类、方法、字段全部删掉,这一步能显著瘦身;代码混淆,把保留但不需要反射调用的类和方法改成无意义的短名字;资源压缩,删除没被引用的资源文件。听起来很简单,但实际工程里最坑的就是第一步——R8的裁剪逻辑是“从入口点出发做可达性分析”,一旦代码里有通过反射调用的类,R8在静态分析阶段看不到这条调用链,就会把目标类当成无用代码直接删掉,运行期就会抛出ClassNotFoundException。
我建议在你的proguard-rules.pro里,至少给以下几种情况加keep规则:被反射调用的类、被Gson/Json序列化使用的数据类、被JNI调用的native方法所在的类、被注解处理器动态生成的类。另外,如果你的项目里用了ARouter或类似的路由框架,一定要记得给路由表类加keep规则,否则release包一打开就闪退,这是新手最高频的线上事故之一。
3.5 UI进阶:动态图标主题与协调布局的联动实践
动态图标主题这几年在Android社区里讨论度持续走高。练习卷如果刁钻一点,会把它跟CoordinatorLayout放到同一个题目里出——要求你做一个首页,顶部是一个可折叠的Banner区,Banner的颜色主题跟随当前选中的动态图标风格实时切换。
这个需求一拆解,就涉及两个层面的技术储备。第一层是动态图标主题的实现方式。Android 8.0引入了Adaptive Icon,系统通过前景层和背景层的叠加来生成不同形状的图标。要实现“动态切换”,需要拿到Icon的Drawable,然后叠加一层ColorFilter或者使用ThemeOverlay来动态修改元素颜色。第二层是CoordinatorLayout的联动机制。AppBarLayout的滚动行为通过Behavior来响应RecyclerView的滑动事件,Banner的高度变化会被AppBarLayout的onOffsetChanged回调捕获。
两件事结合起来的正确做法,是把“主题色状态”提升到ViewModel里,而不是让Banner组件自己持有。Banner在滚动折叠时,把当前偏移量传给ViewModel,ViewModel计算出当前应该用的主题色,再通过LiveData/StateFlow发回给AppBarLayout里各个需要变色和调整透明度的View。这样既满足动态主题的联动需求,又不至于把颜色状态散落在各个View里,想改逻辑时也不至于牵一发动全身。
3.6 基础细节:透明度对照表与颜色换算
最后补一个看起来不起眼但非常实用的小知识点——颜色透明度换算。练习卷里有道基础题是“把一个颜色从50%透明度改成80%透明度,写出对应的ARGB值”,很多人现场就开始心算,算得满头大汗还算错。
其实这套换算有规律。Android的颜色值是八位十六进制,格式是AARRGGBB,前两位是透明度,后面六位是RGB色值。透明度的计算方式是:百分比乘以255再转十六进制。50%透明对应255乘以0.5等于127.5,四舍五入128,转十六进制是80;80%透明对应255乘以0.8等于204,十六进制是CC。所以50%透明度的纯黑色是#80000000,80%透明度的纯黑色是#CC000000。这些常用档位的换算值,建议直接背下来:10%是1A,20%是33,30%是4D,40%是66,50%是80,60%是99,70%是B3,80%是CC,90%是E6。日常开发里这个表几乎天天用,省下来的时间足够多看两页源码。
4. 实操过程:从环境配置到项目落地的完整路径
4.1 项目初始化:手把手搭出练习卷的标准工程结构
有了前面的理论知识,接下来的重点是亲手走一遍完整流程。我建议你新建一个空项目,目标SDK和最低SDK按练习卷要求设置。开始动手之前,先把依赖仓库和JDK版本环境的问题一次性解决,否则后面每跑一步都会报错。
具体步骤是这样的:第一步,确认JDK版本是17,在命令行执行java -version查看,如果不是,去IDE里把Project Structure的SDK Location指到17的安装路径。第二步,打开gradle-wrapper.properties,确认Gradle版本是8.2;打开项目根目录的build.gradle,确认AGP版本是8.2。第三步,检查Maven仓库配置,确保google()和mavenCentral()都在。做完这三步,95%的“could not load compiled classes for settings file”这类环境问题都能提前规避。
然后开始搭工程目录。我的习惯是按功能模块分包,而不是按类型分包。也就是说,不要搞一个包叫activities、一个包叫adapters、一个包叫models,这种按类型分包的方式在项目小的时候还行,项目一大就是一锅粥。好的做法是按业务模块分包——user包、home包、setting包,每个包里再按需要放该模块的activity、adapter、viewmodel。练习卷的题目如果是“做一个带用户登录的个人中心”,目录结构就应该是:
com.example.practice ├── base/ # 基类、通用工具 ├── data/ │ ├── local/ # 本地数据库与DAO │ ├── remote/ # 网络接口与DTO │ └── repository/ # 数据仓库 ├── ui/ │ ├── home/ # 首页Feed与Banner │ ├── login/ # 登录模块 │ └── profile/ # 个人中心 └── utils/ # 工具类,比如透明度换算工具这种结构在应对二星级练习卷的后续迭代时有一个很大的优势:当练习卷新增一个功能需求时,你只需要在ui目录下新建一个包,再把对应的数据层逻辑加进repository里,不触碰其他模块的代码。工程上的这个“可扩展性”印象分,往往是拿高分的关键。
4.2 核心功能开发:Banner列表页与收藏状态的完整链路
接下来我以练习卷里最常见的“首页Feed流+Banner轮播+点击收藏并本地持久化”这道题为例,给你拆一遍完整的实现过程。
第一步,搭建首页骨架。布局文件最外层用CoordinatorLayout,里面放AppBarLayout和RecyclerView。AppBarLayout内部先放一个高度约200dp的Banner容器,再放一个TabLayout或搜索栏,用来在上滑时固定到顶部。RecyclerView的layoutManager用LinearLayoutManager,adapter用ListAdapter加DiffUtil,这样数据更新时可以精确到item级别做增量刷新,避免整个列表闪烁。
第二步,处理Banner数据。Banner的数据源不要写死在Activity里,而是像我在架构章节说的一样,走Repository -> ViewModel -> Fragment这条链。Banner轮播的定时器用一个Handler放到ViewModel里启动,页面不可见时停止,否则会白耗电。
第三步,实现收藏功能。收藏状态属于“持久化数据”,练习卷通常要求做本地保存。我建议直接用Room,核心代码如下:
@Entity(tableName = "favorite") data class FavoriteEntity( @PrimaryKey val feedId: String, val title: String, val imageUrl: String, val createTime: Long ) @Dao interface FavoriteDao { @Insert(onConflict = OnConflictStrategy.REPLACE) suspend fun insert(favorite: FavoriteEntity) @Query("DELETE FROM favorite WHERE feedId = :feedId") suspend fun delete(feedId: String) @Query("SELECT * FROM favorite") fun observeFavorites(): Flow<List<FavoriteEntity>> } // ViewModel中调用 class FeedViewModel( private val feedRepository: FeedRepository, private val favoriteRepository: FavoriteRepository ) : ViewModel() { val feedState: StateFlow<FeedUiState> = ... fun toggleFavorite(feedItem: FeedItem) { viewModelScope.launch { if (feedItem.isFavorite) { favoriteRepository.delete(feedItem.id) } else { favoriteRepository.insert(feedItem.toEntity()) } } } }这里注意几点:DAO层的方法要用suspend函数,Room底层会自动用子线程执行;数据库实例用单例,不要每次创建;如果需要跨模块访问FavoriteDao,可以考虑用Application的ServiceLocator模式,但练习卷的体量下,直接放在Activity级别的ViewModelProvider里就够了,不必为了设计而设计。
第四步,处理点击跳转。点击Feed item跳详情页,跳转前先判断登录状态——这就是练习卷设的另一个小关卡。未登录弹窗引导登录,已登录则直接跳转。这个判断逻辑要放在ViewModel里,通过返回结果的Callback来驱动UI,不要在Adapter里直接拿Context去startActivity。
4.3 性能检查:用火焰图定位列表卡顿
功能写完,别急着交卷。二星级练习卷的隐藏分数往往放在性能检查上。把你的项目跑起来,打开Android Studio主窗口右下角的Profiler标签,选择CPU Profiler,再打开火焰图模式。
为什么火焰图对排查卡顿这么有用?因为火焰图能直观展示“每个方法在CPU上消耗的时间占比”。如果列表滑动有掉帧,你录制一段滚动操作,再点开火焰图,一眼就能看到是哪个方法占用了大头。我见过最多的卡顿元凶有几个:onBindViewHolder里做了图片的同步加载和压缩、item的布局层级过深导致measure耗时、getItemId返回了重复值导致RecyclerView频繁全量刷新。
用火焰图定位之后,修复方向也很明确。图片加载全面改为异步,解码走BitmapFactory的inSampleSize参数配合网络图片库的占位图机制;布局层级超过三层就考虑用ConstraintLayout压平;getItemId用数据源的唯一主键,确保每次返回的值唯一且稳定。修完再用Profiler跑一遍,对比火焰图里对应方法的耗时,就能直观看到优化效果。
4.4 静态检查与代码评审习惯
性能跑通之后,还剩最后一个容易被忽略的环节——代码的静态质量。练卷如果是在真实项目里演练,建议在提交之前花十分钟跑一遍IDE自带的Lint检查。Lint能抓出Activity/Fragment内存泄漏隐患(比如内部类持有了外部类引用)、未处理的权限请求、硬编码的字符串和颜色值等一堆低级问题。
我在带新人时一直强调一个习惯:每次提交代码之前,把自己当成评审者重新读一遍自己写的每一个文件。这个动作看起来很简单,效果却出奇地好。因为写代码的时候注意力在“怎么把功能做出来”,读代码的时候注意力才会切换到“这么写有没有问题”。二星级练习卷考的不是你写得有多快,而是你写完之后能不能自己发现潜在问题,这个自我纠错能力,才是工作两三年和刚毕业之间的真正差距。
5. 常见问题与排查技巧实录
5.1 高频问题速查表
我把练习过程中最常见的报错、原因和解决方案整理成了一张速查表,你可以直接收藏或者打印出来放在手边。每一个问题都是我或者我带过的同事在真实开发中遇到过的,不是网上抄来的文档。
| 现象 | 根因 | 解决方案 |
|---|---|---|
| 编译报“Minimum supported Gradle version is 8.2” | AGP 8.2要求Gradle 8.2以上 | 打开gradle-wrapper.properties,把distributionUrl改成gradle-8.2-bin.zip |
| Settings文件加载失败(could not load compiled classes) | Gradle编译缓存损坏或JDK版本不符 | 关闭IDE,删除项目根目录的.gradle目录和build目录,重新打开项目 |
| release包启动闪退,日志出现ClassNotFoundException | R8裁剪了反射调用的类 | 在proguard-rules.pro里补充反射路径类的keep规则 |
| 动态图标主题切换后部分控件颜色不跟随 | 主题色写死在布局文件 | 把颜色引用改为?attr/themeColor形式,并在代码中动态更新 |
| 自定义View的onDraw里使用new Pattern造成卡顿 | 每帧都创建了画笔或着色器对象 | 将画笔和着色器提为成员变量,复用对象 |
| 文件读取报FileUriExposedException | targetSdk 24以上直接暴露了文件路径 | 使用FileProvider,配置content://形式的URI |
| 蓝牙扫描不到设备 | 缺少位置权限或者未动态申请权限 | 在Manifest声明并运行时申请ACCESS_FINE_LOCATION |
| 列表收藏状态刷新后错乱 | item复用时未正确保存选中状态 | 在ListAdapter的onBindViewHolder里以数据驱动状态,不能被convertView残留状态干扰 |
| AppBarLayout折叠后Banner不消失 | 未设置alayout_scrollFlags属性 | 给折叠的View设置alayout_scrollFlags="scroll |
| 隐私政策弹窗前申请了危险权限被拒 | Android 6.0以上权限是运行时动态申请的 | 把首次启动的权限申请放到用户点击同意隐私政策之后 |
5.2 我踩过的三个特别想提醒你的坑
第一个坑,是R8混淆之后本地数据库表结构失败。当时我们的Bean类是用Gson直接解析服务端下发的字段,字段名跟JSON key长得很像。开启混淆后,Gson通过反射读取字段名的逻辑被破坏了——R8把字段名改成了a、b、c,Gson按新字段名去匹配JSON,结果全部解析失败。这个问题的标准解法是给数据类整体加keep,或者在字段上标注@SerializedName注解,让Gson不依赖字段名进行解析。从那以后,我所有的项目都优先用@SerializedName,而不是依赖字段名跟JSON key巧合一致。
第二个坑,是协调布局里嵌套刷新控件引发的“滚不动”问题。嵌套滑动机制里,NestedScrollingChild和NestedScrollingParent之间的协作一旦有一方没有正确处理事件分发,就会导致坐标偏移,列表看起来是能滚动但瞬移到某个位置就卡住。排查到最后发现是自定义的Banner容器没有实现NestedScrollingChild接口,微信小程序里常见的“解决滚动冲突”其实是同一类问题。这个问题的排查思路是:先确认所有滚动容器都实现了NestedScrolling接口,再检查scrollFlags是否配置正确,最后再考虑用requestDisallowInterceptTouchEvent强制接管事件序列。
第三个坑,是Android 12及以上系统的主题兼容问题。练习卷里做了动态图标主题,我在自己的项目里也做了类似功能,结果在真机上发现通知栏的图标变成了白色方块。原因是我只改了应用内的主题色,没有处理系统级别的资源颜色引用,尤其是通知栏图标依赖系统的smallIcon资源。要兼容的话,需要在values-v31目录下单独配置SplashScreen主题和通知图标的颜色引用,保证系统组件拿到的颜色资源始终是存在的。
5.3 练习卷之外的三个进阶方向
如果你已经把二星级练习卷做透了,想继续往上走,我建议你按这三个方向去延伸。第一个方向,是研究AMS相关的源码级知识。当你能回答“AMS在进程创建过程中做了哪些事情”“ActivityTaskManager和AMS在Android 10之后是什么关系”这类问题时,你对系统机制的理解就超过大多数同龄人了。第二个方向,是把MVVM架构升级到多模块化设计,把首页、登录、个人中心拆成独立的Gradle模块,用navigation组件或者单Activity架构来组织路由,这个方向练的是大项目的拆分能力。第三个方向,是深入学习R8和打包流程的底层原理,理解Dex编译、资源合并、签名机制这些概念。到了这个层次,你已经不再是一个只会写业务代码的普通开发,而是开始往“懂构建系统、懂性能优化、懂架构治理”的方向进化了。
最后分享一个我这些年带团队的小心得:不管是面试还是实际工作,最能反映一个Android工程师真实水平的,从来不是他简历上写了多少个框架,而是他拿到一个需求之后,能不能自己锁定技术方案、预判风险、写出好维护的代码。二星级练习卷只是一个起点,每天留一点时间,把自己手头项目里的功能往“如果重新设计会怎么做”的方向想一想,坚持半年,你的成长速度会自己告诉你答案。