2. 项目初始化与依赖配置
本章将引导你从零搭建一个以离线优先为核心理念的 Android 项目,并完成核心依赖库的引入与 Gradle 配置。我们将依次完成:项目骨架创建、依赖版本管理、Room / WorkManager / 网络库 / 依赖注入框架的集成,以及构建脚本的优化。正确的初始化与配置是后续章节所有离线能力的基石。
2.1 创建 Android 项目
使用 Android Studio 的 New Project 向导,选择 Empty Views Activity 模板,包名建议采用 com.yourdomain.offlinefirst。最低 SDK 版本推荐 API 24(Android 7.0),以确保 Room 和 WorkManager 的完整支持。项目创建后,立即检查 gradle/libs.versions.toml 是否存在——如果使用 Android Studio Hedgehog 或更高版本,项目默认采用版本目录(Version Catalog)管理依赖,这正是我们需要的。
libs.versions.toml,可在项目根目录下手动创建 gradle/libs.versions.toml 文件。版本目录是大型团队协作的最佳实践,能统一所有模块的依赖版本。2.2 版本目录与核心依赖声明
编辑 gradle/libs.versions.toml,定义全局版本变量与依赖库坐标。下面的配置涵盖了离线优先应用最关键的四个组件:
[versions]
kotlin = "1.9.22"
agp = "8.2.2"
room = "2.6.1"
work = "2.9.0"
retrofit = "2.9.0"
okhttp = "4.12.0"
hilt = "2.50"
ksp = "1.9.22-1.0.17"
coroutines = "1.8.0"
[libraries]
room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" }
room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" }
room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "room" }
work-runtime = { group = "androidx.work", name = "work-runtime-ktx", version.ref = "work" }
retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version.ref = "retrofit" }
retrofit-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version.ref = "retrofit" }
okhttp-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version.ref = "okhttp" }
hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" }
hilt-compiler = { group = "com.google.dagger", name = "hilt-android-compiler", version.ref = "hilt" }
coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
[plugins]
android-application = { id = "com.android.application", version.ref = "agp" }
kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" }
hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" }
2.3 模块级 build.gradle.kts 配置
在 app/build.gradle.kts 中应用插件并添加依赖库。注意 Room 的编译器使用 KSP 而非 kapt,这能显著提升注解处理速度。
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.ksp)
alias(libs.plugins.hilt)
}
android {
namespace = "com.yourdomain.offlinefirst"
compileSdk = 34
defaultConfig {
applicationId = "com.yourdomain.offlinefirst"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0.0"
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
// 开启 BuildConfig 以在代码中使用版本号等常量
buildFeatures {
buildConfig = true
}
// 如果使用 Room 的实体多表关联,需要启用 schema 导出
ksp {
arg("room.schemaLocation", "$projectDir/schemas")
}
}
dependencies {
// Room
implementation(libs.room.runtime)
implementation(libs.room.ktx)
ksp(libs.room.compiler)
// WorkManager
implementation(libs.work.runtime)
// 网络层
implementation(libs.retrofit.core)
implementation(libs.retrofit.gson)
implementation(libs.okhttp.logging)
// 依赖注入
implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
// 协程
implementation(libs.coroutines.core)
// 测试依赖(可选)
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
}
2.4 项目级 build.gradle.kts 与 settings.gradle.kts
确保项目级文件正确应用了版本目录和 KSP 插件。典型配置如下:
// 项目根 build.gradle.kts
plugins {
alias(libs.plugins.android.application) apply false
alias(libs.plugins.kotlin.android) apply false
alias(libs.plugins.ksp) apply false
alias(libs.plugins.hilt) apply false
}
// settings.gradle.kts
pluginManagement {
repositories {
google()
mavenCentral()
gradlePluginPortal()
}
}
dependencyResolutionManagement {
repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
repositories {
google()
mavenCentral()
}
}
rootProject.name = "OfflineFirstApp"
include(":app")
ksp 替代,否则会出现重复注解处理。检查 app/build.gradle.kts 中不应出现 kapt 相关行。2.5 添加 Application 类与 Hilt 入口
Hilt 要求应用提供 @HiltAndroidApp 注解的 Application 子类。在 OfflineFirstApp.kt 中添加:
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class OfflineFirstApp : Application()
记得在 AndroidManifest.xml 的 <application> 标签中指定 android:name=".OfflineFirstApp"。
2.6 网络层与 WorkManager 初始化配置
在 OfflineFirstApp 中或者通过独立的 Hilt Module 配置 OkHttp 客户端和 Retrofit 实例。WorkManager 不需要额外初始化代码,只要依赖添加正确即可自动使用默认配置。若需自定义 WorkManager 初始化(例如更改线程池大小),可在 AndroidManifest.xml 中用 android:name="androidx.work.impl.WorkManagerInitializer" 节点覆盖默认 provider。
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule {
@Provides
@Singleton
fun provideOkHttpClient(): OkHttpClient {
return OkHttpClient.Builder()
.addInterceptor(HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
})
.build()
}
@Provides
@Singleton
fun provideRetrofit(okHttpClient: OkHttpClient): Retrofit {
return Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
}
2.7 常见错误与调试
| 错误现象 | 原因 | 解决方案 |
|---|---|---|
Room 编译报错:Cannot find symbol | KSP 插件版本与 Room 版本不兼容,或未添加 KSP 插件 | 检查 libs.versions.toml 中 ksp 版本号是否与 Kotlin 版本匹配(例如 1.9.22‑1.0.17) |
| WorkManager 在 Android 12+ 上崩溃 | 未添加 android:foregroundServiceType 权限声明 | 在 AndroidManifest.xml 的 <service> 中设置 android:foregroundServiceType="dataSync" |
Hilt 注入时出现 Dagger does not support injection into @HiltViewModel | ViewModel 未使用 @HiltViewModel 注解 | 添加 @HiltViewModel 并使用 @Inject constructor |
| Retrofit 接口调用无网络日志 | OkHttp 日志拦截器等级设置不正确 | 使用 BODY 或 HEADERS,并确保调试 Build 下生效(可添加 addNetworkInterceptor) |
2.8 性能与稳定性优化建议
- 使用版本目录(Version Catalog):避免硬编码版本号,避免多模块版本漂移。
- 启用 Room 的 WAL 模式:在
Room.databaseBuilder()中调用.setJournalMode(JournalMode.WRITE_AHEAD_LOGGING)可提升并发写入性能。 - WorkManager 的唯一工作名称:为同步任务指定唯一
WorkRequest名称(OneTimeWorkRequest.from),防止重复调度。 - 网络超时设置:在 OkHttp 中设置合理的
connectTimeout和readTimeout(例如 15 秒),避免无响应挂起。 - 保留混淆规则:添加
proguard-rules.pro,保留 Room 实体、Retrofit 接口、Hilt 生成的类:
-keep class * extends androidx.room.RoomDatabase { *; }
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.SerializedName <fields>;
}
-keep class dagger.hilt.** { *; }
./gradlew assembleDebug 应能成功编译。上图展示了离线优先应用的依赖层次:app 模块通过 Gradle 配置与版本目录 引入 Room / WorkManager / 网络层 / Hilt 四大核心库,底部辅助工具(协程与 Manifest 配置)为骨架提供运行时支持。箭头方向表示配置依赖关系。接下来每一章都将基于此骨架扩展功能。