- fun enable() {
- enabled = true
复制代码 //如果 Lifecycle 如今未处于精良的状态,则应避免调用某些回调。
//比方,如果回调在 Activity 状态保存后运行 Fragment 事务,就会引发崩溃,因此我们绝不能调用该回调。
//以是,Lifecycle 类允许其他对象查询当前状态。
- if (lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
- // connect if not connected
- }
- }
- @OnLifecycleEvent(Lifecycle.Event.ON_STOP)
- fun stop() {
- // disconnect if connected
- }
- }
复制代码- [/code] [code]class MyActivity : AppCompatActivity() {
- private lateinit var myLocationListener: MyLocationListener
- override fun onCreate(...) {
- myLocationListener = MyLocationListener(this, lifecycle) { location ->
- // update UI
- }
- Util.checkUserStatus { result ->
- if (result) {
- myLocationListener.enable()
- }
- }
- }
- }
复制代码 class NameViewModel : ViewModel() {
- // Create a LiveData with a String
- val currentName: MutableLiveData<String> by lazy {
- MutableLiveData<String>()
- }
- // Rest of the ViewModel...
- }
复制代码- 注意:请确保将用于更新界面的 LiveData 对象存储在 ViewModel 对象中,而不是将其存储在 Activity 或 Fragment 中,原因如下:
- * 避免 Activity 和 Fragment 过于庞大。现在,这些界面控制器负责显示数据,但不负责存储数据状态。
- * 将 LiveData 实例与特定的 Activity 或 Fragment 实例分离开,并使 对象在配置更改后继续存在。
- ### []( )观察 LiveData 对象
- 一般在 onCreate() 方法是开始观察 LiveData 对象的正确着手点,原因如下:
- * 确保系统不会从 Activity 或 Fragment 的 onResume() 方法进行冗余调用。
- * 确保 Activity 或 Fragment 变为活跃状态后具有可以立即显示的数据。一旦应用组件处于 STARTED 状态,就会从它正在观察的 LiveData 对象接收最新值。只有在设置了要观察的 LiveData 对象时,才会发生这种情况。
复制代码 //在传递 nameObserver 参数的情况下调用 observe() 后,系统会立刻调用 onChanged(),从而提供 mCurrentName 中存储的最新值。 如果 LiveData 对象尚未在 mCurrentName 中设置值,则不会调用 onChanged()。
class NameActivity : AppCompatActivity() {
- private lateinit var model: NameViewModel
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- // Other code to setup the activity...
- // Get the ViewModel.
- model = ViewModelProviders.of(this).get(NameViewModel::class.java)
- // Create the observer which updates the UI.
- val nameObserver = Observer<String> { newName ->
- // Update the UI, in this case, a TextView.
- nameTextView.text = newName
- }
- // Observe the LiveData, passing in this activity as the LifecycleOwner and the observer.
- model.currentName.observe(this, nameObserver)
- }
- }
复制代码- ### []( )更新 LiveData 对象
- LiveData 没有公开可用的方法来更新存储的数据。MutableLiveData 类将公开 setValue(T) 和 postValue(T) 方法,如果您需要修改存储在 LiveData 对象中的值,则必须使用这些方法。
- 通常情况下会在 ViewModel 中使用 MutableLiveData,然后 ViewModel 只会向观察者公开不可变的 LiveData 对象。
- 设置观察者关系后,您可以更新 LiveData 对象的值(如以下示例中所示),这样当用户点按某个按钮时会触发所有观察者:
复制代码 button.setOnClickListener {
- val anotherName = "John Doe"
- model.currentName.setValue(anotherName)
- }
复制代码- **注意**:您必须调用 [`setValue(T)`]( ) 方法以从主线程更新 `LiveData` 对象。如果在 worker 线程中执行代码,则您可以改用 [`postValue(T)`]( ) 方法来更新 `LiveData` 对象。
- ### []( )将LiveData与Room一起使用
- 当数据库更新时,Room 会生成更新 `LiveData` 对象所需的所有代码。在需要时,生成的代码会在后台线程上异步运行查询。此模式有助于使界面中显示的数据与存储在数据库中的数据保持同步。
- ### []( )将协程与 LiveData 一起使用
- [https://developer.android.google.cn/topic/libraries/architecture/coroutines]( )
- ### []( )扩展 LiveData
复制代码- class StockLiveData(symbol: String) : LiveData<BigDecimal>() {
- private val stockManager = StockManager(symbol)
- private val listener = { price: BigDecimal ->
- value = price
- }
复制代码 //LiveData有活跃观察者时
- override fun onActive() {
- stockManager.requestPriceUpdates(listener)
- }
复制代码 //LiveData没有任何活跃观察者时
- override fun onInactive() {
- stockManager.removeUpdates(listener)
- }
- }
复制代码- LiveData 可以在多个 Activity、Fragment 和 Service 之间共享它们。您可以将 LiveData 类实现为单一实例
复制代码 class StockLiveData(symbol: String) : LiveData() {
- private val stockManager: StockManager = StockManager(symbol) private val listener = { price: BigDecimal -> value = price } override fun onActive() {
- stockManager.requestPriceUpdates(listener)
- }
- override fun onInactive() { stockManager.removeUpdates(listener) } companion object { private lateinit var sInstance: StockLiveData @MainThread fun get(symbol: String): StockLiveData { sInstance = if (::sInstance.isInitialized) sInstance else StockLiveData(symbol) return sInstance } }}
复制代码 class MyFragment : Fragment() {
- override fun onActivityCreated(savedInstanceState: Bundle?) {
- StockLiveData.get(symbol).observe(this, Observer<BigDecimal> { price: BigDecimal? ->
- // Update the UI.
- })
- }
复制代码- ### []( )转换LiveData
- LiveData 对象分派给观察者之前对存储的值进行更改,
- 根据另一个实例的值返回不同的 LiveData 实例
- Lifecycle 软件包会提供 Transformations 类,该类包括可应对这些情况的辅助程序方法。
复制代码 val userLiveData: LiveData = UserLiveData()
- val userName: LiveData<String> = Transformations.map(userLiveData) {
- user -> "${user.name} ${user.lastName}"
- }
复制代码- 与 map() 类似,对存储在 LiveData 对象中的值应用函数,并将结果解封和分派到下游。传递给 switchMap() 的函数必须返回 LiveData 对象,如以下示例中所示:
复制代码 private fun getUser(id: String): LiveData {
- ...
- }
- val userId: LiveData<String> = ...
- val user = Transformations.switchMap(userId) { id -> getUser(id) }
复制代码- 要实现自定义转换,您可以使用 [`MediatorLiveData`]( ) 类
- ### []( )合并多个 LiveData 源
- MediatorLiveData 是 LiveData 的子类,允许您合并多个 LiveData 源。只要任何原始的 LiveData 源对象发生更改,就会触发 MediatorLiveData 对象的观察者。
- 例如,如果界面中有可以从本地数据库或网络更新的 LiveData 对象,则可以向 MediatorLiveData 对象添加以下源:
- * 与存储在数据库中的数据关联的 LiveData 对象。
- * 与从网络访问的数据关联的 LiveData 对象。
- 您的 Activity 只需观察 MediatorLiveData 对象即可从这两个源接收更新。
- ### []( )示例
- * [Sunflower]( ),这是一个演示应用,演示架构组件的最佳做法
- * [Android 架构组件基本示例]( )
- []( )[4、navigation - 处理应用内导航所需的一切]( )
- --------------------------------------------------------------------------------------------------------------------------------------------------
- 导航是指支持用户导航、进入和退出应用中不同内容片段的交互。Android Jetpack 的导航组件可帮助您实现导航,无论是简单的按钮点击,还是应用栏和抽屉式导航栏等更为复杂的模式,该组件均可应对。导航组件还通过遵循[一套既定原则]( )来确保一致且可预测的用户体验。
- []( )[5、Paging]( )
- -------------------------------------------------------------------------------------------------------------------------------------------------
- 分页库可帮助您一次加载和显示一小块数据。按需载入部分数据会减少网络带宽和系统资源的使用量。
- 使用 PagedList 对象的 LiveData 存储器加载和显示数据:
复制代码 class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
- val concertList: LiveData<PagedList<Concert>> =
- concertDao.concertsByDate().toLiveData(pageSize = 50)
- }
复制代码- 每个 [`PagedList`]( ) 实例都会从对应的 [`DataSource`]( ) 对象加载应用数据
复制代码- @Dao
- interface ConcertDao {
- // The Int type parameter tells Room to use a PositionalDataSource object.
- @Query("SELECT * FROM concerts ORDER BY date DESC")
- fun concertsByDate(): DataSource.Factory<Int, Concert>
- }
复制代码- 要详细了解如何将数据加载到 `PagedList` 对象中,请参阅有关如何[加载分页数据]( )的指南。
- ### []( )示例
- * [Android 架构组件分页示例]( )
- * [网络分页展示示例]( )
- ### []( )界面:
- [`PagedList`]( ) 类使用 `PagedListAdapter` 将项加载到 [`RecyclerView`]( )。这些类共同作用,在内容加载时抓取和显示内容,预取不在视线范围内的内容以及针对内容更改添加动画。
- 要了解详情,请参阅有关如何[显示分页列表]( )的指南。
- ### []( )支持不同的数据架构
- 获取数据的3种方式:网络,本地数据库,网络+本地数据库
- ##### []( )网络(Retrofit):
- **注意**:由于不同的应用处理和显示错误界面的方式不同,因此分页库的 [`DataSource`]( ) 对象不提供任何错误处理。如果发生错误,请遵循结果回调,并在稍后重试请求。有关此行为的示例,请参阅 [PagingWithNetwork 示例]( )。
- ##### []( )本地数据库
- 设置您的 [`RecyclerView`]( ) 以观察本地存储空间,最好使用 [Room 持久性库]( )。这样,无论您何时在应用数据库中插入或修改数据,这些更改都会自动反映在显示此数据的 `RecyclerView` 中。
- #### []( )网络和数据库
- 在开始观察数据库之后,您可以使用 [`PagedList.BoundaryCallback`]( ) 监听数据库中的数据何时耗尽。然后,您可以从网络中获取更多项目并将它们插入到数据库中。如果界面正在观察数据库,则您只需执行此操作即可。
- ### []( )处理网络错误
- 由于服务器不稳定或者网络异常,如果数据刷新步骤不起作用,您可以提供“重试”按钮供用户选择。如果在数据分页步骤中发生错误,则最好自动重新尝试分页请求。
- ### []( )更新现有应用
- 1.在应用中将 [`List`]( ) 对象替换成 `PagedList` 对象,后者不需要对应用界面结构或数据更新逻辑进行任何更改。
- 2.使用 CursorAdapter
- 3.使用 AsyncListUtil 异步加载内容
- ### []( )数据库示例
- ##### []( )使用 LiveData 观察分页数据
复制代码- @Daointerface ConcertDao { // The Int type parameter tells Room to use a PositionalDataSource // object, with position-based loading under the hood. @Query("SELECT * FROM concerts ORDER BY date DESC") fun concertsByDate(): DataSource.Factory<Int, Concert>}class ConcertViewModel(concertDao: ConcertDao) : ViewModel() { val concertList: LiveData<PagedList<Concert>> =
- concertDao.concertsByDate().toLiveData(pageSize = 50)
- }
- class ConcertActivity : AppCompatActivity() { public override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) val viewModel = ViewModelProviders.of(this) .get<ConcertViewModel>() val recyclerView = findViewById(R.id.concert_list) val adapter = ConcertAdapter() viewModel.livePagedList.observe(this, PagedList(adapter::submitList)) recyclerView.setAdapter(adapter) }}class ConcertAdapter() : PagedListAdapter<Concert, ConcertViewHolder>(DIFF_CALLBACK) { fun onBindViewHolder(holder: ConcertViewHolder, position: Int) { val concert: Concert? = getItem(position) // Note that "concert" is a placeholder if it's null. holder.bindTo(concert) } companion object { private val DIFF_CALLBACK = object : DiffUtil.ItemCallback<Concert>() { // Concert details may have changed if reloaded from the database, // but ID is fixed. override fun areItemsTheSame(oldConcert: Concert, newConcert: Concert) = oldConcert.id == newConcert.id override fun areContentsTheSame(oldConcert: Concert, newConcert: Concert) = oldConcert == newConcert } }}
复制代码- ##### []( )使用 RxJava2 观察分页数据
- 如果您倾向于使用 [RxJava2]( ) 而不是 [`LiveData`]( ),则可以改为创建 `Observable` 或 `Flowable` 对象:
复制代码- class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
- val concertList: Observable<PagedList<Concert>> =
- concertDao.concertsByDate().toObservable(pageSize = 50)
- }
复制代码- 然后,您可以使用以下代码段中的代码来开始和停止观察数据:
复制代码 class ConcertActivity : AppCompatActivity() {
- private val adapter: ConcertAdapter()
- private lateinit var viewModel: ConcertViewModel
- private val disposable = CompositeDisposable()
- public override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- val recyclerView = findViewById(R.id.concert_list)
- viewModel = ViewModelProviders.of(this)
- .get<ConcertViewModel>()
- recyclerView.setAdapter(adapter)
- }
- override fun onStart() {
- super.onStart()
- disposable.add(viewModel.concertList
- .subscribe(adapter::submitList)))
- }
- override fun onStop() {
- super.onStop()
- disposable.clear()
- }
- }
复制代码- ### []( )示例
- * [Android 架构组件分页示例]( )
- * [网络分页展示示例]( )
- []( )[6、Room - 流畅地访问 SQLite 数据库]( )
- ----------------------------------------------------------------------------------------------------------------------------------------------------------------
- [Room]( ) 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。
- Room 包含 3 个主要组件:
- * [**数据库**]( ):包含数据库持有者,并作为应用已保留的持久关系型数据的底层连接的主要接入点。
-
- 使用 [`@Database`]( ) 注释的类应满足以下条件:
-
- * 是扩展 [`RoomDatabase`]( ) 的抽象类。
- * 在注释中添加与数据库关联的实体列表。
- * 包含具有 0 个参数且返回使用 [`@Dao`]( ) 注释的类的抽象方法。
-
- 在运行时,您可以通过调用 [`Room.databaseBuilder()`]( ) 或 [`Room.inMemoryDatabaseBuilder()`]( ) 获取 [`Database`]( ) 的实例。
-
- * [**Entity**]( ):表示数据库中的表。
-
- * [**DAO**]( ):包含用于访问数据库的方法。
-
- 应用使用 Room 数据库来获取与该数据库关联的数据访问对象 (DAO)。然后,应用使用每个 DAO 从数据库中获取实体,然后再将对这些实体的所有更改保存回数据库中。最后,应用使用实体来获取和设置与数据库中的表列相对应的值。
- ### []( )[使用 Room 将数据保存到本地数据库]( )
- #### []( )基本用法
- User
复制代码- @Entity
- data class User(
- @PrimaryKey val uid: Int,
- @ColumnInfo(name = "first_name") val firstName: String?,
- @ColumnInfo(name = "last_name") val lastName: String?
- )
复制代码- @Daointerface
- UserDao
- { @Query("SELECT * FROM user") fun getAll(): List<User> @Query("SELECT * FROM user WHERE uid IN (:userIds)") fun loadAllByIds(userIds: IntArray): List<User> @Query("SELECT * FROM user WHERE first_name LIKE :first AND " + "last_name LIKE :last LIMIT 1") fun findByName(first: String, last: String): User @Insert fun insertAll(vararg users: User) @Delete fun delete(user: User)}
复制代码 @Database(entities = arrayOf(User::class), version = 1)
- abstract class
- AppDatabase
- : RoomDatabase() { abstract fun userDao():
- UserDao
- }
复制代码 val db = Room.databaseBuilder(
- applicationContext,
- AppDatabase
- ::class.java, "database-name" ).build()
复制代码- **注意**:如果您的应用在单个进程中运行,则在实例化 `
- AppDatabase
- ` 对象时应依照单例设计模式。每个 [`RoomDatabase`]( ) 实例的本钱相称高,而您几乎不需要在单个进程中访问多个实例。如果您的应用在多个进程中运行,请在数据库构建器调用中包罗 `enableMultiInstanceInvalidation()`。如许,如果您在每个进程中都有一个 `
- AppDatabase
- ` 实例,就可以在一个进程中使共享数据库文件失效,而且这种失效会自动传播到其他进程中的 `
- AppDatabase
- ` 实例。### []( )示例* [Sunflower]( ),这是一款园艺应用,展示了使用 Android Jetpack 举行 Android 开发的最佳做法。* [Room 迁移示例]( )* Room 和 RxJava 示例 [(Java)]( ) [(Kotlin)]( )### []( )博客* [Android Sunflower 简介]( )* [Room + Time]( )* [从 SQLite 逐步迁移到 Room]( )* [有关 Room 的 7 个专业提示]( )* [了解借助 Room 举行的迁移]( )* [测试 Room 迁移]( )* [Room + RxJava]( )[]( )[7、ViewModel -以注重生命周期的方式管理界面相干的数据]( )----------------------------------------------------------------------------------------------------------------------------------------------------------------------------##### []( )ViewModel 解决的题目:1、某个 Activity 包罗用户列表。因设置更改而重新创建 Activity 后,新 Activity 必须重新提取用户列表 2、界面控制器经常需要异步调用,并确保系统在其烧毁后清算这些调用以避免潜在的内存泄露,而且在因设置更改而重新创建对象的情况下,会造成资源的浪费,由于对象可能需要重新发出已经发出过的调用。 3、如果要求界面控制器也负责从数据库或网络加载数据,那么会使类越发膨胀。##### []( )从界面控制器逻辑中分离出视图数据所有权的做法更易行且更高效。### []( )实现 ViewModel架构组件为界面控制器提供了 ViewModel辅助程序类,该类负责为界面准备数据。 在设置更改期间会自动保留 [`ViewModel`]( ) 对象,以便它们存储的数据立刻可供下一个 Activity 或 Fragment 实例使用。 比方,如果您需要在应用中显示用户列表,请确保将获取和保留该用户列表的责任分配给 [`ViewModel`]( ),而不是 Activity 或 Fragment,如以下示例代码所示:
复制代码 class MyViewModel : ViewModel() {
- private val users: MutableLiveData<List<User>> by lazy {
- MutableLiveData().also {
- loadUsers()
- }
- }
- fun getUsers(): LiveData<List<User>> {
- return users
- }
- private fun loadUsers() {
- // Do an asynchronous operation to fetch users.
- }
- }
复制代码- 然后,您可以从 Activity 访问该列表,如下所示:
复制代码- class MyActivity : AppCompatActivity() {
- override fun onCreate(savedInstanceState: Bundle?) {
- // Create a ViewModel the first time the system calls an activity's onCreate() method.
- // Re-created activities receive the same MyViewModel instance created by the first activity.
- val model = ViewModelProviders.of(this)[MyViewModel::class.java]
- model.getUsers().observe(this, Observer<List<User>>{ users ->
- // update UI
- })
- }
- }
复制代码- 如果重新创建了该 Activity,它接收的 `MyViewModel` 实例与第一个 Activity 创建的实例相同。当所有者 Activity 完成时,框架会调用 [`ViewModel`]( ) 对象的 [`onCleared()`]( ) 方法,以便它可以清理资源。
- **注意**:[`ViewModel`]( ) 绝不能引用视图、[`Lifecycle`]( ) 或可能存储对 Activity 上下文的引用的任何类。
- ### []( )ViewModel 的生命周期
- [`ViewModel`]( ) 对象存在的时间范围是获取 [`ViewModel`]( ) 时传递给 [`ViewModelProvider`]( ) 的 [`Lifecycle`]( )。[`ViewModel`]( ) 将一直留在内存中,直到限定其存在时间范围的 [`Lifecycle`]( ) 永久消失:对于 Activity,是在 Activity 完成时;而对于 Fragment,是在 Fragment 分离时。
- 系统首次调用 Activity 对象的 onCreate()方法时请求 ViewModel,系统可能会在 Activity 的整个生命周期内多次调用 onCreate(),ViewModel存在的时间范围是从您首次请求 ViewMode 直到 Activity 完成并销毁。
- ### []( )在 Fragment 之间共享数据
- AFragment和BFragment数据传递,两个 Fragment 都需要定义接口描述,这两个 Fragment 都必须处理另一个 Fragment 尚未创建或不可见的情况。
- 可以使用 ViewModel对象解决这一常见的难点。这两个 Fragment 可以使用其 Activity 范围共享 ViewModel来处理此类通信,如以下示例代码所示:
复制代码- class SharedViewModel : ViewModel() {
- val selected = MutableLiveData<Item>()
- fun select(item: Item) {
- selected.value = item
- }
- }
- class MasterFragment : Fragment() {
- private lateinit var itemSelector: Selector
- private lateinit var model: SharedViewModel
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- model = activity?.run {
- ViewModelProviders.of(this)[SharedViewModel::class.java]
- } ?: throw Exception("Invalid Activity")
- itemSelector.setOnClickListener { item ->
- // Update the UI
- }
- }
- }
- class DetailFragment : Fragment() {
- private lateinit var model: SharedViewModel
- override fun onCreate(savedInstanceState: Bundle?) {
- super.onCreate(savedInstanceState)
- model = activity?.run {
- ViewModelProviders.of(this)[SharedViewModel::class.java]
- } ?: throw Exception("Invalid Activity")
- model.selected.observe(this, Observer<Item> { item ->
- // Update the UI
- })
- }
- }
复制代码- 请注意,这两个 Fragment 都会检索包含它们的 Activity。这样,当这两个 Fragment 各自获取 [`ViewModelProvider`]( ) 时,它们会收到相同的 `SharedViewModel` 实例(其范围限定为该 Activity)。
- 此方法具有以下优势:
- * Activity 不需要执行任何操作,也不需要对此通信有任何了解。
- * 除了 `SharedViewModel` 约定之外,Fragment 不需要相互了解。如果其中一个 Fragment 消失,另一个 Fragment 将继续照常工作。
- * 每个 Fragment 都有自己的生命周期,而不受另一个 Fragment 的生命周期的影响。如果一个 Fragment 替换另一个 Fragment,界面将继续工作而没有任何问题。
- ### []( )将加载器替换为 ViewModel
- 诸如 `[CursorLoader]( )` 之类的加载器类经常用于使应用界面中的数据与数据库保持同步。您可以将 [`ViewModel`]( ) 与一些其他类一起使用来替换加载器。使用 [`ViewModel`]( ) 可将界面控制器与数据加载操作分离,这意味着类之间的强引用更少
- [`ViewModel`]( ) 与 [Room]( ) 和 [LiveData]( ) 一起使用可替换加载器。[`ViewModel`]( ) 确保数据在设备配置更改后仍然存在。[Room]( ) 在数据库发生更改时通知 [`LiveData`]( ),[LiveData]( ) 进而使用修订后的数据更新界面。
- ![image.png](https://imgconvert.csdnimg.cn/aHR0cHM6Ly91cGxvYWQtaW1hZ2VzLmppYW5zaHUuaW8vdXBsb2FkX2ltYWdlcy8zMTU0ODA4LTJiOWZlOGNmMzVlNzM5YzgucG5n?x-oss-process=image/format,png)
- ### []( )[将协程与 ViewModel 一起使用]( )
- `ViewModel` 支持 Kotlin 协程。如需了解详情,请参阅[将 Kotlin 协程与 Android 架构组件一起使用]( )。
- ### []( )示例
- * [Kotlin 协程与架构组件]( )
- 如需更多与协程相关的信息,请参阅以下链接:
- * [利用 Kotlin 协程提升应用性能]( )
- * [协程概览]( )
- * [在 CoroutineWorker 中进行线程处理]( )
- []( )[8、WorkManager - 管理您的 Android 后台作业]( )
- -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
- 当前稳定版:
- | 2020 年 1 月 22 日 | [2.3.0]( ) |
复制代码 dependencies {
- def work_version = "2.3.0"
- // (Java only)
- implementation "androidx.work:work-runtime:$work_version"
- // Kotlin + coroutines
- implementation "androidx.work:work-runtime-ktx:$work_version"
- // optional - RxJava2 support
- implementation "androidx.work:work-rxjava2:$work_version"
- // optional - GCMNetworkManager support
- implementation "androidx.work:work-gcm:$work_version"
- // optional - Test helpers
- androidTestImplementation "androidx.work:work-testing:$work_version"
- }
复制代码- WorkManager 旨在用于可延迟运行(即不需要立即运行)并且在应用退出或设备重启时必须能够可靠运行的任务。例如:
- * 向后端服务发送日志或分析数据
- * 定期将应用数据与服务器同步
- WorkManager 不适用于应用进程结束时能够安全终止的运行中后台工作,也不适用于需要立即执行的任务。请查看[后台处理指南]( ),了解哪种解决方案符合您的需求。
- ### []( )创建后台任务
- 要创建后台任务,请扩展 Worker 类并替换 doWork() 方法。例如,要创建上传图像的 Worker:
复制代码 class UploadWorker(appContext: Context, workerParams: WorkerParameters) Worker(appContext, workerParams) { - override fun doWork(): Result {
- // Do the work here--in this case, upload the images.
- uploadImages()
- // Indicate whether the task finished successfully with the Result
- return Result.success()
- }
- }
复制代码- 从 `doWork()` 返回的 [`Result`]( ) 会通知 WorkManager 任务是否:
- * 已成功完成:`Result.success()`
- * 已失败:`Result.failure()`
- * 需要稍后重试:`Result.retry()`
- **注意**:`Worker` 是运行工作的最简单方式。要了解 Worker 的更多高级选项,请参阅 WorkManager [线程指南]( )。
- ##### []( )WorkManager 提供了四种不同类型的工作基元:
- * [`Worker`]( ) 是最简单的实现,前面几节已经有所介绍。WorkManager 会在后台线程上自动运行它(您可以将它替换掉)。请参阅[工作器中的线程处理]( ),详细了解 `Worker` 中的线程处理。
-
- * 建议 Kotlin 用户实现 [`CoroutineWorker`]( )。`CoroutineWorker` 针对后台工作公开挂起函数。默认情况下,它们运行默认的 `Dispatcher`,您可以对其进行自定义。请参阅 [CorventineWorker 中的线程处理]( ),详细了解 `CoroutineWorker` 中的线程处理。
-
- * 建议 RxJava2 用户实现 [`RxWorker`]( )。如果您有很多现有异步代码是用 RxJava 建模的,则应使用 RxWirkers。与所有 RxJava2 概念一样,您可以自由选择所需的线程处理策略。请参阅 [RxWorker 中的线程处理]( ),详细了解 `RxWorker` 中的线程处理。
-
- * [`ListenableWorker`]( ) 是 `Worker`、`CoroutineWorker` 和 `RxWorker` 的基类。该类专为需要与基于回调的异步 API(例如 `FusedLocationProviderClient`)进行交互并且不使用 RxJava2 的 Java 开发者而设计。请参阅 [ListenableWorker 中的线程处理]( ),详细了解 `ListenableWorker` 中的线程处理。
-
- ### []( )配置运行任务的方式和时间
- `Worker` 定义工作单元,[`WorkRequest`]( ) 则定义工作的运行方式和时间。任务可以是一次性的,也可以是周期性的。对于一次性 `WorkRequest`,请使用 [`OneTimeWorkRequest`]( ),对于周期性工作,请使用 [`PeriodicWorkRequest`]( )。
复制代码 val uploadWorkRequest = OneTimeWorkRequestBuilder()
- `WorkRequest` 中还可以包含其他信息,例如任务在运行时应遵循的约束、工作输入、延迟,以及重试工作的退避时间政策。关于这些选项,在[定义工作指南]( )中有更详细的说明。
- 在本指南中,您将了解如何自定义工作请求来处理常见用例:
- * 处理网络可用性等任务约束
- * 保证任务执行的延迟时间最短
- * 处理任务重试和退避
- * 处理任务输入和输出
- * 使用标记对任务进行分组
- ##### []( )1、工作约束:
- 您可以向工作添加 Constraints,以指明工作何时可以运行:
复制代码- // Create a Constraints object that defines when the task should runval constraints = Constraints.Builder() .setRequiresDeviceIdle(true) .setRequiresCharging(true) .build()
- // ...then create a OneTimeWorkRequest that uses those constraintsval compressionWork = OneTimeWorkRequestBuilder<CompressWorker>() .setConstraints(constraints) .build()
复制代码- ##### []( )2、初始延迟
- 任务设置为在加入队列后至少经过 10 分钟再运行:
复制代码 val uploadWorkRequest = OneTimeWorkRequestBuilder()
- .setInitialDelay(10, TimeUnit.MINUTES)
- .build()
复制代码- ##### []( )3、重试和退避政策
- 如果您需要让 WorkManager 重新尝试执行您的任务,可以从工作器返回 [`Result.retry()`]( )。
复制代码 val uploadWorkRequest = OneTimeWorkRequestBuilder()
- .setBackoffCriteria(
- BackoffPolicy.LINEAR,
- OneTimeWorkRequest.MIN_BACKOFF_MILLIS,
- TimeUnit.MILLISECONDS)
- .build()
复制代码- ##### []( )4、定义任务的输入/输出
- 输入和输出值以键值对的形式存储在 [`Data`]( ) 对象中。下面的代码展示了如何在 `WorkRequest` 中设置输入数据。
复制代码 // workDataOf (part of KTX) converts a list of pairs to a [Data] object.
- val imageData = workDataOf(Constants.KEY_IMAGE_URI to imageUriString)
- val uploadWorkRequest = OneTimeWorkRequestBuilder<UploadWorker>()
- .setInputData(imageData)
- .build()
复制代码- Worker 类可通过调用 Worker.getInputData() 访问输入参数。
- 类似地,Data 类可用于输出返回值。要返回 Data 对象,请将它包含到 Result 的 Result.success() 或 Result.failure() 中,如下所示。
复制代码- class UploadWorker(appContext: Context, workerParams: WorkerParameters)
- : Worker(appContext, workerParams) {
- override fun doWork(): Result {
- // Get the input
- val imageUriInput = getInputData().getString(Constants.KEY_IMAGE_URI)
- // TODO: validate inputs.
- // Do the work
- val response = uploadFile(imageUriInput)
- // Create the output of the work
- val outputData = workDataOf(Constants.KEY_IMAGE_URL to response.imageUrl)
- // Return the output
- return Result.success(outputData)
- }
- }
复制代码- ##### []( )5、标记工作
- 以下代码展示了如何使用 [`WorkRequest.Builder.addTag(String)`]( ) 向任务添加“cleanup”标记:
复制代码 val cacheCleanupTask =
- OneTimeWorkRequestBuilder<CacheCleanupWorker>()
- .setConstraints(constraints)
- .addTag("cleanup")
- .build()
复制代码- ### []( )将您的任务提交给系统
- 定义 `WorkRequest` 之后,您现在可以通过 [`WorkManager`]( ) 使用 [`enqueue()`]( ) 方法来调度它。
复制代码 WorkManager.getInstance(myContext).enqueue(uploadWorkRequest)
- 执行 Worker 的确切时间取决于 WorkRequest 中使用的约束以及系统优化。WorkManager 的设计目的就是要在这些限制下提供尽可能好的表现。
- ### []( )方法指南
- * [定义您的 WorkRequest]( )
- * [观察工作状态]( )
- * [观察工作器的中间进度]( )
- * [将工作链接在一起]( )
- * [取消和停止工作]( )
- * [处理重复性工作]( )
- * [处理特有的工作]( )
- * [测试工作器]( )
- ### []( )高级概念
- * [自定义和初始化]( )
- * [在 WorkManager 中进行线程处理]( )
- * [在工作器中进行线程处理]( )
- * [在 CoroutineWorker 中进行线程处理]( )
- * [在 RxWorker 中进行线程处理]( )
- * [在 ListenableWorker 中进行线程处理]( )
- ### []( )迁移指南
- * [从 Firebase JobDispatcher 迁移]( )
- * [从 GCMNetworkManager 迁移]( )
- ### []( )示例
- * [WorkManagerSample]( ),一个简单的图像处理应用
复制代码 免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |