面试题答案
一键面试针对ViewModel编写高效单元测试
- 使用Mockito库
- 引入Mockito依赖:在
build.gradle
文件中添加testImplementation 'org.mockito:mockito - core:4.11.0'
(版本号可根据实际情况调整)。 - 示例:假设ViewModel中有一个依赖的仓库接口
UserRepository
,可以这样模拟:
import org.junit.Test import org.mockito.Mockito import org.mockito.Mockito.mock import org.mockito.Mockito.verify import androidx.arch.core.executor.testing.InstantTaskExecutorRule import androidx.lifecycle.SavedStateHandle import com.example.app.viewmodel.UserViewModel import com.example.app.repository.UserRepository import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest @ExperimentalCoroutinesApi class UserViewModelTest { @get:Rule val instantExecutorRule = InstantTaskExecutorRule() @Test fun `test fetchUser`() = runTest { val mockRepository = mock(UserRepository::class.java) val savedStateHandle = SavedStateHandle() val viewModel = UserViewModel(mockRepository, savedStateHandle) viewModel.fetchUser() verify(mockRepository).fetchUser() } }
- 引入Mockito依赖:在
- 使用JUnit 5
- 引入JUnit 5依赖:
testImplementation 'org.junit.jupiter:junit - jupiter:5.9.2'
。 - 使用
@Test
注解标记测试方法,如上述示例中的test fetchUser
方法。
- 引入JUnit 5依赖:
- 测试LiveData
- 使用
getValue()
方法获取LiveData的值。例如,假设ViewModel中有一个LiveData对象userLiveData
:
@Test fun `test userLiveData`() = runTest { val mockRepository = mock(UserRepository::class.java) val savedStateHandle = SavedStateHandle() val viewModel = UserViewModel(mockRepository, savedStateHandle) viewModel.fetchUser() val user = viewModel.userLiveData.getValue() assert(user!= null) }
- 使用
大量ViewModel时的性能优化
资源管理
- 复用资源
- 数据库连接:如果ViewModel需要访问数据库,使用单例模式创建数据库连接对象。例如,使用
object
关键字创建一个单例的DatabaseHelper
对象:
object DatabaseHelper { private lateinit var database: SQLiteDatabase fun getDatabase(context: Context): SQLiteDatabase { if (!::database.isInitialized) { val dbHelper = MyOpenHelper(context) database = dbHelper.writableDatabase } return database } }
- 网络请求实例:对于网络请求,如使用OkHttp,可以创建一个单例的
OkHttpClient
实例。
object NetworkClient { val client: OkHttpClient by lazy { OkHttpClient.Builder() .build() } }
- 数据库连接:如果ViewModel需要访问数据库,使用单例模式创建数据库连接对象。例如,使用
- 及时释放资源
- 关闭数据库连接:在ViewModel的
onCleared
方法中关闭数据库连接。例如:
class MyViewModel : ViewModel() { private var database: SQLiteDatabase? = null override fun onCleared() { super.onCleared() database?.close() } }
- 取消网络请求:在ViewModel的
onCleared
方法中取消正在进行的网络请求。如果使用Retrofit
和OkHttp
,可以在Call
对象上调用cancel()
方法。
class MyViewModel : ViewModel() { private var call: Call<MyResponse>? = null override fun onCleared() { super.onCleared() call?.cancel() } }
- 关闭数据库连接:在ViewModel的
数据加载策略
- 分页加载
- 在ViewModel中实现分页逻辑。例如,假设使用RecyclerView展示数据,在ViewModel中定义当前页码和每页数量:
class MyViewModel : ViewModel() { private var currentPage = 1 private val pageSize = 20 val dataList = MutableLiveData<List<MyData>>() fun loadData() { viewModelScope.launch { val response = myRepository.fetchData(currentPage, pageSize) if (response.isSuccess) { dataList.value = response.data currentPage++ } } } }
- 缓存数据
- 内存缓存:使用
LruCache
在内存中缓存数据。例如,创建一个ViewModelCache
类:
class ViewModelCache { private val cache = LruCache<String, Any>(1024 * 1024 * 10) // 10MB cache fun put(key: String, value: Any) { cache.put(key, value) } fun get(key: String): Any? { return cache.get(key) } }
- 磁盘缓存:使用
DiskLruCache
在磁盘上缓存数据。例如,在ViewModel中初始化磁盘缓存:
class MyViewModel : ViewModel() { private lateinit var diskCache: DiskLruCache init { val cacheDir = context.cacheDir diskCache = DiskLruCache.open(cacheDir, 1, 1, 1024 * 1024 * 10) // 10MB cache } // 使用磁盘缓存获取和保存数据的方法 }
- 内存缓存:使用
- 懒加载
- 对于一些非必要立即加载的数据,使用懒加载策略。例如,在ViewModel中定义一个懒加载的LiveData:
class MyViewModel : ViewModel() { private val _lazyData = lazy { viewModelScope.launch { val data = myRepository.fetchLazyData() lazyDataLiveData.postValue(data) } } val lazyDataLiveData = MutableLiveData<MyLazyData>() fun loadLazyData() { _lazyData.value } }