Jetpack
Jetpack 是一個豐富的組件庫,它的組件庫按類別分為 4 類,分別是架構(gòu)(Architecture)、界面(UI)、行為(behavior)和基礎(chǔ)(foundation)。每個組件都可以單獨使用,也可以配合在一起使用。每個組件都給用戶提供了一個標(biāo)準(zhǔn),能夠幫助開發(fā)者遵循最佳做法,減少樣板代碼并編寫可在各種 Android 版本和設(shè)備中一致運行的代碼,讓開發(fā)者能夠集中精力編寫重要的業(yè)務(wù)代碼。

Jetpack 是各種組件庫的統(tǒng)稱,AndroidX 是這些組件的統(tǒng)一包名。
AndroidX 對原始 Android Support Library 進(jìn)行了重大改進(jìn),后者現(xiàn)在已不再維護(hù)。androidx 軟件包完全取代了 support 包,不僅提供同等的功能,而且提供了新的庫。Jetpack 組件中也是完全使用 androidx 開頭的包名。
與 Support Library 一樣,androidx 命名空間中的庫與 Android 平臺分開提供,并向后兼容各個 Android 版本。
LiveData
LiveData是一種可觀察的數(shù)據(jù)存儲器類。與常規(guī)的可觀察類不同,LiveData 具有生命周期感知能力,意指它遵循其他應(yīng)用組件(如 Activity、Fragment 或 Service)的生命周期。這種感知能力可確保 LiveData 僅更新處于活躍生命周期狀態(tài)的應(yīng)用組件觀察者。
LiveData的優(yōu)點有很多,如不會造成內(nèi)存泄漏,另外還有一個最重要的優(yōu)勢是數(shù)據(jù)驅(qū)動UI,也是MVVM模型的核心設(shè)計,所以LiveData通常會配合ViewModel來使用,ViewModel負(fù)責(zé)觸發(fā)數(shù)據(jù)的更新,更新會通知到LiveData,然后LiveData再通知活躍狀態(tài)的觀察者。此外還有與頁面的生命周期綁定,數(shù)據(jù)自動更新等等。
創(chuàng)建 LiveData 對象
在ViewModel中創(chuàng)建LiveData數(shù)據(jù)對象
private val userList: MutableLiveData<List<UserInfo>> by lazy {
MutableLiveData<List<UserInfo>>()
}
fun getUserListLiveData() = userList
觀察 LiveData 對象
在UI中初始化承載userList對象的ViewModel。
private val mainViewModel: MainViewModel
mainViewModel.getUserListLiveData().observe(this, Observer {
text.text = it.toString()
})
更新 LiveData 對象
直接在獲取數(shù)據(jù)之后調(diào)用setValue或者postValue即可,一般情況下在ViewModel中進(jìn)行數(shù)據(jù)的請求和和LiveData的更新。
userList.value = it
另外LiveData還可以和Room、協(xié)程以及與Flow流進(jìn)行相互轉(zhuǎn)化。
https://developer.android.google.cn/topic/libraries/architecture/livedata
ViewModel
ViewModel類是被設(shè)計用來以可感知生命周期的方式存儲和管理 UI 相關(guān)數(shù)據(jù),ViewModel中數(shù)據(jù)會一直存活即使 activity configuration發(fā)生變化,比如橫豎屏切換的時候。ViewModel是Google的MVVM中的重要一環(huán)VM。
ViewModel的定義
ViewModel需要集成系統(tǒng)API提供的ViewModel類,然后就是如何操作LiveData了。
class MainViewModel : ViewModel() {
private val userList = MutableLiveData<List<UserInfo>>()
@ExperimentalCoroutinesApi
fun getAllUser() {
GlobalScope.launch(Dispatchers.Main) {
getAllUserFlow()
.flowOn(Dispatchers.IO)
.onStart {
Log.e("MainActivity", "getAllUser onStart")
}
.onCompletion {
Log.e("MainActivity", "getAllUser onCompletion")
}
.onEach {
userList.value = it
}
.catch {
Log.e("MainActivity", "getAllUser catch it=${it.message}")
}
.collect()
}
}
}
ViewModel的使用
在UI(Activity或者Fragment)中實例化ViewModel,最基本的方式:
val viewModel = ViewModelProvider(this).get(MainViewModel::class.java)
還可以依賴activity-ktx,它利用kotlin的屬性委托property delegate進(jìn)行初始化:
implementation "androidx.activity:activity-ktx:1.2.2"
val viewModel: MainViewModel by viewModels()
這是在Activity中使用ViewModel,若在Fragment中使用可以依賴fragment-ktx:
implementation "androidx.fragment:fragment-ktx:1.3.2"
val viewModel: MainViewModel by activityViewModels()
另外還有一種,使用Koin一款依賴注入框架進(jìn)行ViewModel初始化:
val viewModel: MainViewModel by inject()
ViewModel的生命周期
ViewModel對象存在的時間范圍是獲取 ViewModel ViewModelProvider 的 Lifecycle。ViewModel將一直留在內(nèi)存中,直到限定其存在時間范圍的 Lifecycle 永久消失:對于 Activity,是在 Activity 完成時;而對于 Fragment,是在 Fragment 分離時。

如何判斷ViewModel的生命周期與哪個UI綁定呢,看ViewModelProvider的參數(shù)。
在 Fragment 之間共享數(shù)據(jù)
傳統(tǒng)的Fragment共享數(shù)據(jù)是比較困難的事情,首先Fragment之間是隔離的,不能通信。其次如果使用他們的Activity則有一個類型強轉(zhuǎn)的問題,F(xiàn)ragment的設(shè)計初衷就不是為單一Activity服務(wù)的,也不可取。最后一般都得借助另外一個類來保存多個Fragment都要使用的數(shù)據(jù),這樣做會讓整體架構(gòu)設(shè)計變得很糟糕?,F(xiàn)在使用ViewModel讓此事變得非常輕松合理。就是每個Fragment都可以直接訂閱ViewModel的observe,因為ViewModel的數(shù)據(jù)驅(qū)動UI的本質(zhì)還是觀察者模式,每個Fragment都可以持有一個觀察者。
https://developer.android.google.cn/topic/libraries/architecture/viewmodel
Room
Room 在 SQLite 上提供了一個抽象層,以便在充分利用 SQLite 的強大功能的同時,能夠流暢地訪問數(shù)據(jù)庫。
如需在應(yīng)用中使用 Room,請將以下依賴項添加到應(yīng)用的 build.gradle 文件。
implementation 'androidx.room:room-runtime:2.2.6'
kapt 'androidx.room:room-compiler:2.2.6'
implementation 'androidx.room:room-ktx:2.2.6'
testImplementation 'androidx.room:room-testing:2.2.6'
基本使用
Room 包含 3 個主要組件:Database、Entity、Dao。
Database:包含數(shù)據(jù)庫持有者,并作為應(yīng)用已保留的持久關(guān)系型數(shù)據(jù)的底層連接的主要接入點。包含:@Database、繼承擴展 RoomDatabase 的抽象類、在注釋中添加與數(shù)據(jù)庫關(guān)聯(lián)的實體列表、包含具有 0 個參數(shù)且返回使用@Dao注釋的類的抽象方法。
@Database(entities = [User::class], version = 1)
abstract class AppDatabase : RoomDatabase() {
abstract fun userDao(): UserDao
}
Entity:
@Entity(tableName="user")
data class User(
@PrimaryKey
val userId: UserId,
@ColumnInfo(name = "name")
val name: Name,
@ColumnInfo(name = "age")
val age: Age
)
typealias UserId = Int
typealias Name = String
typealias Age = Int
Dao:
@Dao
interface UserDao {
@Query("SELECT * FROM user")
fun getAllUser(): Flow<List<User>>
@Query("SELECT * FROM user WHERE userId = :id")
fun findUserById(id: UserId): Flow<User>
@Insert
fun insertUser(user: User)
@Delete
fun deleteUser(user: User)
}
初始化Database:
Room.databaseBuilder(
applicationContext,
AppDatabase::class.java, "database-name"
)
.addCallback(AppDatabaseCallback())
.addMigrations(AppDatabaseMigration())
.build()
其中的AppDatabaseCallback是管理Database的初始化每個階段的回調(diào)
class AppDatabaseCallback : RoomDatabase.Callback() {
/**
* Called when the database is created for the first time. This is called after all the
* tables are created.
*
* @param db The database.
*/
override fun onCreate(db: SupportSQLiteDatabase) {
super.onCreate(db)
}
/**
* Called when the database has been opened.
*
* @param db The database.
*/
override fun onOpen(db: SupportSQLiteDatabase) {
super.onOpen(db)
}
/**
* Called after the database was destructively migrated
*
* @param db The database.
*/
override fun onDestructiveMigration(db: SupportSQLiteDatabase) {
super.onDestructiveMigration(db)
}
}
其中AppDatabaseMigration是升級庫升級管理器
class AppDatabaseMigration : Migration(1, 2) {
/**
* Should run the necessary migrations.
*
*
* This class cannot access any generated Dao in this method.
*
*
* This method is already called inside a transaction and that transaction might actually be a
* composite transaction of all necessary `Migration`s.
*
* @param database The database instance
*/
override fun migrate(database: SupportSQLiteDatabase) {
TODO("Not yet implemented")
}
}
現(xiàn)在看起來Room并沒有特別的優(yōu)勢能讓我們?nèi)滩蛔●R上把代碼從SQLite切換過來,但是本地數(shù)據(jù)庫的訪問畢竟訪問的是磁盤,所以最好的寫法一定是異步訪問的,如果用Flow和協(xié)程配合把Room包裝起來也會讓代碼閱讀起來非常的流暢,并且Room和Flow流配合也是數(shù)據(jù)響應(yīng)的。
https://developer.android.google.cn/training/data-storage/room
Flow
Flow其實和RxJava解決的事情是一樣的。鏈?zhǔn)秸{(diào)用,完美解決回調(diào)函數(shù)的使用出現(xiàn)的無限代碼縮進(jìn);異步,和協(xié)程配合可以解決異步調(diào)用;Flow可以其他技術(shù)兼容,比方說Room、ViewModel并且可以與LiveData相互轉(zhuǎn)化,就是說Flow可以串起來整個架構(gòu)的數(shù)據(jù)流。
創(chuàng)建數(shù)據(jù)流
1.flow
flow {
emit(1)
}
2.flowof 其中也是調(diào)用了flow()去初始化一個Flow
flowOf(1)
修改數(shù)據(jù)流
修改數(shù)據(jù)流的方法有很多,基本上RxJava有的基本上都可以找到對應(yīng)的方法。這里只說幾個重要的:
flowOf(1)
.flowOn(Dispatchers.IO)
.onStart {
Log.e("MainActivity", "onStart")
}
.onCompletion {
Log.e("MainActivity", "onCompletion")
}
.onEach {
liveData.value = it
}
.catch {
Log.e("MainActivity", "catch it=$it")
}
flowOn()作用是指定被觀察者在哪類線程中執(zhí)行,很像RxJava,接受一個Dispatchers參數(shù),Dispatchers.IO IO線程,一般處理網(wǎng)絡(luò)請求;Dispatchers.Main 主線程,一般處理UI展示;Dispatchers.Default 默認(rèn)線程由系統(tǒng)分配,主要處理CPU密集型操作,比方說一個龐大的遍歷。flowOn()和RxJava的observeOn()一樣都是作用于調(diào)用鏈它以上的部分。但是僅僅這樣寫還不會實現(xiàn)異步的目的,F(xiàn)low必須配合協(xié)程完成異步請求。
onStart()是在Flow流成功執(zhí)行之前最先觸發(fā)的方法,這里可以做一些準(zhǔn)備工作。
onCompletion()是在Flow流成功執(zhí)行完成之后最后一個方法,表示完成。都可以在RxJava中找到影子。
onEach()這個方法的傳入的高階函數(shù)有個參數(shù)傳回表示Flow最后的對象,就是整個Flow流的目的。相當(dāng)于RxJava方法的onNext()。
catch()是調(diào)用鏈拋出異常之后會觸發(fā)此方法的執(zhí)行,參數(shù)是Exception和RxJava一樣,它的作用域也是在調(diào)用鏈它以上的部分,所以一般寫在整個流的最后,確保異常不會外流導(dǎo)致崩潰。
此外還有很多二級方法可供使用:http://www.itdecent.cn/p/0d0ee5fd4931
Flow轉(zhuǎn)化LiveData
最簡單的使用
flowOf(1).asLiveData()
https://developer.android.google.cn/kotlin/flow
協(xié)程
上面說了Flow流,但是單獨Flow流是沒法進(jìn)行工作的,它只是一種設(shè)計結(jié)構(gòu),這里說說如果配合協(xié)程進(jìn)行異步任務(wù)開發(fā)。
協(xié)程是一種并發(fā)設(shè)計模式,您可以在 Android 平臺上使用它來簡化異步執(zhí)行的代碼。
特點
輕量:您可以在單個線程上運行多個協(xié)程,因為協(xié)程支持掛起suspend,不會使正在運行協(xié)程的線程阻塞。掛起比阻塞節(jié)省內(nèi)存,且支持多個并行操作。
內(nèi)存泄漏更少:使用結(jié)構(gòu)化并發(fā)機制在一個作用域內(nèi)執(zhí)行多項操作。
內(nèi)置取消支持:取消操作會自動在運行中的整個協(xié)程層次結(jié)構(gòu)內(nèi)傳播。
Jetpack 集成:許多 Jetpack 庫都包含提供全面協(xié)程支持的擴展。某些庫還提供自己的協(xié)程作用域,可供您用于結(jié)構(gòu)化并發(fā)。
如需在 Android 項目中使用協(xié)程,請將以下依賴項添加到應(yīng)用的 build.gradle 文件中:
dependencies {
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9'
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1"
}
第一個依賴的協(xié)程自身的功能包,第二個依賴提供了viewModelScope擴展屬性可以直接調(diào)用launch(默認(rèn)參數(shù)為Dispatchers.Main)
掛起函數(shù)
被suspend關(guān)鍵字修飾的函數(shù)為可掛起函數(shù)
private suspend fun getAllUser()
掛起函數(shù)可以調(diào)用普通函數(shù),普通函數(shù)不可以直接調(diào)用掛起函數(shù),掛起函數(shù)只能在協(xié)程中或其他掛起函數(shù)中調(diào)用。掛起函數(shù)就是將函數(shù)的執(zhí)行暫停,然后省下來資源去做別的事情,掛起函數(shù)掛起協(xié)程時,不會阻塞協(xié)程所在的線程。掛起函數(shù)是協(xié)程實現(xiàn)異步任務(wù)的主要手段。
協(xié)程的創(chuàng)建
GlobalScope.launch(Dispatchers.Main)
launch()是CoroutineScope的擴展函數(shù),而GlobalScope又繼承CoroutineScope,核心的東西都在CoroutineScope中,CoroutineScope可以看作協(xié)程自身,Dispatchers.Main指定協(xié)程執(zhí)行在主線程中但不會阻塞線程。launch()方法有一個返回值Job,類似于RxJava的Disposable,可以用全局變量持有,它可以取消并且有簡單生命周期。
runBlocking {}:是創(chuàng)建一個新的協(xié)程同時阻塞當(dāng)前線程,直到協(xié)程結(jié)束。這個不應(yīng)該在協(xié)程中使用,主要是為main函數(shù)和測試設(shè)計的。
withContext {}:不會創(chuàng)建新的協(xié)程,在指定協(xié)程上運行掛起代碼塊,并掛起該協(xié)程直至代碼塊運行完成。
async {}:可以實現(xiàn)與 launch()一樣的效果,在后臺創(chuàng)建一個新協(xié)程,唯一的區(qū)別是它有返回值,因為CoroutineScope.async {}返回的是 Deferred 類型。Deferred是Job的子類,區(qū)別在于完成之后又返回值可以根據(jù)返回值做一些處理。
協(xié)程與Flow配合實現(xiàn)異步
GlobalScope.launch(Dispatchers.Main) {
getAllUserFlow()
.flatMapConcat {
deleteUserFlow(it)
}
.flowOn(Dispatchers.IO)
.onStart {
Log.e("MainActivity", "onStart")
}
.onCompletion {
Log.e("MainActivity", "onCompletion")
}
.catch {
Log.e("MainActivity", "catch it=${it.message}")
}
.collect()
}
Dispatchers.Main:表示協(xié)程在主線程上創(chuàng)建。
getAllUserFlow():調(diào)用網(wǎng)絡(luò)服務(wù)接口,返回Flow攜帶數(shù)據(jù)。
.flowOn(Dispatchers.IO):表示被觀察者就是getAllUserFlow()的操作在IO線程上執(zhí)行。
.collect():相當(dāng)于RxJava的subscribe(),不過它可以不攜帶參數(shù),也可以攜帶,建議是用.OnEach()處理返回的數(shù)據(jù),.collect()只做訂閱,因為Flow屬于冷鏈必須訂閱才能激活。因為之前講過.catch()方法只能捕獲到它前面的流操作,所以使用攜帶參數(shù)的.collect(),在處理函數(shù)中若拋出異常是直接導(dǎo)致程序崩潰的。
這樣協(xié)程的基本使用的可以運用到項目中了。https://developer.android.google.cn/kotlin/coroutines
koin
https://zhuanlan.zhihu.com/p/188485918
Retrofit擴展
整體架構(gòu)
