Ai编程前序
现在是Ai的时代,竟然已经知道Android Jetpack的简介和组成,那么我们可以直接问Ai,让它帮我们用Jetpack进行封装一套基础框架出来。我们需要掌握的是方向,不要让Ai给带出轨了。
比如,我是这么问的"帮我用Android Jetpack生成一个基础的框架,需要包含图片、网络、数据库的处理,以及Fragment、Activity、Dialog的声明周期管控,尽量使用封装的方式,增加代码的复用性!"
你需要一个基于 Android Jetpack 的基础项目框架,要求封装图片、网络、数据库模块,同时管控 Activity、Fragment、Dialog 的生命周期,提升代码复用性。下面我会基于 MVVM 架构 搭建这个框架,所有模块均做通用封装,且附带完整的代码结构和示例(见文章末)。
一、框架整体设计
1. 技术栈选型
| 功能模块 | 核心技术/库 |
|---|---|
| 生命周期管控 | Jetpack Lifecycle、ViewModel、SavedStateHandle |
| 网络请求 | Retrofit + OkHttp + Kotlin 协程 |
| 本地数据库 | Jetpack Room(ORM 框架) |
| 图片加载 | Glide(封装统一入口) |
| 基础 UI 封装 | BaseActivity、BaseFragment、BaseDialogFragment |
| 架构模式 | MVVM(ViewModel + Repository + LiveData/Flow) |
2. 项目目录结构(清晰分层,便于扩展)
├── app
│ ├── src
│ │ ├── main
│ │ │ ├── java/com/your/package/
│ │ │ │ ├── App.kt // 应用入口,初始化全局组件
│ │ │ │ ├── core/ // 核心封装(全局复用)
│ │ │ │ │ ├── base/ // 基础组件封装(生命周期管控核心)
│ │ │ │ │ │ ├── BaseActivity.kt
│ │ │ │ │ │ ├── BaseFragment.kt
│ │ │ │ │ │ ├── BaseDialogFragment.kt
│ │ │ │ │ │ └── BaseViewModel.kt
│ │ │ │ │ ├── constant/ // 全局常量
│ │ │ │ │ └── utils/ // 工具类(网络、图片、生命周期等)
│ │ │ │ ├── network/ // 网络模块封装
│ │ │ │ │ ├── ApiService.kt // 接口定义
│ │ │ │ │ ├── RetrofitClient.kt // Retrofit 单例封装
│ │ │ │ │ ├── OkHttpConfig.kt // OkHttp 配置(拦截器、超时等)
│ │ │ │ │ └── model/ // 网络返回数据模型
│ │ │ │ ├── database/ // 数据库模块封装
│ │ │ │ │ ├── AppDatabase.kt // Room 数据库单例
│ │ │ │ │ ├── dao/ // 数据访问接口
│ │ │ │ │ └── entity/ // 数据库实体类
│ │ │ │ ├── image/ // 图片加载模块封装
│ │ │ │ │ └── ImageLoader.kt // Glide 封装入口
│ │ │ │ ├── repository/ // 数据仓库(统一管理网络/本地数据)
│ │ │ │ └── ui/ // 业务页面(示例)
│ │ │ │ ├── main/ // 主页面示例
│ │ │ │ └── demo/ // 功能演示页面
│ │ │ └── res/ // 资源文件
│ │ └── test/ // 单元测试
│ └── build.gradle // 模块依赖配置
二、第一步:项目依赖配置
先在 Module 级 build.gradle(app 目录下)添加所需依赖,确保 Jetpack 组件和第三方库版本兼容:
gradle
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt' // Room 注解处理器需要
}
android {
namespace "com.example.jetpackbase"
compileSdk 34
defaultConfig {
applicationId "com.example.jetpackbase"
minSdk 21
targetSdk 34
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
viewBinding {
enabled = true // 启用 ViewBinding,替代 findViewById,提升 UI 操作安全性
}
}
dependencies {
// ===== Jetpack 核心(生命周期 + 架构)=====
implementation 'androidx.core:core-ktx:1.12.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.11.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
// Lifecycle(生命周期管控)
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.7.0'
implementation 'androidx.lifecycle:lifecycle-fragment-ktx:2.7.0'
// ViewModel + SavedState(进程回收数据恢复)
implementation 'androidx.lifecycle:lifecycle-viewmodel-savedstate:2.7.0'
// Room(数据库)
implementation 'androidx.room:room-runtime:2.6.1'
kapt 'androidx.room:room-compiler:2.6.1'
implementation 'androidx.room:room-ktx:2.6.1' // Room 对 Kotlin 协程的支持
// ===== 网络相关 =====
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
implementation 'com.squareup.retrofit2:converter-gson:2.9.0' // Gson 解析
implementation 'com.squareup.okhttp3:okhttp:4.12.0'
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0' // 网络日志拦截器
// ===== 图片加载 =====
implementation 'com.github.bumptech.glide:glide:4.16.0'
kapt 'com.github.bumptech.glide:compiler:4.16.0'
// ===== 测试 =====
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
}
三、第二步:核心封装(生命周期管控 + 基础 UI)
这是框架的核心,通过封装 Base 系列类,统一管控生命周期,抽离通用逻辑,减少重复代码。
3.1 封装 BaseViewModel(统一管理数据状态 + 生命周期)
所有业务 ViewModel 继承此类,统一处理加载状态、错误信息、数据回调,避免冗余。
kotlin
package com.example.jetpackbase.core.base
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
/**
* 基础 ViewModel,封装通用逻辑:加载状态、错误信息、SavedState 数据恢复
* @param savedStateHandle 用于进程回收后的数据恢复
*/
open class BaseViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() {
// 加载状态(可观察,用于 UI 显示加载框)
private val _loadingState = MutableLiveData<Boolean>(false)
val loadingState: LiveData<Boolean> = _loadingState
// 错误信息(可观察,用于 UI 显示错误提示)
private val _errorMsg = MutableLiveData<String>("")
val errorMsg: LiveData<String> = _errorMsg
// ===== 通用方法封装 =====
/**
* 更新加载状态
*/
protected fun updateLoadingState(isLoading: Boolean) {
_loadingState.postValue(isLoading)
}
/**
* 更新错误信息
*/
protected fun updateErrorMsg(msg: String) {
_errorMsg.postValue(msg)
}
/**
* 保存数据到 SavedState(进程回收后可恢复)
*/
protected fun <T> saveData(key: String, value: T) {
savedStateHandle.set(key, value)
}
/**
* 从 SavedState 读取数据
*/
protected fun <T> getData(key: String, defaultValue: T): T {
return savedStateHandle.get(key) ?: defaultValue
}
/**
* ViewModel 销毁时的清理工作(子类可重写)
*/
override fun onCleared() {
super.onCleared()
// 统一清理资源,避免内存泄漏(如取消协程、关闭流等)
}
}
3.2 封装 BaseActivity(管控 Activity 生命周期 + 通用 UI 逻辑)
统一处理布局加载、ViewModel 初始化、加载状态、生命周期回调,所有业务 Activity 继承此类。
kotlin
package com.example.jetpackbase.core.base
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.ViewModelProvider
import androidx.viewbinding.ViewBinding
import com.example.jetpackbase.core.utils.ToastUtils
/**
* 基础 Activity,封装通用逻辑:
* 1. ViewBinding 初始化
* 2. ViewModel 统一获取
* 3. 生命周期管控
* 4. 通用加载状态、错误提示
*/
abstract class BaseActivity<VB : ViewBinding, VM : BaseViewModel> : AppCompatActivity() {
// 视图绑定(子类无需重复编写 findViewById)
protected lateinit var binding: VB
// 业务 ViewModel
protected lateinit var viewModel: VM
override fun onCreate(savedInstanceState: android.os.Bundle?) {
super.onCreate(savedInstanceState)
// 1. 初始化 ViewBinding
binding = getViewBinding()
setContentView(binding.root)
// 2. 初始化 ViewModel
viewModel = createViewModel()
// 3. 订阅通用数据(加载状态、错误信息)
subscribeCommonData()
// 4. 子类初始化逻辑(页面数据、事件绑定等)
initView()
initData()
initEvent()
}
// ===== 抽象方法(子类必须实现)=====
/**
* 获取 ViewBinding 实例(如:ActivityMainBinding.inflate(layoutInflater))
*/
protected abstract fun getViewBinding(): VB
/**
* 创建 ViewModel 实例
*/
protected abstract fun createViewModel(): VM
/**
* 初始化视图(如:设置标题、隐藏导航栏等)
*/
protected open fun initView() {}
/**
* 初始化数据(如:加载网络数据、本地数据等)
*/
protected open fun initData() {}
/**
* 初始化事件(如:按钮点击、列表条目点击等)
*/
protected open fun initEvent() {}
// ===== 通用逻辑封装 =====
/**
* 订阅 ViewModel 通用数据(加载状态、错误信息)
*/
private fun subscribeCommonData() {
// 监听加载状态(子类可重写 onLoadingChanged 自定义逻辑)
viewModel.loadingState.observe(this) { isLoading ->
onLoadingChanged(isLoading)
}
// 监听错误信息(子类可重写 onError 自定义逻辑,默认 Toast 提示)
viewModel.errorMsg.observe(this) { msg ->
if (msg.isNotBlank()) {
onError(msg)
viewModel.updateErrorMsg("") // 消费错误信息,避免重复展示
}
}
}
/**
* 加载状态变化回调(子类可重写,如:显示/隐藏加载框)
*/
protected open fun onLoadingChanged(isLoading: Boolean) {
// 示例:默认空实现,子类可自定义加载框展示
}
/**
* 错误信息回调(子类可重写,如:显示错误页面、弹窗提示等)
*/
protected open fun onError(msg: String) {
ToastUtils.showShort(msg)
}
// ===== 生命周期管控(子类可重写,增加统一逻辑)=====
override fun onStart() {
super.onStart()
// 统一处理:页面可见时的逻辑(如:开启定位、刷新数据等)
}
override fun onPause() {
super.onPause()
// 统一处理:页面暂停时的逻辑(如:暂停视频播放、停止定位等)
}
override fun onDestroy() {
super.onDestroy()
// 统一清理资源,避免内存泄漏
}
}
3.3 封装 BaseFragment(管控 Fragment 生命周期 + 与 Activity 协同)
Fragment 生命周期比 Activity 更复杂,封装后统一处理视图绑定、ViewModel 初始化、生命周期回调,避免常见坑(如:视图销毁后数据回调导致的空指针)。
kotlin
package com.example.jetpackbase.core.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.viewbinding.ViewBinding
import com.example.jetpackbase.core.utils.ToastUtils
/**
* 基础 Fragment,封装通用逻辑:
* 1. ViewBinding 初始化(避免视图销毁后空指针)
* 2. ViewModel 统一获取
* 3. 生命周期管控(处理 Fragment 重建问题)
* 4. 通用加载状态、错误提示
*/
abstract class BaseFragment<VB : ViewBinding, VM : BaseViewModel> : Fragment() {
// 视图绑定(使用 nullable 类型,避免视图销毁后内存泄漏)
private var _binding: VB? = null
protected val binding: VB get() = _binding!!
// 业务 ViewModel
protected lateinit var viewModel: VM
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// 1. 初始化 ViewBinding
_binding = getViewBinding(inflater, container)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 2. 初始化 ViewModel
viewModel = createViewModel()
// 3. 订阅通用数据(加载状态、错误信息)
subscribeCommonData()
// 4. 子类初始化逻辑
initView()
initData()
initEvent()
}
// ===== 抽象方法(子类必须实现)=====
/**
* 获取 ViewBinding 实例(如:FragmentHomeBinding.inflate(inflater, container, false))
*/
protected abstract fun getViewBinding(inflater: LayoutInflater, container: ViewGroup?): VB
/**
* 创建 ViewModel 实例
*/
protected abstract fun createViewModel(): VM
/**
* 初始化视图
*/
protected open fun initView() {}
/**
* 初始化数据
*/
protected open fun initData() {}
/**
* 初始化事件
*/
protected open fun initEvent() {}
// ===== 通用逻辑封装 =====
private fun subscribeCommonData() {
// 监听加载状态(与 Activity 生命周期协同,仅在视图可见时响应)
viewModel.loadingState.observe(viewLifecycleOwner) { isLoading ->
onLoadingChanged(isLoading)
}
// 监听错误信息
viewModel.errorMsg.observe(viewLifecycleOwner) { msg ->
if (msg.isNotBlank()) {
onError(msg)
viewModel.updateErrorMsg("")
}
}
}
/**
* 加载状态变化回调
*/
protected open fun onLoadingChanged(isLoading: Boolean) {}
/**
* 错误信息回调
*/
protected open fun onError(msg: String) {
ToastUtils.showShort(msg)
}
// ===== 生命周期管控(关键:处理 Fragment 重建)=====
override fun onDestroyView() {
super.onDestroyView()
// 销毁 ViewBinding,避免内存泄漏(Fragment 视图销毁后,Binding 应置空)
_binding = null
}
override fun onStart() {
super.onStart()
// 统一处理:Fragment 可见时的逻辑
}
override fun onPause() {
super.onPause()
// 统一处理:Fragment 暂停时的逻辑
}
}
3.4 封装 BaseDialogFragment(管控 Dialog 生命周期 + 通用弹窗逻辑)
使用 DialogFragment 封装弹窗(自带 Lifecycle 支持,避免传统 Dialog 的生命周期问题),统一处理弹窗样式、数据传递、生命周期回调。
kotlin
package com.example.jetpackbase.core.base
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.DialogFragment
import androidx.lifecycle.ViewModelProvider
import androidx.viewbinding.ViewBinding
import com.example.jetpackbase.core.utils.ToastUtils
/**
* 基础 DialogFragment,封装通用逻辑:
* 1. ViewBinding 初始化
* 2. ViewModel 统一获取
* 3. 生命周期管控(避免弹窗泄漏)
* 4. 通用弹窗样式、数据传递
*/
abstract class BaseDialogFragment<VB : ViewBinding, VM : BaseViewModel> : DialogFragment() {
private var _binding: VB? = null
protected val binding: VB get() = _binding!!
protected lateinit var viewModel: VM
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = getViewBinding(inflater, container)
// 初始化弹窗样式(子类可重写)
initDialogStyle()
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = createViewModel()
subscribeCommonData()
initView()
initData()
initEvent()
}
// ===== 抽象方法 =====
protected abstract fun getViewBinding(inflater: LayoutInflater, container: ViewGroup?): VB
protected abstract fun createViewModel(): VM
protected open fun initView() {}
protected open fun initData() {}
protected open fun initEvent() {}
// ===== 通用逻辑封装 =====
/**
* 初始化弹窗样式(如:宽高、背景、动画等)
*/
protected open fun initDialogStyle() {
dialog?.window?.apply {
// 默认样式:宽度填充屏幕80%,无标题
setLayout(
(resources.displayMetrics.widthPixels * 0.8).toInt(),
ViewGroup.LayoutParams.WRAP_CONTENT
)
setTitle(null)
}
}
private fun subscribeCommonData() {
viewModel.loadingState.observe(viewLifecycleOwner) { isLoading ->
onLoadingChanged(isLoading)
}
viewModel.errorMsg.observe(viewLifecycleOwner) { msg ->
if (msg.isNotBlank()) {
onError(msg)
viewModel.updateErrorMsg("")
}
}
}
protected open fun onLoadingChanged(isLoading: Boolean) {}
protected open fun onError(msg: String) {
ToastUtils.showShort(msg)
}
// ===== 生命周期管控 =====
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
/**
* 关闭弹窗(统一入口,方便子类调用)
*/
protected fun dismissDialog() {
if (isAdded && !isRemoving) {
dismiss()
}
}
}
四、第三步:功能模块封装(图片 + 网络 + 数据库)
4.1 图片加载模块封装(基于 Glide)
封装 ImageLoader 统一图片加载入口,屏蔽 Glide 底层细节,方便后续替换图片加载库(如:换成 Coil),提升复用性。
kotlin
package com.example.jetpackbase.image
import android.content.Context
import android.widget.ImageView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.resource.bitmap.CircleCrop
import com.bumptech.glide.request.RequestOptions
import com.example.jetpackbase.R
/**
* 图片加载工具类(基于 Glide 封装)
* 提供通用图片加载、圆形图片、占位图/错误图等功能
*/
object ImageLoader {
// ===== 通用配置 =====
private val defaultOptions: RequestOptions by lazy {
RequestOptions()
.placeholder(R.mipmap.ic_image_placeholder) // 默认占位图
.error(R.mipmap.ic_image_error) // 默认错误图
.centerCrop()
}
// ===== 公开方法(统一入口)=====
/**
* 加载普通图片
*/
fun loadImage(
context: Context,
imageView: ImageView,
url: String?,
placeholderRes: Int = R.mipmap.ic_image_placeholder,
errorRes: Int = R.mipmap.ic_image_error
) {
val options = defaultOptions
.placeholder(placeholderRes)
.error(errorRes)
Glide.with(context)
.load(url)
.apply(options)
.into(imageView)
}
/**
* 加载圆形图片(如:用户头像)
*/
fun loadCircleImage(
context: Context,
imageView: ImageView,
url: String?,
placeholderRes: Int = R.mipmap.ic_avatar_placeholder,
errorRes: Int = R.mipmap.ic_avatar_error
) {
val options = defaultOptions
.placeholder(placeholderRes)
.error(errorRes)
.transform(CircleCrop()) // 圆形裁剪
Glide.with(context)
.load(url)
.apply(options)
.into(imageView)
}
/**
* 清除图片缓存(内存 + 磁盘)
*/
fun clearCache(context: Context) {
// 清除内存缓存(主线程)
Glide.get(context).clearMemory()
// 清除磁盘缓存(子线程)
Thread {
Glide.get(context).clearDiskCache()
}.start()
}
}
4.2 网络模块封装(Retrofit + OkHttp)
4.2.1 配置 OkHttp(拦截器 + 超时)
kotlin
package com.example.jetpackbase.network
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import java.util.concurrent.TimeUnit
/**
* OkHttp 配置类
* 封装超时设置、日志拦截器、请求头拦截器等
*/
object OkHttpConfig {
// 超时时间(10 秒)
private const val TIMEOUT = 10L
// 构建 OkHttpClient 实例
fun createOkHttpClient(): OkHttpClient {
// 日志拦截器(仅在调试模式下打印网络日志)
val loggingInterceptor = HttpLoggingInterceptor().apply {
level = HttpLoggingInterceptor.Level.BODY
}
return OkHttpClient.Builder()
.connectTimeout(TIMEOUT, TimeUnit.SECONDS) // 连接超时
.readTimeout(TIMEOUT, TimeUnit.SECONDS) // 读取超时
.writeTimeout(TIMEOUT, TimeUnit.SECONDS) // 写入超时
.addInterceptor(loggingInterceptor) // 添加日志拦截器
// 可添加其他拦截器(如:请求头拦截器、Token 拦截器、错误拦截器等)
// .addInterceptor(HeaderInterceptor())
.build()
}
}
4.2.2 封装 Retrofit 单例
kotlin
package com.example.jetpackbase.network
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
/**
* Retrofit 单例类
* 统一配置 BaseUrl、转换器、OkHttpClient 等
*/
object RetrofitClient {
// 基础 Url(替换为你的实际接口地址)
private const val BASE_URL = "https://api.example.com/"
// Retrofit 实例(懒加载,仅初始化一次)
private val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.client(OkHttpConfig.createOkHttpClient())
.addConverterFactory(GsonConverterFactory.create()) // Gson 解析
.build()
}
/**
* 获取 ApiService 实例(通用方法,支持所有接口定义)
*/
fun <T> createService(serviceClass: Class<T>): T {
return retrofit.create(serviceClass)
}
// 示例:获取默认 ApiService 实例(可根据业务拆分多个 ApiService)
val apiService: ApiService by lazy {
createService(ApiService::class.java)
}
}
4.2.3 定义接口与统一返回模型
kotlin
package com.example.jetpackbase.network
import com.example.jetpackbase.network.model.ImageResponse
import retrofit2.http.GET
import retrofit2.http.Query
/**
* 接口定义类(所有网络接口统一在此声明)
*/
interface ApiService {
/**
* 示例:获取图片列表
*/
@GET("images")
suspend fun getImageList(
@Query("page") page: Int,
@Query("size") size: Int
): BaseResponse<List<ImageResponse>>
}
// ===== 统一返回数据模型 =====
/**
* 所有接口的统一返回格式
* @param code 状态码(200 成功,其他失败)
* @param msg 提示信息
* @param data 返回数据
*/
data class BaseResponse<T>(
val code: Int,
val msg: String,
val data: T?
)
/**
* 图片数据模型(示例)
*/
data class ImageResponse(
val id: String,
val url: String,
val title: String,
val createTime: String
)
4.3 数据库模块封装(Jetpack Room)
4.3.1 定义实体类(Entity)
kotlin
package com.example.jetpackbase.database.entity
import androidx.room.Entity
import androidx.room.PrimaryKey
/**
* 图片缓存实体类(示例)
* @Entity 注解:标记为 Room 数据库表,tableName 为表名
*/
@Entity(tableName = "image_cache")
data class ImageCacheEntity(
@PrimaryKey val id: String, // 主键(唯一标识)
val url: String, // 图片地址
val title: String, // 图片标题
val createTime: String, // 创建时间
val cacheTime: Long // 缓存时间(用于清理过期缓存)
)
4.3.2 定义数据访问接口(Dao)
kotlin
package com.example.jetpackbase.database.dao
import androidx.room.Dao
import androidx.room.Insert
import androidx.room.Query
import androidx.room.Update
import com.example.jetpackbase.database.entity.ImageCacheEntity
import kotlinx.coroutines.flow.Flow
/**
* 图片缓存 Dao(数据访问接口,定义增删改查方法)
* @Dao 注解:标记为 Room 数据访问接口
*/
@Dao
interface ImageCacheDao {
/**
* 插入单条图片缓存(如果主键已存在,替换)
*/
@Insert(onConflict = androidx.room.OnConflictStrategy.REPLACE)
suspend fun insertImageCache(imageCache: ImageCacheEntity)
/**
* 批量插入图片缓存
*/
@Insert(onConflict = androidx.room.OnConflictStrategy.REPLACE)
suspend fun insertImageCacheList(imageCacheList: List<ImageCacheEntity>)
/**
* 根据 ID 查询图片缓存
*/
@Query("SELECT * FROM image_cache WHERE id = :id")
suspend fun getImageCacheById(id: String): ImageCacheEntity?
/**
* 查询所有图片缓存(返回 Flow,支持数据变化监听)
*/
@Query("SELECT * FROM image_cache ORDER BY cacheTime DESC")
fun getAllImageCacheFlow(): Flow<List<ImageCacheEntity>>
/**
* 删除过期缓存(根据缓存时间)
*/
@Query("DELETE FROM image_cache WHERE cacheTime < :expireTime")
suspend fun deleteExpireImageCache(expireTime: Long)
}
4.3.3 封装数据库单例(AppDatabase)
kotlin
package com.example.jetpackbase.database
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.example.jetpackbase.App
import com.example.jetpackbase.database.dao.ImageCacheDao
import com.example.jetpackbase.database.entity.ImageCacheEntity
/**
* Room 数据库单例类
* @Database 注解:定义数据库版本、包含的实体类
*/
@Database(
entities = [ImageCacheEntity::class], // 包含的实体类
version = 1, // 数据库版本(升级时需修改)
exportSchema = false // 关闭 schema 导出(生产环境建议开启)
)
abstract class AppDatabase : RoomDatabase() {
// 获取 Dao 实例(Room 自动实现)
abstract fun imageCacheDao(): ImageCacheDao
// 单例实现
companion object {
// 双重校验锁单例(避免多线程初始化问题)
@Volatile
private var INSTANCE: AppDatabase? = null
fun getInstance(): AppDatabase {
return INSTANCE ?: synchronized(this) {
val instance = Room.databaseBuilder(
App.context, // 全局上下文(在 App 中初始化)
AppDatabase::class.java,
"jetpack_base_db" // 数据库文件名
)
.fallbackToDestructiveMigration() // 版本升级时销毁旧数据库(开发环境,生产环境需写迁移逻辑)
.build()
INSTANCE = instance
instance
}
}
}
}
4.3.4 封装 Repository(统一管理网络/本地数据)
遵循 MVVM 架构,Repository 作为数据层统一入口,屏蔽数据来源(网络/本地),ViewModel 仅与 Repository 交互,提升代码解耦和复用性。
kotlin
package com.example.jetpackbase.repository
import com.example.jetpackbase.database.AppDatabase
import com.example.jetpackbase.database.entity.ImageCacheEntity
import com.example.jetpackbase.network.RetrofitClient
import com.example.jetpackbase.network.model.ImageResponse
/**
* 图片数据仓库(统一管理网络图片数据和本地缓存数据)
*/
class ImageRepository {
// 数据库 Dao 实例
private val imageCacheDao = AppDatabase.getInstance().imageCacheDao()
// ===== 网络数据操作 =====
/**
* 从网络获取图片列表
*/
suspend fun getImageListFromNetwork(page: Int, size: Int): Result<List<ImageResponse>> {
return try {
val response = RetrofitClient.apiService.getImageList(page, size)
if (response.code == 200 && response.data != null) {
// 网络请求成功,缓存到本地数据库
val cacheList = response.data!!.map {
ImageCacheEntity(
id = it.id,
url = it.url,
title = it.title,
createTime = it.createTime,
cacheTime = System.currentTimeMillis()
)
}
imageCacheDao.insertImageCacheList(cacheList)
Result.success(response.data!!)
} else {
Result.failure(Throwable(response.msg ?: "网络请求失败"))
}
} catch (e: Exception) {
Result.failure(e)
}
}
// ===== 本地数据操作 =====
/**
* 从本地数据库获取图片缓存列表(支持数据变化监听)
*/
fun getImageListFromLocal() = imageCacheDao.getAllImageCacheFlow()
/**
* 根据 ID 获取本地图片缓存
*/
suspend fun getImageCacheById(id: String) = imageCacheDao.getImageCacheById(id)
/**
* 清理过期图片缓存
*/
suspend fun clearExpireImageCache(expireTime: Long) = imageCacheDao.deleteExpireImageCache(expireTime)
}
五、第四步:全局初始化与示例使用
5.1 应用入口(App.kt)
kotlin
package com.example.jetpackbase
import android.app.Application
import android.content.Context
/**
* 应用入口类,初始化全局组件
*/
class App : Application() {
companion object {
// 全局上下文(避免内存泄漏,仅用于非 UI 场景)
lateinit var context: Context
}
override fun onCreate() {
super.onCreate()
context = this.applicationContext
// 初始化全局组件(如:数据库、网络、图片缓存等)
// 1. 初始化 Room 数据库(预加载,可选)
AppDatabase.getInstance()
// 2. 初始化 Glide(可选,Glide 会自动初始化)
// Glide.init(context, GlideBuilder())
}
}
5.2 示例:使用框架实现图片加载与缓存
kotlin
// 1. 业务 ViewModel
package com.example.jetpackbase.ui.demo
import androidx.lifecycle.SavedStateHandle
import com.example.jetpackbase.core.base.BaseViewModel
import com.example.jetpackbase.repository.ImageRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
class ImageDemoViewModel(savedStateHandle: SavedStateHandle) : BaseViewModel(savedStateHandle) {
private val imageRepository = ImageRepository()
// 加载网络图片列表
fun loadImageList(page: Int = 1, size: Int = 20) {
updateLoadingState(true)
GlobalScope.launch(Dispatchers.IO) {
val result = imageRepository.getImageListFromNetwork(page, size)
updateLoadingState(false)
result.onSuccess {
// 数据加载成功,通知 UI 更新
}.onFailure {
// 数据加载失败,更新错误信息
updateErrorMsg(it.message ?: "加载图片列表失败")
}
}
}
}
// 2. 业务 Activity
package com.example.jetpackbase.ui.demo
import androidx.lifecycle.ViewModelProvider
import com.example.jetpackbase.core.base.BaseActivity
import com.example.jetpackbase.databinding.ActivityImageDemoBinding
import com.example.jetpackbase.image.ImageLoader
class ImageDemoActivity : BaseActivity<ActivityImageDemoBinding, ImageDemoViewModel>() {
override fun getViewBinding() = ActivityImageDemoBinding.inflate(layoutInflater)
override fun createViewModel() = ViewModelProvider(this)[ImageDemoViewModel::class.java]
override fun initView() {
// 加载示例图片
ImageLoader.loadImage(this, binding.ivDemo, "https://example.com/demo.jpg")
// 加载圆形头像
ImageLoader.loadCircleImage(this, binding.ivAvatar, "https://example.com/avatar.jpg")
}
override fun initData() {
// 加载图片列表
viewModel.loadImageList()
}
}
六、框架总结与扩展建议
总结
- 生命周期管控 :通过
BaseActivity/BaseFragment/BaseDialogFragment统一封装,结合ViewModel/LiveData,避免内存泄漏,简化生命周期逻辑处理。 - 模块封装:图片、网络、数据库均提供统一入口,屏蔽底层细节,提升代码复用性和可维护性,后续替换第三方库无需修改业务代码。
- 架构规范 :遵循 MVVM 架构,通过
Repository统一管理数据来源,实现 UI 与数据层解耦,便于单元测试和功能扩展。
这个简易框架是基础版本,足够满足日常开发的大部分需求,你可以根据实际业务场景进行扩展和优化。
以上为学习资料,下面是代码案例,跟上面资料有差异
https://github.com/HunterHR/AndroidJetpackFirst
扩展建议
- 添加协程封装 :统一处理协程的异常和生命周期,避免使用
GlobalScope,推荐使用viewModelScope/lifecycleScope。 - 添加错误处理:封装全局错误拦截器,统一处理网络错误、数据库错误、业务错误。
- 添加缓存策略:完善图片缓存和数据缓存的过期策略,提升应用离线体验。
- 添加组件化支持:如果项目规模扩大,可基于此框架进行组件化拆分,提升团队协作效率。
- 添加 UI 组件封装:封装通用的列表、弹窗、下拉刷新等 UI 组件,进一步提升复用性。