Android kotlin Jetpack mvvm 项目,阿里+头条+腾讯大厂Android笔试真题
- 富有表现力且简洁:您可以使用更少的代码实现更多的功能。表达自己的想法,少编写样板代码。在使用 Kotlin 的专业开发者中,有 67% 的人反映其工作效率有所提高。
- 更安全的代码:Kotlin 有许多语言功能,可帮助您避免 null 指针异常等常见编程错误。包含 Kotlin 代码的 Android 应用发生崩溃的可能性降低了 20%。
- 可互操作:您可以在 Kotlin 代码中调用 Java 代码,或者在 Java 代码中调用 Kotlin 代码。Kotlin 可完全与 Java 编程语言互操作,因此您可以根据需要在项目中添加任意数量的 Kotlin 代码。
- 结构化并发:Kotlin 协程让异步代码像阻塞代码一样易于使用。协程可大幅简化后台任务管理,例如网络调用、本地数据访问等任务的管理。
- 空判断、方法/属性扩展、属性委托、高阶函数、DSL语法糖、协程、默认参数等等
- 虽然谷歌承诺永远不会放弃对Java的支持,但是新的compose必须要使用kotlin,虽然Java也可以实现,但是不容易实现,而不容易实现往往就意味着“无法实现”
- 真香!
使用Kotlin Script+buildSrc编写构建脚本
目前我们使用Gradle构建语言是Groovy,但是Gradle实际上是支持Kotlin来编写Gradle构建脚本的,常见的构建脚本是.gradle结尾,而Koltin语法编写的脚本则是.gradle.kts,使用kotlin构建脚本的好处是可以有代码提示,和写一些扩展方法。
同时,我们可以使用buildSrc进行版本管理。
使用Kotlin Script
- 方式一(新项目):
使用最新的Canary版本Android studio,新建项目的时候勾选Use Kotlin script(.kts) for Gradle build files
- 方式二:
将项目中所有.gradle文件改为.gradle.kts,同时修改内容语法为kotlin script语法(具体不展开,可以参照demo),包括settings.gradle,然后在settings.gradle.kts加上
rootProject.buildFileName = “build.gradle.kts”
使用buildSrc
- 新建一个Android library,名字必须为buildSrc,创建之后会报这个名字的module已经存在,因为这个名字是保留名字,所以去setting.gradle中删掉include
buildSrc那一行即可。
- buildSrc目录文件目录如下
- buildSrc 中 build.gradle.kts内容
plugins {
kotlin-dsl
}
repositories {
google()
mavenCentral()
}
- 然后就可以像下面这样引用
Android Jetpack
Jetpack 是一个由多个库组成的套件,可帮助开发者遵循最佳做法,减少样板代码并编写可在各种 Android 版本和设备中一致运行的代码,让开发者精力集中编写重要的代码
导航 navigation
navigation是一个框架,用于在 Android 应用中的“目标位置”之间导航,Android Jetpack的导航组件可帮助您实现导航,无论是简单的按钮点击,还是应用栏和抽屉式导航栏等更为复杂的模式,该组件均可应对。导航组件还通过遵循一套既定原则来确保一致且可预测的用户体验。导航组件由以下三个关键部分组成:
- 导航图:在一个集中位置包含所有导航相关信息的 XML 资源。这包括应用内所有单个内容区域(称为目标)以及用户可以通过应用获取的可能路径。
- NavHost:显示导航图中目标的空白容器。导航组件包含一个默认 NavHost 实现 (NavHostFragment),可显示 Fragment 目标。
- NavController:在 NavHost 中管理应用导航的对象。当用户在整个应用中移动时,NavController 会安排 NavHost 中目标内容的交换。
navigation 依赖
- app依赖
implementation “androidx.navigation:navigation-fragment-ktx:navversion"implementation"androidx.navigation:navigation−ui−ktx:nav_version" implementation "androidx.navigation:navigation-ui-ktx:navversion"implementation"androidx.navigation:navigation−ui−ktx:nav_version”
implementation “androidx.navigation:navigation-dynamic-features-fragment:$nav_version”
- 如果要使用Safe Args传递安全参数, 要在 项目build.gradle 文件中包含以下 classpath
buildscript {
repositories {
google()
}
dependencies {
def nav_version = “2.3.4”
classpath “androidx.navigation:navigation-safe-args-gradle-plugin:$nav_version”
}
}
此外还需要在应用模块build.gradle添加
apply plugin: “androidx.navigation.safeargs.kotlin”
NavHost:
- NavigationActivity
class NavigationActivity: AppCompatActivity(R.layout.activity_navigation)
- activity_navigation
<?xml version="1.0" encoding="utf-8"?>
android:name="androidx.navigation.fragment.NavHostFragment"这是依赖包中的fragment,作用是定义整个导航的起始
设置app:defaultNavHost=“true”,就会拦截系统的返回按钮,这时切换fragment会默认进行入栈
navGraph引用定义好的导航文件
导航图
- res/navigation/navigation_demo_navigation.xml
<?xml version="1.0" encoding="utf-8"?>
- 还可以切换到Design模式编辑
NavController
《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》
【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享
- 在fragment1中直接调用以下代码即可从fragment1切换到fragment2,并且传入参数
findNavController().navigate(NavigationFragment1Directions.actionNavigationFragment1ToNavigationFragment22(“测试”))
其他功能
- 全局操作 使用全局操作来创建可由多个目的地共用的通用操作
- 创建深层链接 将用户直接转到应用内特定目的地的链接
- 使用 NavigationUI 更新界面组件 使用顶部应用栏、抽屉式导航栏和底部导航栏来管理导航
- 自定义返回导航
Lifecycle
传统在生命周期方法写逻辑代码的缺点
- 太多管理界面和其他组件的调用,以响应生命周期的当前状态。管理多个组件会在生命周期方法(如 onStart() 和 onStop())中放置大量的代码,这使得它们难以维护
- 无法保证组件会在 Activity 或 Fragment 停止之前启动。在我们需要执行长时间运行的操作(如 onStart() 中的某种配置检查)时尤其如此
Lifecycle的优势
- 生命周期感知型组件可执行操作来响应另一个组件(如 Activity 和 Fragment)的生命周期状态的变化。这些组件有助于您编写出更有条理且往往更精简的代码,您可以将依赖组件的代码从生命周期方法移入组件本身中,此类代码更易于维护。
lifecycle使用
- 定义LifecycleObserver
inner class MyLifecycleObserver(val lifecycle: Lifecycle) : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_START)
fun onStart() {
CoroutineScope(scope).launch {
delay(3000)
if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
LogUtil.e(“开启定位”)
}
}
}
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onStop() {
LogUtil.e(“关闭定位”)
}
}
- 添加LifecycleObserver
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
lifecycle.addObserver(MyLifecycleObserver(lifecycle))
}
ViewModel
- 旨在以注重生命周期的方式存储和管理界面相关的数据。ViewModel 类让数据可在发生屏幕旋转等配置更改后继续留存,负责为界面准备数据。在配置更改期间会自动保留 ViewModel 对象,以便它们存储的数据立即可供下一个 Activity 或 Fragment 实例使用。
ViewModel可以解决什么问题
- 如果系统销毁或重新创建界面控制器,则存储在其中的任何瞬态界面相关数据都会丢失,为了避免我们的程序出现上述这种情况,我们除了使用Activity的savedInstanceState(仅适合可以序列化再反序列化的少量数据)保存数据之外,还可以使用ViewModel来进行处理数据(可保存较大数据)
- 简化资源管理工作,避免内存泄漏风险,Activity 和 Fragment经常需要进行可能需要一些时间才能返回的异步调用(如网络请求),我们需要确保系统在其销毁后清理这些调用以避免潜在的内存泄漏,同时如果配置发生更改重新创建对象的时候可能会发生重复此前已完成的工作,造成资源浪费
- 让 Activity 和 Fragment 专注于界面显示,如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀,ViewModel可以更容易、高效的分离出视图数据操作
- 实现Fragment和Activity/Fragment共享数据
ViewModel使用
- 实现ViewModel,继承ViewModel抽象类即可
class ViewModelViewModel: ViewModel() {
val userList = mutableListOf()
}
- 引用ViewModel
在fragment/activity中使用
val viewModel:ViewModelViewModel by viewModels()
viewModel.userList
在多个fragment中共享的ViewModel,使用by activityViewModels()创建的ViewModel依赖于Activity,在多个Fragment为同一个对象
val shareViewModel:ViewModelViewModel by activityViewModels()
或者使用koin依赖注入,koin使用说明在下文
val viewModel: ViewModelViewModel by viewModel()
val viewModel: ViewModelViewModel by sharedViewModel()
ViewModel生命周期
生命周期感知数据 LiveData
是一种可观察的数据存储器类。与常规的可观察类不同,LiveData 具有生命周期感知能力,意指它遵循其他应用组件(如 Activity、Fragment 或 Service)的生命周期。这种感知能力可确保 LiveData 仅更新处于活跃生命周期状态的应用组件观察者。
LiveData的优势
- 确保界面符合数据状态:LiveData 遵循观察者模式。当底层数据发生变化时,LiveData 会通知 Observer 对象。您可以整合代码以在这些 Observer 对象中更新界面。
- 不会发生内存泄漏:观察者会绑定到 Lifecycle 对象,并在其关联的生命周期遭到销毁后进行自我清理。
- 不会因 Activity 停止而导致崩溃:如果观察者的生命周期处于非活跃状态(如返回栈中的 Activity),则它不会接收任何 LiveData 事件。
- 不再需要手动处理生命周期:界面组件只是观察相关数据,不会停止或恢复观察。LiveData 将自动管理所有这些操作,因为它在观察时可以感知相关的生命周期状态变化。
- 数据始终保持最新状态:如果生命周期变为非活跃状态,它会在再次变为活跃状态时接收最新的数据。
- 适当的配置更改:如果由于配置更改(如设备旋转)而重新创建了 Activity 或 Fragment,它会立即接收最新的可用数据。
- 共享资源:可以使用单例模式扩展 LiveData 对象以封装系统服务,以便在应用中共享它们。
LiveData使用
- 定义
private val normaLiveData1 = MutableLiveData()
- 赋值
normaLiveData1.value=“LiveDataValue”//UI线程
normaLiveData1.postValue(“LiveDataValue”)//非UI主线程
- 观察数据
normaLiveData1.observe(viewLifecycleOwner, Observer {
LogUtil.e(“观察到第一个值发生了变化”)
tv.text = it
})
- 无生命周期感知地观察数据,这种方式需要手动取消观察,否则会发生内存泄漏
val observer=Observer{
LogUtil.e(“观察到第一个值发生了变化”)
}
normaLiveData1.observeForever(observer)
//在合适的生命周期移除观察
normaLiveData1.removeObserver(observer)
- 转换 LiveData
private val transLiveData= Transformations.map(normaLiveData1){
“$it -----转换”
}
- 合并多个 LiveData
private val mediatorLiveData = MediatorLiveData()
mediatorLiveData.addSource(normaLiveData1){
mediatorLiveData.value=“合并后的值:it−−−it---it−−−{normaLiveData2.value}”
}
mediatorLiveData.addSource(normaLiveData2){
mediatorLiveData.value=“合并后的值:normaLiveData1.value−−−{normaLiveData1.value}---normaLiveData1.value−−−it”
}
- LiveData结合room、协程使用(下文)
kotlin数据流 Flow
Flow数据流以协程为基础构建,可提供多个值。从概念上来讲,数据流是可通过异步方式进行计算处理的一组数据序列,有点像RxJava。
Flow的使用
- 创建Flow
val flow = flow {
emit(“value1”)
emit(“value2”)
delay(1000)
emit(“value3”)
}
val flow = flowOf(“value”)
val flow = listOf(1, 2, 3).asFlow()
- 收集Flow----collect()
由于 collect 是挂起函数,因此需要在协程中执行
scope.launch {
flow.collect {
LogUtil.e(it)
}
}
- 转换Flow----map()
flowOf(1, 2, 3).map {
“第$it 个”
}.collect {
LogUtil.e(it)
}
- 过滤Flow----filter()
flowOf(1, 2, 3).filter {
it > 1
}.collect {
LogUtil.e(it)
}
- 合并Flow
zip操作符会把 flow1 中的一个 item 和 flow2 中对应的一个 item 进行合并,如果 flow1 中 item 个数大于 flow2 中 item 个数,合并后新的 flow 的 item 个数 = 较小的 flow 的 item 个数
val flow1 = flowOf(1, 2, 3, 4, 5)
val flow2 = flowOf(“一”, “二”, “三”, “四”, “五”, “六”)
flow1.zip(flow2) { a, b ->
“a−−−a---a−−−b”
}.collect {
LogUtil.e(it)
}
combine合并时,每次从 flow1 发出新的 item ,会将其与 flow2 的最新的 item 合并
val flow1 = flowOf(1, 2, 3, 4, 5).onEach { delay(1000) }
val flow2 = flowOf(“一”, “二”, “三”, “四”, “五”, “六”).onEach { delay(500) }
flow1.combine(flow2) { a, b ->
“a−−−a---a−−−b”
}.collect {
LogUtil.e(it)
}
- 捕获异常----catch()
flow {
emit(1)
emit(1 / 0)
emit(2)
}.catch {
it.printStackTrace()
}.collect {
LogUtil.e(it)
}
- 线程切换----flowOn()
withContext(Dispatchers.IO){
flowOf(1, 2, 3, 4).onEach {
//受到下面最近的flowOn控制-Main
LogUtil.e(“init—KaTeX parse error: Expected 'EOF', got '}' at position 33: …read().name}") }̲.filter { //受到下…{Thread.currentThread().name}”)
it > 1
}.flowOn(Dispatchers.Main).map {
//受到下面最近的flowOn控制-IO
LogUtil.e(“map—Thread.currentThread().name")"第{Thread.currentThread().name}") "第Thread.currentThread().name")"第it”
}.flowOn(Dispatchers.IO).map {
//受到下面最近的flowOn控制-Main
LogUtil.e(“第二次map—Thread.currentThread().name")"{Thread.currentThread().name}") "Thread.currentThread().name")"it 个结果”
}.flowOn(Dispatchers.Main).collect {
//collect要看整个flow处于哪个线程,此处为IO
LogUtil.e(“collect—${Thread.currentThread().name}”)
LogUtil.e(it)
}
}
- 转为liveData----asLiveData()
添加依赖
“androidx.lifecycle:lifecycle-livedata-ktx:${LibraryVersion.LIVEDATA_KTX}”
flowOf(1, 2, 3, 4).asLiveData().observe(viewLifecycleOwner, Observer {
LogUtil.e(it)
})
更多操作符
本地保存 DataStore
DataStore 是一种数据存储解决方案,允许您使用协议缓冲区存储键值对或类型化对象。DataStore 使用 Kotlin 协程和 Flow 以异步、一致的事务方式存储数据。 参考鸿洋的公众号内容
SharedPreferences存在的问题
- 通过 getXXX() 方法获取数据,可能会导致主线程阻塞
- SharedPreference 不能保证类型安全
- SharedPreference 加载的数据会一直留在内存中,浪费内存
- apply() 方法虽然是异步的,可能会发生 ANR,在 8.0 之前和 8.0 之后实现各不相同
- apply() 方法无法获取到操作成功或者失败的结果
DataStore 解决了什么问题
- DataStore 是基于 Flow 实现的,所以保证了在主线程的安全性
- 以事务方式处理更新数据,事务有四大特性(原子性、一致性、 隔离性、持久性)
- 没有 apply() 和 commit() 等等数据持久的方法
- 自动完成 SharedPreferences 迁移到 DataStore,保证数据一致性,不会造成数据损坏
- 可以监听到操作成功或者失败结果
- 另外 Jetpack DataStore 提供了 Proto DataStore 方式,用于存储类的对象(typed objects ),通过 protocol buffers 将对象序列化存储在本地,protocol buffers 现在已经应用的非常广泛,无论是微信还是阿里等等大厂都在使用
DataStore使用
添加依赖
const val DATA_STORE = “1.0.0-alpha05”
const val PROTOBUF = “3.11.0”
“androidx.datastore:datastore-preferences:LibraryVersion.DATASTORE"//protobuf需下面的依赖"androidx.datastore::datastore−core:{LibraryVersion.DATA_STORE}" //protobuf需下面的依赖 "androidx.datastore::datastore-core:LibraryVersion.DATASTORE"//protobuf需下面的依赖"androidx.datastore::datastore−core:{LibraryVersion.DATA_STORE}”
“com.google.protobuf:protobuf-java:${LibraryVersion.PROTOBUF}”
保存键值对
object DataStore {
private const val APP_DATA_STORE_NAME = “APP_DATA_STORE_NAME”
private lateinit var dataStore: DataStore
fun init(context: Context) {
dataStore = context.createDataStore(APP_DATA_STORE_NAME)
}
suspend fun save(key: Preferences.Key, value: T) {
dataStore.edit {
it[key] = value
}
}
suspend fun get(key: Preferences.Key): T? {
val value = dataStore.data.map {
it[key]
}
return value.first()
}
}
保存
CoroutineScope(scope).launch {
DataStore.save(preferencesKey(“key1”), “aa”)
}
读取
CoroutineScope(scope).launch {
val get = DataStore.get(preferencesKey(“key1”))
}
保存protobuf
protobuf相关知识不在这里展开叙述
- 定义.proto文件
syntax = “proto3”;
option java_package = “com.haikun.jetpackapp.home.ui.demo.datastore.bean”;
option java_multiple_files = true;
message MessageEvent {
int32 type = 1;
string message = 2;
}
- 编译文件
编译后得到下图三个文件
- 定义Serializer
object MessageSerializer : Serializer {
override val defaultValue: MessageEvent
get() = MessageEvent.getDefaultInstance()
override fun readFrom(input: InputStream): MessageEvent {
return MessageEvent.parseFrom(input)
}
override fun writeTo(t: MessageEvent, output: OutputStream) {
t.writeTo(output)
}
}
- 保存
val createDataStore = context?.createDataStore(“data”, MessageSerializer)
createDataStore?.updateData {
it.toBuilder().setType(12).setMessage(“消息”).build()
}
- 读取
CoroutineScope(scope).launch {
context?.createDataStore(“data”, MessageSerializer)?.data?.first()?.let {
LogUtil.e("it.type−−−{it.type}---it.type−−−{it.message}")
}
}
声明式UI DataBinding
数据绑定库是一种支持库,借助该库,您可以使用声明性格式(而非程序化地)将布局中的界面组件绑定到应用中的数据源,可以使用 LiveData 对象作为数据绑定来源,自动将数据变化通知给界面 。
声明式UI VS 命令式UI
声明式UI 只需要把界面给「声明」出来,而不需要手动更新,只要声明的数据发生了变化,UI就跟着变化
命令式UI 需要主动让UI更新,比如setText()
DataBinding使用
开启DataBinding
android {
…
dataBinding {
enabled = true
}
}
基本用法
- 在ViewModel中定义数据和方法
class DataBindingViewModel : ViewModel() {
val userName = MutableLiveData()
val clickTimes = MutableLiveData()
val sexCheckId = MutableLiveData()
val love = MutableLiveData()
fun save(){
LogUtil.e("userName.value−−−{userName.value}---userName.value−−−{sex.value}—${love.value}")
}
}
- xml中引用和调用
- Fragment
class DataBindingFragment : Fragment() {
private val mViewModel: DataBindingViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val dataBinding = DataBindingUtil.inflate(
inflater,
R.layout.fragment_data_binding,
container,
false
)
//使用liveData必须要设置lifecycleOwner,否则无法更新数据
dataBinding.lifecycleOwner = viewLifecycleOwner
dataBinding.viewModel = mViewModel
return dataBinding.root
}
}
进阶用法
- BindingMethods 绑定方法名
@BindingMethods(value = [BindingMethod(type = MyButton::class, attribute = “maxTimes”, method = “setMaxTimes”)])
xml中使用
app:maxTimes="@{15}"
- BindingAdapter 提供自定义逻辑
一些属性需要自定义绑定逻辑。例如,android:paddingLeft 特性没有关联的 setter,而是提供了 setPadding(left, top, right, bottom) 方法。使用 BindingAdapter 注释的静态绑定适配器方法支持自定义特性 setter 的调用方式。
object ViewAdapter {
@BindingAdapter(“minTimes”)
@JvmStatic
fun setMinTimes(view: MyButton, minTimes: Int) {
view.setMin(minTimes)
}
}
xml中使用
app:minTimes="@{8}"
- 自定义双向绑定
@InverseBindingAdapter(attribute = “clickTimes”)
@JvmStatic
fun getClickTimes(view: MyButton): Int {
return view.clickTimes
}
@BindingAdapter(“clickTimesAttrChanged”)
@JvmStatic
fun setListener(view: MyButton, listener: InverseBindingListener?) {
view.onTimesChangeListener = {
listener?.onChange()
}
}
xml使用
app:clickTimes="@={viewModel.clickTimes}"
Compose----android声明式UI未来的趋势
- 2019 年中,Google 在 I/O 大会上公布了 Android 最新的 UI 框架:Jetpack Compose。Compose 可以说是 Android 官方有史以来动作最大的一个库了。它在 2019 年中就公布了,但要到今年也就是 2021 年才会正式发布。这两年的时间 Android 团队在干嘛?在开发这个库,在开发 Compose。一个 UI 框架而已,为什么要花两年来打造呢?因为 Compose 并不是像 RecyclerView、ConstraintLayout 这种做了一个或者几个高级的 UI 控件,而是直接抛弃了我们写了 N 年的 View 和 ViewGroup 那一套东西,从上到下撸了一整套全新的 UI 框架。直白点说就是,它的渲染机制、布局机制、触摸算法以及 UI 的具体写法,全都是新的。
- 第一眼看到Compose,第一感觉就是觉得和Flutter的写法惊人的相似,Compose需要系统的学习,而且需要较高的学习成本,不在这里展开叙述
- Compose只支持kotlin,并且目前需要用Canary版本的android studio进行开发
数据库 Room
Room 在 SQLite 上提供了一个抽象层,以便在充分利用 SQLite 的强大功能的同时,能够流畅地访问数据库 Room可以和kotlin协程/flow结合使用 Room可以和LiveData结合使用
Room 包含 3 个主要组件:
- 数据库:包含数据库持有者,并作为应用已保留的持久关系型数据的底层连接的主要接入点。
- Entity:表示数据库中的表。
- DAO:包含用于访问数据库的方法。
添加依赖
implementation “androidx.room:room-runtime:roomversion"kapt"androidx.room:room−compiler:room_version" kapt "androidx.room:room-compiler:roomversion"kapt"androidx.room:room−compiler:room_version”
implementation “androidx.room:room-ktx:$room_version”
基本用法
- 定义Entity
@Entity
data class Car(
@PrimaryKey(autoGenerate = true) val id: Long,
var name: String,
val color: String,
)
@PrimaryKey 主键 类名就是表名,也可以在@Entity(table=)设置表名 可以使用@Ignore忽略某个字段
- 定义Dao
@Dao
interface CarDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun insertCar(car: Car):Long
@Delete
fun deleteCar(car: Car)
@Update
fun updateCar(car: Car)
@Query(“SELECT * From Car”)
fun queryCarList(): MutableList
@Query(“Select * from Car where id=:id”)
fun queryCarById(id: Long): Car?
}
- 定义Database
@Database(entities = [Car::class], version = 1,exportSchema = false)
abstract class DemoDatabase : RoomDatabase() {
abstract fun carDao(): CarDao
}
- 创建Database和获取Dao
private val db: DemoDatabase by lazy {
Room.databaseBuilder(
JetpackApp.getContext(),
DemoDatabase::class.java, “demo-database”
).build()
}
private val carDao: CarDao by lazy {
db.carDao()
}
- 增删改查
carDao.insertCar(car)
carDao.delete(car)
carDao.updateCar(car)
val car = carDao.queryCarById(mUpdateId)
Room并不支持在主线程访问数据库, 除非在Builder调用allowMainThreadQueries()方法, 因为它很可能将UI锁上较长一段时间. 但是, 异步查询–返回LiveData/Flowable实例的查询–则从此规则中免除, 因为它们在需要的时候会在后台线程异步地运行查询.
- 使用Flow流进行响应式查询
只要表中的任何数据发生变化,返回的 Flow 对象就会再次触发查询并重新发出整个结果集。
使用 Flow 的响应式查询有一个重要限制:只要对表中的任何行进行更新(无论该行是否在结果集中),Flow 对象就会重新运行查询。通过将 distinctUntilChanged() 运算符应用于返回的 Flow 对象,可以确保仅在实际查询结果发生更改时通知界面:
@Query(“Select * From Car where id = :id”)
r?
}
- 定义Database
@Database(entities = [Car::class], version = 1,exportSchema = false)
abstract class DemoDatabase : RoomDatabase() {
abstract fun carDao(): CarDao
}
- 创建Database和获取Dao
private val db: DemoDatabase by lazy {
Room.databaseBuilder(
JetpackApp.getContext(),
DemoDatabase::class.java, “demo-database”
).build()
}
private val carDao: CarDao by lazy {
db.carDao()
}
- 增删改查
carDao.insertCar(car)
carDao.delete(car)
carDao.updateCar(car)
val car = carDao.queryCarById(mUpdateId)
Room并不支持在主线程访问数据库, 除非在Builder调用allowMainThreadQueries()方法, 因为它很可能将UI锁上较长一段时间. 但是, 异步查询–返回LiveData/Flowable实例的查询–则从此规则中免除, 因为它们在需要的时候会在后台线程异步地运行查询.
- 使用Flow流进行响应式查询
只要表中的任何数据发生变化,返回的 Flow 对象就会再次触发查询并重新发出整个结果集。
使用 Flow 的响应式查询有一个重要限制:只要对表中的任何行进行更新(无论该行是否在结果集中),Flow 对象就会重新运行查询。通过将 distinctUntilChanged() 运算符应用于返回的 Flow 对象,可以确保仅在实际查询结果发生更改时通知界面:
@Query(“Select * From Car where id = :id”)
Android kotlin Jetpack mvvm 项目,阿里+头条+腾讯大厂Android笔试真题相关推荐
- 从投递到拿到offer,这份Android面试秘籍一文全解,2021年阿里+头条+腾讯大厂Android笔试真题
切勿答非所问,偷换概念.当面试官提出一个你并不是很了解的问题时候,即使冷场也不要答非所问,进行偷换概念.因为面试最讲究的就是真诚二字.你这样做只会加深面试官对你的厌恶. 切勿侃侃而谈.有很多人在面试时 ...
- 2021年“金三银四”来袭!2021年阿里+头条+腾讯大厂Android笔试真题,含答案解析
前言 现在几乎每个it公司都在开发移动产品,我最早知道Android还是在13年成都某学院上大学的时候,从新闻上知道有这么一家公司,创始人安迪·鲁宾很有名,但安卓到底是做什么的,我并没有关注. 到20 ...
- 升职加薪必看!阿里、腾讯大厂Android面试必问知识点系统梳理,深度好文
前言 今年是转折的一年,很多学android开发的小伙伴失业了,虽找到了一份工作,但高不成低不就,下半年金九银十有想法更换一份工作,很多需要大厂面试经验和大厂面试真题的小伙伴,想提前准备刷下题.接下来 ...
- 阿里、腾讯大厂Android面试必问知识点系统梳理,满满干货指导
前言 金三银四,又是一年校招季. 经历过,才深知不易.最近,和作为校招面试官的同事聊了聊,问他们是如何去考察一个学生的,我简单归为以下几点: 聪明.反应快,这点自不必说,聪明意味着学习能力.适应力强, ...
- 划重点!百度、阿里、腾讯大厂Android面试必问知识点系统梳理,啃一半公司随便挑
前言 大厂面试一直是我们程序员小伙伴茶余饭后所津津乐道的话题.能进一线互联网大厂工作,也是每个程序员生涯的梦想,为的不仅仅是大厂的种种福利.工作环境和高薪,更为的是大厂的工作氛围,能加入到大牛的圈子, ...
- Android kotlin jetpack compose 在APP中部署运行ktor服务器
Android kotlin jetpack compose 在APP中部署运行ktor服务器 前言 添加依赖 服务器管理 活动 效果 DEMO 完事 前言 遇到需求,需要在APP中部署一个服务器,局 ...
- Android MVP模式深入实践探索(二),阿里+头条+腾讯等大厂Android笔试题目分享
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); boolean isLogin ...
- [最新] Android 代码规范大全(Android开发速看),2021年最新大厂Android面试笔试题目
| 数据库类 | 功能名 + DBHelper | 新闻数据库:NewsDBHelper | | 自定义的共享基础类 | Base + 基础 | BaseActivity, BaseFragment ...
- 2022最新出炉的互联网大厂软件测试面试真题(阿里、京东、百度、美团、腾讯.......)
先前看过小编文章的小伙伴都知道.小编会时不时发一些互联网大厂(阿里.京东.百度.美团.腾讯.......)的面试真题出来.今天小编一一把它整理了出来.结尾有彩蛋 1.性能测试关注的指标是什么 从外部 ...
最新文章
- VC++ 自定义消息学习总结
- git 多仓库源 配置
- 台达a2_台达自动化控制系统在IC烧录机上的应用
- SQL Server 分区表 处理海量数据
- 工作308:控制change
- mac php命令行模式,phpstorm分别在Mac和Windows下启动命令行,并启用ssh
- SpringBoot 精通系列-SpringBoot如何操作Memcache
- 【node内存泄漏耗尽之解决方法】
- hex2bin附源代码
- (超赞的Chrome翻译插件)沙拉查词-聚合词典划词翻译
- 【交换机在江湖】第十二章 VLAN基础篇
- javashop B2C开源电商系统源代码
- 《阿凡达》《泰坦尼克号》
- CNAS 认证机构认可规范文件清单
- 【微信开发第一章】SpringBoot实现微信公众号创建菜单,同步菜单功能
- 1.国民技术N32G45X例程之-串口打印
- CopyOnWriteArraySet
- nms、softnms、softernms
- 中学物理教学参考杂志社中学物理教学参考编辑部2022第9期目录
- API接口:item_search - 按关键字搜索商品
热门文章
- 物联网平台可以做什么
- Kmeans聚类②——Sklearn数据生成器(make_blobs,make_classification,make_circles,make_moons)
- Mysql数据库刷题1
- 计算机sci多少字,一篇医学sci多少字?
- 通用万能excel导入的Python实现
- ssh登录远程服务器,scp从本地提交数据给远程服务器
- vue 使用 transition 动画实现页面滑入
- 命令行窗口的常用命令
- 椭圆形微分方程的五点差分法、九点紧差分法以及边值问题的解法
- C语言实现地球自转参数更新