根据我们曾经做的调查,开发者们希望 Android 官方可以维护一些实用的组件库和架构实践,以降低中大型应用的开发门槛,这样开发团队就可以集中更多精力在实际业务的优化和改进上。
Jetpack 项目正是为了解决这些问题而诞生的,Jetpack 是一系列助力您更容易打造优秀 Android 应用的工具和组件,这些组件能帮助您遵循最佳实践、免除编写繁复的样板代码并简化复杂任务,从而使您可以专注于最核心的代码逻辑。其中 androidx.* 库与 Framework API 解耦,这能够提供向后兼容的同时,也能更频繁地更新。
Android Jetpack 中的架构组件可帮助您设计稳健、可测试且易维护的应用。从最初发布的管理 Activity 和 Fragment 生命周期的 Lifecycle 库和访问 SQLite 数据库的 Room 库,后来推出了分页 (Paging)、导航 (Navigation) 和管理后台任务的 WorkManager 库。根据 2019 年最新的开发者调查中,70% 以上的专业开发者用过这五个库当中的至少一个库进行应用开发。
这里我们分上下两篇介绍架构组件的最新更新,如果您还没有阅读本文的上篇,请点击这里查看。本篇将会继续为大家介绍分页库、Room 持久性库和 WorkManager。希望大家能在其中发现对自己的应用有帮助的全新功能以及改进:
分页库
Room 持久性库
Room 是一个在 SQLite 上提供抽象层的持久存储库,您可以回顾了解更多 Room 的详细信息。
在 Room 2.1 中,开发者可以通过 Kotlin 语言的 suspend 关键字让 Room 生成正确的协程代码,包括使用后台 dispatcher,这大大降低了开发者处理协程的工作量:
// Room 2.1
@Query("SELECT * FROM song WHERE songId = :songId")
suspend fun getSong(songId: String): Song
@Insert
suspend fun insertSong(song: Song)
@Transaction
suspend fun deleteShortSongs(): List {
val songs = getSongsWithElapsedTimeLessThan(1000)
deleteSongsWithIds(songs.map { it.songId })
return songs
}
database.withTransaction {
val songs = getSongsWithElapsedTimeLessThan(1000)
deleteSongsWithIds(songs.map { it.songId })
return songs
}
全文搜索功能是对 SQLite 的一个扩展,让其创建一个数据表从而更高效地检索数据。
在 Room 2.0 中,一个 Dao 的检索方法看起来可能是这样:
// Room 2.0
@Dao
interface SongDao {
@Query("""
SELECT *
FROM Song
WHERE songName LIKE ‘%’ || :query || ‘%’
OR albumName LIKE ‘%’ || :query || ‘%’
OR artistName LIKE ‘%’ || :query || ‘%
""")
fun searchSongs(query: String): List
}
而在 2.1 中,只需要加入一个 @Fts4 注解,就可以通过 MATCH 语句让一切都轻松很多:
// Room 2.1
@Entity
@Fts4
data class Song(
@PrimaryKey
@ColumnInfo(name = "rowid")
val id: Long,
val url: String,
val songName: String,
val albumName: String,
val artistName: String
)
@Dao
interface SongDao {
@Query("""
SELECT *
FROM Song
WHERE Song MATCH :query
""")
fun searchSongs(query: String): List
}
很像数据表,但又不完全一样。基本上,您可以像检索数据表一样检索数据库视图,但不能在其中插入数据。
在 2.1 中,您可以用 @DatabaseView 注解您的数据类,但这时您不需要创建一个数据表,而是直接将 BigQuery 放在注解部分,让其成为一个能快速检索的视图:
@DatabaseView("""
SELECT
Album.*,
count(song_id) AS num_of_songs,
sum(song_elapsed_time) AS total_time
FROM Album
JOIN AlbumSongRef ON (album_id = ref_album_id)
JOIN Song ON (ref_song_id = song_id)
GROUP BY album_id
""")
data class AlbumItem(
@Embedded
val album: Album,
@ColumnName("num_of_songs")
val numOfSongs: Int,
@ColumnName("total_time")
val totalTime: Long
)
而这个视图 (如上面的 AlbumItem) 可以像其他数据表一样使用:
@Query("""
SELECT * FROM AlbumItem
ORDER BY num_of_songs DESC
""")
fun getAlbumItemsByNumOfSongs(): List
在 Room 2.1 中,您使用的 insert, update, delete 方法能返回 Completable, Maybe 和 Single。而且在 Query 注解的方法里可以使用 Rx 作为返回类型,并处理 update, insert 或者 delete 这样的写入操作:
@Insert
fun addSong(song: Song): Completable
@Update
fun updateSong(song: Song): Single
@Query("""
UPDATE Song SET
lastPlayedTime = :time
WHERE id = :id
""")
fun updateSongPlayTime(Long: time, String: id): Completable
WorkManager
是一个后台进程库,用于处理那些不需要即时处理的任务,而且可以在应用甚至设备重启后依然确保任务正确触发。另外,WorkManager 也支持按条件启动,比如根据网络连接状况的变化启动特定的任务。
按需配置
// WorkManager 2.1.0
class MyApp : Application(), Configuration.Provider {
override fun getWorkManagerConfiguration() : Configuration {
return Configuration.Builder()
// set your options here
.build()
}
}
WorkManager.getInstance().enqueue(...)
测试
// WorkManager 2.1.0
// Create a test worker
val request = OneTimeWorkRequestBuilder.build()
val worker = TestWorkerBuilder
.from(context, request, executor)
.build()
// Test its behavior
val result = worker.doWork()
assertThat(result, `is`(Result.success()))
assertThat(...)
// WorkManager 2.1.0
// Or create a listenable worker
val request = OneTimeWorkRequestBuilder.build()
val listenableWorker = TestListenableWorkerBuilder
.from(context, request)
.build()
// Test its behavior
val result = listenableWorker.startWork().get()
assertThat(result, `is`(Result.success()))
assertThat(...)
WorkManager 的下一步
腾讯视频链接
https://v.qq.com/x/page/g30084zstx6.html
Bilibili 视频链接
https://www.bilibili.com/video/av71179658/
如果对架构组件有疑问或者建议,欢迎在评论区和我们分享。
点击屏末 | 阅读原文 | 进一步了解 Android Jetpack
推荐阅读