做 Android 开发这几年,最深刻的体会就是:不要重复造轮子,但要清楚轮子的材质和刹车片在哪。

现在的 Android 生态已经非常成熟,从 UI 渲染到网络请求,从图片加载到依赖注入,几乎每个环节都有经过海量项目验证的“黄金标准”。很多刚入行的朋友或者急于赶进度的团队,容易陷入两个极端:要么自己手写所有基础组件,导致 Bug 频出且难以维护;要么盲目引入一堆重型框架,把 App 体积撑得巨大。

今天我不搞那些虚头巴脑的理论,直接给你盘点几类真正在一线大厂和高星开源项目中被反复验证、值得你直接 Copy-Paste(或者更准确地说是 Add Dependency)的核心库。我会结合具体场景、代码片段以及为什么选它不选别的,把这些干货掰碎了讲给你听。


一、 异步与并发:告别回调地狱

Android 开发中,处理后台任务依然是重灾区。虽然 Kotlin Coroutines 已经是官方首选,但在某些特定场景下,配合 RxJava 或特定的并发工具类依然能事半功倍。不过,对于大多数现代项目,Kotlin Coroutines + Flow 是绝对的主流。但如果你的项目需要更复杂的流式数据处理,或者需要与旧代码兼容,以下工具至关重要。

1. Kotlin Coroutines (官方标配)

如果你还在用 AsyncTask 或者大量的 Handler 切换线程,请立刻停止。Coroutines 让异步代码看起来像同步代码,逻辑清晰得多。

场景: 并行执行多个网络请求,然后合并结果。

// 传统写法可能嵌套很深,或者需要 CountDownLatch
// 使用 coroutine 的 async/await 模式:

suspend fun loadDashboardData(): Dashboard {
    return coroutineScope {
        // 并行启动三个任务
        val userDeferred = async { api.getUser() }
        val newsDeferred = async { api.getNews() }
        val settingsDeferred = async { api.getSettings() }

        // 等待所有结果返回,如果任何一个抛出异常,整个 scope 会取消
        Dashboard(
            user = userDeferred.await(),
            news = newsDeferred.await(),
            settings = settingsDeferred.await()
        )
    }
}

专家点评: 注意 coroutineScope 的使用。它与 launch 不同,它会等待块内所有的子协程完成才返回。这是处理“收集型”数据的最佳实践。

2. Flow (状态管理基石)

Flow 是 Kotlin 提供的冷流集合。它比 LiveData 更强大,支持背压(Backpressure),并且完全基于协程。

场景: 用户输入搜索关键词,防抖后发起网络请求。

// 在 ViewModel 中
val searchResults = viewModelScope.launch {
    searchQueryFlow
        .debounce(300) // 防抖:用户停止输入300ms后才触发
        .distinctUntilChanged() // 去重:如果两次输入相同,不重新请求
        .flatMapLatest { query -> 
            api.search(query) // 如果新查询到来,取消旧的请求
        }
        .collect { results ->
            _searchResultUIState.value = SearchSuccess(results)
        }
}

二、 图片加载:性能与体验的平衡器

图片加载不仅仅是显示一张图,它涉及内存缓存、磁盘缓存、缩略图计算、占位符处理、GIF/WebP 支持等复杂逻辑。自己写一个稳定且高性能的图片加载器,成本极高。

1. Glide (Google 御用,稳定之王)

Glide 是目前 Android 上最流行的图片加载库之一,由 Google 维护。它的默认配置非常合理,特别适合列表滑动场景。

核心优势:

  • 自动生命周期管理: 绑定 Activity/Fragment 生命周期,防止内存泄漏。
  • 资源占用低: 默认使用 RGB_565 格式,节省内存。
  • 转换丰富: 内置圆角、裁剪、模糊等效果。

代码示例:

// 简单加载
Glide.with(context)
    .load(imageUrl)
    .placeholder(R.drawable.loading_spinner) // 加载中显示
    .error(R.drawable.error_image)           // 加载失败显示
    .centerCrop()                            // 中心裁剪
    .into(imageView)

// 高级用法:预加载到内存缓存,用于详情页点击列表项时瞬间显示
Glide.with(context)
    .preload(imageUrl) 

2. Coil (Kotlin First, Modern Choice)

如果你的项目全是 Kotlin,且追求更现代的 API 和更小的包体积,Coil 是 Glide 的有力竞争者。它基于 Kotlin Coroutines 构建,API 设计极其优雅。

对比 Glide:

  • Coil 不需要 Context,可以直接在任何地方调用。
  • Coil 使用 Kotlin DSL,链式调用更简洁。
  • Coil 对 GIF 和 WebP 的支持原生且轻量。

代码示例:

imageView.load(url) {
    placeholder(R.drawable.loading_spinner)
    error(R.drawable.error_image)
    transformations(CircleCropTransformation())
    crossfade(true) // 默认开启淡入淡出
}

建议: 新项目优先考虑 Coil,老项目维护优先 Glide


三、 网络请求:Retrofit + OkHttp 的黄金组合

在网络层,Retrofit 依然是事实上的标准。它类型安全、易于测试,并且通过 Converter 适配器可以轻松对接 Gson、Moshi 或 kotlinx.serialization。

关键点:拦截器与错误统一处理

很多开发者只关注 @GET@POST,却忽略了 OkHttp Interceptor 的力量。这是实现 Token 刷新、日志打印、统一错误处理的绝佳位置。

实战:自动刷新 Token 的拦截器逻辑

class AuthInterceptor(private val tokenProvider: () -> String) : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val originalRequest = chain.request()
        
        // 1. 添加 Authorization Header
        val requestWithAuth = originalRequest.newBuilder()
            .header("Authorization", "Bearer ${tokenProvider()}")
            .build()
            
        val response = chain.proceed(requestWithAuth)
        
        // 2. 如果返回 401,尝试刷新 Token 并重试
        if (response.code == 401 && originalRequest.header("Authorization") != null) {
            try {
                val newToken = refreshTokenSync() // 假设这是一个同步刷新方法
                // 更新本地 Token 存储
                saveNewToken(newToken)
                
                // 使用新 Token 重试原请求
                val retryRequest = originalRequest.newBuilder()
                    .header("Authorization", "Bearer $newToken")
                    .build()
                return chain.proceed(retryRequest)
            } catch (e: Exception) {
                // 刷新失败,跳转登录页
                throw e
            }
        }
        
        return response
    }
}

专家提示: 不要把所有逻辑都塞进 Retrofit 的 Callback 里。利用 OkHttp 的拦截器机制,将横切关注点(Cross-cutting concerns)如认证、日志、缓存策略剥离出来,保持业务代码干净。


四、 依赖注入:Hilt 让代码解耦

以前我们用 Dagger 2,配置繁琐,XML 生成文件满天飞。现在,Hilt 作为 Dagger 的封装,提供了更简单的注解驱动方式。对于中小型项目甚至大型项目,Hilt 都能胜任。

为什么需要 DI?

  • 测试友好: 可以轻松替换 Mock 对象进行单元测试。
  • 解耦: Activity 不再直接 new 数据库实例或网络服务。
  • 生命周期安全: Hilt 能自动管理组件的生命周期,避免内存泄漏。

代码示例:

// 1. 定义模块
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
    
    @Provides
    @Singleton
    fun provideRetrofit(): Retrofit {
        return Retrofit.Builder()
            .baseUrl("https://api.example.com/")
            .addConverterFactory(MoshiConverterFactory.create())
            .build()
    }
    
    @Provides
    @Singleton
    fun provideApiService(retrofit: Retrofit): ApiService {
        return retrofit.create(ApiService::class.java)
    }
}

// 2. 在 Activity 中注入
@AndroidEntryPoint
class MainActivity : AppCompatActivity() {
    
    @Inject lateinit var apiService: ApiService
    
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        // 直接使用,无需手动初始化
        lifecycleScope.launch {
            val data = apiService.getData()
            // ...
        }
    }
}

五、 UI 与动画:Jetpack Compose 与 Lottie

1. Jetpack Compose

如果你还没开始接触 Compose,现在是最好的时候。声明式 UI 彻底改变了 Android 开发范式。

优势:

  • 代码量少: 同样的界面,Compose 的代码量通常是 XML 的 1/3。
  • 预览强大: 可以在 IDE 中实时预览各种状态下的 UI。
  • 状态驱动: 数据变化自动更新 UI,无需手动 findViewByIdsetText

例子:一个简单的按钮组件

@Composable
fun PrimaryButton(
    text: String,
    onClick: () -> Unit,
    modifier: Modifier = Modifier
) {
    Button(
        onClick = onClick,
        modifier = modifier.fillMaxWidth().padding(16.dp),
        colors = ButtonDefaults.buttonColors(containerColor = Color.Blue)
    ) {
        Text(text = text, color = Color.White)
    }
}

2. Lottie (动画神器)

设计师给的复杂动画(JSON 格式),用传统 View 动画实现需要几百行代码,用 Lottie 只需要一行。

代码示例:

// 在 XML 中
<com.airbnb.lottie.LottieAnimationView
    android:id="@+id/successAnimation"
    app:lottie_rawRes="@raw/success_json"
    app:lottie_autoPlay="true"
    app:lottie_loop="false"
    android:layout_width="200dp"
    android:layout_height="200dp" />

// 在代码中控制进度
successAnimation.addAnimatorUpdateListener { animation ->
    val progress = animation.animatorValue
    // 根据进度更新其他 UI 元素,比如进度条
}

六、 数据库与本地存储:Room 与 DataStore

1. Room (SQLite 的抽象层)

Room 不是 ORM,它是一个 SQLite 的抽象层,编译时检查 SQL 语句的正确性,避免了运行时崩溃。

实体类定义:

@Entity(tableName = "users")
data class User(
    @PrimaryKey val id: Int,
    val name: String,
    val email: String
)

@Dao
interface UserDao {
    @Insert(onConflict = OnConflictStrategy.REPLACE)
    suspend fun insert(user: User)

    @Query("SELECT * FROM users WHERE id = :userId")
    suspend fun getUserById(userId: Int): User?
    
    @Query("SELECT * FROM users ORDER BY name ASC")
    fun getAllUsers(): Flow<List<User>>
}

2. DataStore (SharedPreferences 的现代替代品)

SharedPreferences 是阻塞式的,且在处理复杂数据类型时很麻烦。DataStore 基于 Protobuf 或 Preferences,支持异步操作和 Coroutines。

使用 Preferences DataStore 存储简单设置:

// 定义键
object PreferencesKeys {
    val THEME_MODE = stringPreferencesKey("theme_mode")
}

// 读取
val themeMode: Flow<ThemeMode> = dataStore.data
    .map { preferences ->
        when (preferences[PreferencesKeys.THEME_MODE]) {
            "dark" -> ThemeMode.Dark
            else -> ThemeMode.Light
        }
    }

// 写入
suspend fun setTheme(mode: ThemeMode) {
    dataStore.edit { preferences ->
        preferences[PreferencesKeys.THEME_MODE] = 
            if (mode == ThemeMode.Dark) "dark" else "light"
    }
}

七、 测试:保证质量的最后一道防线

很多团队忽略测试,觉得浪费时间。但事实上,没有测试的代码,重构就是噩梦。

1. 单元测试:JUnit + Mockito/Kluent

测试 ViewModel 的逻辑,确保 UI 状态更新正确。

@Test
fun `when user logs in successfully, then show success message`() = runTest {
    // Given
    val mockApi = mock<ApiService>()
    val viewModel = LoginViewModel(mockApi)
    
    // When
    viewModel.login("user", "pass")
    
    // Then
    assertEquals(viewModel.uiState.value, LoginState.Success)
}

2. 截图测试:Screenshot Testing

对于 UI 变化,传统的断言很难覆盖。可以使用 Facebook 的 Screenshot Testing 库,自动对比 UI 截图,防止样式崩坏。


八、 构建与优化:ProGuard/R8 与 Gradle 配置

最后,别忘了打包前的优化。

  1. 启用 R8:gradle.properties 中设置 android.enableR8=true,它比 ProGuard 更快、更小。
  2. 资源压缩: 移除未使用的资源和字符串。
  3. 多 Dex 支持: 当方法数超过 65k 时,确保配置了 multiDexEnabled true(AndroidX 已自动处理大部分情况)。

总结:如何选择?

需求场景 推荐方案 理由
网络请求 Retrofit + OkHttp 行业标准,生态完善,拦截器强大
图片加载 Coil (新) / Glide (稳) Coil 更现代轻量,Glide 兼容性极好
异步并发 Kotlin Coroutines + Flow 官方支持,语法简洁,解决回调地狱
依赖注入 Hilt 基于 Dagger,上手简单,配置少
本地存储 Room (复杂) / DataStore (简单) Room 适合结构化数据,DataStore 替代 SP
UI 框架 Jetpack Compose 声明式,预览好,未来趋势

给开发者的建议:

技术选型没有绝对的“最好”,只有“最适合”。

  1. 从小处着手: 不要一次性重构整个项目。先在新的 Feature Module 中使用新库。
  2. 阅读源码: 当你遇到 Bug 时,阅读这些开源库的源码(它们通常很干净),你会学到很多架构设计的精髓。
  3. 保持更新: Android 生态迭代很快,定期关注 GitHub Trending 和官方 Blog,但不要盲目追逐最新 Beta 版本。

希望这份清单能帮你构建更高效、更稳定的 Android 应用。如果有具体的技术难点,欢迎继续深入交流。