最后
说一千道一万,不如自己去行动。要想在移动互联网的下半场是自己占有一席之地,那就得从现在开始,从今天开始,马上严格要求自己,既重视业务实现能力,也重视基础和原理。基础夯实好了,高楼才能够平地而起,稳如泰山。
最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节。
还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。
网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
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 { price: BigDecimal? ->
// Update the UI.
})
}
转换LiveData
LiveData 对象分派给观察者之前对存储的值进行更改,
根据另一个实例的值返回不同的 LiveData 实例
Lifecycle 软件包会提供 Transformations 类,该类包括可应对这些情况的辅助程序方法。
val userLiveData: LiveData = UserLiveData()
val userName: LiveData = Transformations.map(userLiveData) {
user -> “${user.name} ${user.lastName}”
}
与 map() 类似,对存储在 LiveData 对象中的值应用函数,并将结果解封和分派到下游。传递给 switchMap() 的函数必须返回 LiveData 对象,如以下示例中所示:
private fun getUser(id: String): LiveData {
…
}
val userId: LiveData = …
val user = Transformations.switchMap(userId) { id -> getUser(id) }
要实现自定义转换,您可以使用 MediatorLiveData
类
合并多个 LiveData 源
MediatorLiveData 是 LiveData 的子类,允许您合并多个 LiveData 源。只要任何原始的 LiveData 源对象发生更改,就会触发 MediatorLiveData 对象的观察者。
例如,如果界面中有可以从本地数据库或网络更新的 LiveData 对象,则可以向 MediatorLiveData 对象添加以下源:
-
与存储在数据库中的数据关联的 LiveData 对象。
-
与从网络访问的数据关联的 LiveData 对象。
您的 Activity 只需观察 MediatorLiveData 对象即可从这两个源接收更新。
示例
-
Sunflower,这是一个演示应用,演示架构组件的最佳做法
导航是指支持用户导航、进入和退出应用中不同内容片段的交互。Android Jetpack 的导航组件可帮助您实现导航,无论是简单的按钮点击,还是应用栏和抽屉式导航栏等更为复杂的模式,该组件均可应对。导航组件还通过遵循一套既定原则来确保一致且可预测的用户体验。
分页库可帮助您一次加载和显示一小块数据。按需载入部分数据会减少网络带宽和系统资源的使用量。
使用 PagedList 对象的 LiveData 存储器加载和显示数据:
class ConcertViewModel(concertDao: ConcertDao) : ViewModel() {
val concertList: LiveData<PagedList> =
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
对象中,请参阅有关如何加载分页数据的指南。
示例
界面:
PagedList
类使用 PagedListAdapter
将项加载到 RecyclerView
。这些类共同作用,在内容加载时抓取和显示内容,预取不在视线范围内的内容以及针对内容更改添加动画。
要了解详情,请参阅有关如何显示分页列表的指南。
支持不同的数据架构
获取数据的3种方式:网络,本地数据库,网络+本地数据库
网络(Retrofit):
注意:由于不同的应用处理和显示错误界面的方式不同,因此分页库的 DataSource
对象不提供任何错误处理。如果发生错误,请遵循结果回调,并在稍后重试请求。有关此行为的示例,请参阅 PagingWithNetwork 示例。
本地数据库
设置您的 RecyclerView
以观察本地存储空间,最好使用 Room 持久性库。这样,无论您何时在应用数据库中插入或修改数据,这些更改都会自动反映在显示此数据的 RecyclerView
中。
网络和数据库
在开始观察数据库之后,您可以使用 PagedList.BoundaryCallback
监听数据库中的数据何时耗尽。然后,您可以从网络中获取更多项目并将它们插入到数据库中。如果界面正在观察数据库,则您只需执行此操作即可。
处理网络错误
由于服务器不稳定或者网络异常,如果数据刷新步骤不起作用,您可以提供“重试”按钮供用户选择。如果在数据分页步骤中发生错误,则最好自动重新尝试分页请求。
更新现有应用
1.在应用中将 List
对象替换成 PagedList
对象,后者不需要对应用界面结构或数据更新逻辑进行任何更改。
2.使用 CursorAdapter
3.使用 AsyncListUtil 异步加载内容
数据库示例
使用 LiveData 观察分页数据
@Dao
interface 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> =
concertDao.concertsByDate().toLiveData(pageSize = 50)
}
class ConcertActivity : AppCompatActivity() {
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val viewModel = ViewModelProviders.of(this)
.get()
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 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> =
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()
recyclerView.setAdapter(adapter)
}
override fun onStart() {
super.onStart()
disposable.add(viewModel.concertList
.subscribe(adapter::submitList)))
}
override fun onStop() {
super.onStop()
disposable.clear()
}
}
示例
Room 持久性库在 SQLite 的基础上提供了一个抽象层,让用户能够在充分利用 SQLite 的强大功能的同时,获享更强健的数据库访问机制。
Room 包含 3 个主要组件:
- 数据库:包含数据库持有者,并作为应用已保留的持久关系型数据的底层连接的主要接入点。
使用 @Database
注释的类应满足以下条件:
-
是扩展
RoomDatabase
的抽象类。 -
在注释中添加与数据库关联的实体列表。
-
包含具有 0 个参数且返回使用
@Dao
注释的类的抽象方法。
在运行时,您可以通过调用 Room.databaseBuilder()
或 Room.inMemoryDatabaseBuilder()
获取 Database
的实例。
应用使用 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?
)
UserDao
@Dao
interface UserDao {
@Query(“SELECT * FROM user”)
fun getAll(): List
@Query(“SELECT * FROM user WHERE uid IN (:userIds)”)
fun loadAllByIds(userIds: IntArray): List
@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)
}
AppDatabase
@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 开发的最佳做法。
博客
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> by lazy {
MutableLiveData().also {
loadUsers()
}
}
fun getUsers(): LiveData<List> {
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>{ 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()
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 ->
// 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 进而使用修订后的数据更新界面。
将协程与 ViewModel 一起使用
ViewModel
支持 Kotlin 协程。如需了解详情,请参阅将 Kotlin 协程与 Android 架构组件一起使用。
示例
如需更多与协程相关的信息,请参阅以下链接:
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()
.build()
WorkRequest
中还可以包含其他信息,例如任务在运行时应遵循的约束、工作输入、延迟,以及重试工作的退避时间政策。关于这些选项,在定义工作指南中有更详细的说明。
在本指南中,您将了解如何自定义工作请求来处理常见用例:
-
处理网络可用性等任务约束
-
保证任务执行的延迟时间最短
-
处理任务重试和退避
-
处理任务输入和输出
-
使用标记对任务进行分组
1、工作约束:
您可以向工作添加 Constraints,以指明工作何时可以运行:
// Create a Constraints object that defines when the task should run
val constraints = Constraints.Builder()
.setRequiresDeviceIdle(true)
.setRequiresCharging(true)
.build()
// …then create a OneTimeWorkRequest that uses those constraints
val compressionWork = OneTimeWorkRequestBuilder()
.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()
.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()
.setConstraints(constraints)
.addTag(“cleanup”)
.build()
将您的任务提交给系统
定义 WorkRequest
之后,您现在可以通过 WorkManager
使用 enqueue()
方法来调度它。
WorkManager.getInstance(myContext).enqueue(uploadWorkRequest)
执行 Worker 的确切时间取决于 WorkRequest 中使用的约束以及系统优化。WorkManager 的设计目的就是要在这些限制下提供尽可能好的表现。
方法指南
高级概念
小结
有了这么多优秀的开发工具,可以做出更高质量的Android应用。
当然了,“打铁还需自身硬”,想要写出优秀的代码,最重要的一点还是自身的技术水平,不然用再好的工具也不能发挥出它的全部实力。
在这里我也分享一份大佬自己收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。
总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
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()
.setConstraints(constraints)
.addTag(“cleanup”)
.build()
将您的任务提交给系统
定义 WorkRequest
之后,您现在可以通过 WorkManager
使用 enqueue()
方法来调度它。
WorkManager.getInstance(myContext).enqueue(uploadWorkRequest)
执行 Worker 的确切时间取决于 WorkRequest 中使用的约束以及系统优化。WorkManager 的设计目的就是要在这些限制下提供尽可能好的表现。
方法指南
高级概念
小结
有了这么多优秀的开发工具,可以做出更高质量的Android应用。
当然了,“打铁还需自身硬”,想要写出优秀的代码,最重要的一点还是自身的技术水平,不然用再好的工具也不能发挥出它的全部实力。
在这里我也分享一份大佬自己收录整理的Android学习PDF+架构视频+面试文档+源码笔记,还有高级架构技术进阶脑图、Android开发面试专题资料,高级进阶架构资料这些都是我闲暇还会反复翻阅的精品资料。在脑图中,每个知识点专题都配有相对应的实战项目,可以有效的帮助大家掌握知识点。
总之也是在这里帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!