2024年Android Jetpack(三) 架构,腾讯数据开发面试

最后

说一千道一万,不如自己去行动。要想在移动互联网的下半场是自己占有一席之地,那就得从现在开始,从今天开始,马上严格要求自己,既重视业务实现能力,也重视基础和原理。基础夯实好了,高楼才能够平地而起,稳如泰山。

最后为了帮助大家深刻理解Android相关知识点的原理以及面试相关知识,这里放上相关的我搜集整理的24套腾讯、字节跳动、阿里、百度2020-2021面试真题解析,我把技术点整理成了视频和PDF(实际上比预期多花了不少精力),包知识脉络 + 诸多细节

还有 高级架构技术进阶脑图、Android开发面试专题资料 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

一线互联网面试专题

379页的Android进阶知识大全

379页的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 对象即可从这两个源接收更新。

示例

4、navigation - 处理应用内导航所需的一切


导航是指支持用户导航、进入和退出应用中不同内容片段的交互。Android Jetpack 的导航组件可帮助您实现导航,无论是简单的按钮点击,还是应用栏和抽屉式导航栏等更为复杂的模式,该组件均可应对。导航组件还通过遵循一套既定原则来确保一致且可预测的用户体验。

5、Paging


分页库可帮助您一次加载和显示一小块数据。按需载入部分数据会减少网络带宽和系统资源的使用量。

使用 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,则可以改为创建 ObservableFlowable 对象:

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()

}

}

示例

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?

)

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 实例。

示例

博客

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 时传递给 ViewModelProviderLifecycleViewModel 将一直留在内存中,直到限定其存在时间范围的 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 可将界面控制器与数据加载操作分离,这意味着类之间的强引用更少

ViewModelRoomLiveData 一起使用可替换加载器。ViewModel 确保数据在设备配置更改后仍然存在。Room 在数据库发生更改时通知 LiveDataLiveData 进而使用修订后的数据更新界面。

image.png

将协程与 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 用户实现 CoroutineWorkerCoroutineWorker 针对后台工作公开挂起函数。默认情况下,它们运行默认的 Dispatcher,您可以对其进行自定义。请参阅 CorventineWorker 中的线程处理,详细了解 CoroutineWorker 中的线程处理。

  • 建议 RxJava2 用户实现 RxWorker。如果您有很多现有异步代码是用 RxJava 建模的,则应使用 RxWirkers。与所有 RxJava2 概念一样,您可以自由选择所需的线程处理策略。请参阅 RxWorker 中的线程处理,详细了解 RxWorker 中的线程处理。

  • ListenableWorkerWorkerCoroutineWorkerRxWorker 的基类。该类专为需要与基于回调的异步 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行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值