当前位置:   article > 正文

国外大厂面试题, 7 个 Android Lifecycle 重要的知识点_android lifecyclescope

android lifecyclescope

习惯性的每天都会打开 medium 看一下技术相关的内容,偶然看到一位大佬分享和 Android Lifecycle 相关的面试题,觉得非常的有价值。

在 Android 开发中 Android Lifecycle 是非常重要的知识点。但是不幸的是,我发现很多新的 Android 开发对 Android Lifecycle 不是很了解,导致在开发中遇到很多奇怪的问题。

分享这些面试题,不仅仅是为了通过面试,更是为了让 Android 开发者基础更加的扎实,防止在开发中遇到很多奇怪的问题。

面试题一:Launch Fragment by Default

问题:

花几秒钟思考一下,下面的代码有什么问题。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        supportFragmentManager
           .beginTransaction()
           .replace(R.id.container, MainFragment())
           .commit()
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

错误的回答

  1. 没有使用 KTX 添加 Fragment
  2. 应该使用 .add 而不是 .replace

正确的回答

如果 Activity 因意外被杀死并被恢复,会再次执行 onCreate() 方法,创建新的 Fragment,因此在栈中会存在 2 个 Fragment。在 Fragment 上的任何操作都可能被执行两次,这将会导致出现奇怪的问题。

为了防止 Activity 因意外被杀死而恢复,导致添加新的 Fragment,所以我们可以使用 stateInstanceState == null 作为判断条件,防止添加新的 Fragment,因此我们可以将上面的代码简单修改一下。

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        if (savedInstanceState == null) {
            supportFragmentManager
                .beginTransaction()
                .replace(R.id.container, MainFragment())
                .commit()
        }
   }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

面试题二:Create Fragment with Constructor

问题:

如果往 Fragment 构造函数中添加参数,花几秒钟思考一下,下面的代码会有什么问题?

supportFragmentManager
    .beginTransaction()
    .replace(R.id.container, MainFragment())
    .commit()

class MainFragment(private val repository: Repository): Fragment() {

}    
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

错误的回答

  1. 我们可以使用 .replace(R.id.container, MainFragment(repository)) 方法来代替
  2. 它不会编译和运行

正确的回答

我们不应该直接用带参数的构造函数实例化任何 Fragment(),如果想使用带参数的构造函数实例化 Fragment(),可以使用 FragmentFactory 解决这个问题,这是在 AndriodX Fragment 1.2.0 中新增加的 API,详情可以查看另外一篇文章 Google 建议使用这些 Fragment 的新特性

如果不使用 AndriodX Fragment 库,默认情况下系统是不支持的,虽然上面的代码可以正常编译运行,但是在运行过程当中,因为配置更改,导致在销毁恢复的过程中会崩溃,错误信息如下所示。

Caused by: java.lang.NoSuchMethodException: MainFragment.<init>
  • 1

这是因为系统需要在某些情况下重新初始化它,比如配置更改,例如设备被旋转时,导致 Fragment 被销毁,如果没有默认空的构造函数,系统不知道如何重新初始化 Fragment 实例。

因此,我们总是需要确保实例化 Fragment 的时候有一个空的构造函数。

面试题三:Instantiate ViewModel Directly

ViewModel 是 Jetpack 架构组件成员之一,花几秒钟思考一下,下面的代码会有什么问题?

class MainActivity: AppCompatActivity() {
    private val viewModel = MainViewModel()
}

class  MainViewModel(): ViewModel {

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

错误回答

  1. 没有什么问题,可以正常编译运行
  2. 我们还需要向它注入一些依赖项,例如 MainViewModel (repository)

正确回答

我们不应该直接实例化 ViewModelViewModel 是 Jetpack 架构组件成员之一,意味着它可以在配置更改时存活,例如设备旋转时,它比 Activity 有更长的生命周期。

如果我们在代码中直接实例化 ViewModel,尽管它可以工作,但它将会变成一个普通的 ViewModel,失去原本拥有的特性。

因此,要实例化 ViewModel,建议使用 ViewModel KTX,代码如下所示。

class MainActivity: AppCompatActivity() {
    private val viewMode:MainViewModel by viewModels()
}
  • 1
  • 2
  • 3
  • by viewModels () 会在重新启动或从已杀死的进程中恢复时,实例化一个新的 ViewModel。如果有配置更改,例如设备被旋转时,它将检查 ViewModel 是否已经创建,而不重新创建它

  • ViewModels() 会根据需要自动注入 SavedInstancestate (例如 Activity 中的 SavedInstanceStateIntent),如果我们有其他依赖是通过 Dagger Hilt 注入,它将与 ViewModel 一起使用下面的参数
@HiltViewModel
class MyViewModel @Inject constructor(
    private val repository: Repository,
    savedStateHandle: SavedStateHandle
) : ViewModel {

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

面试题四:ViewModel as StateRestoration Solution

问题:

Jetpack 架构组件提供的 ViewModel 的作用是什么?

class MainActivity: AppCompatActivity() {
    private val viewMode:MainViewModel by viewModels()
    // Some other Activity Code
}
  • 1
  • 2
  • 3
  • 4

错误回答

ViewModel 是用于状态恢复,例如当 Activity 被杀死并重新启动时,ViewModel 是用来帮助恢复到原始状态。

正确回答

ViewModel 实际上是 google 提供的 Jetpack 架构组件之一,它鼓励 Android 开发者使用 MVVM 设计模式。

它还有其它重要的功能,例如设备旋转时,即使 ActivityFragment 被销毁,它们各自的 ViewModel 仍会保留,Google 在 ViewModel 中提供了一个名为 savedStateHandle 参数,该参数用于保存和恢复数据。

面试题五:LiveData as State Restoration Solution

问题:

Jetpack 架构组件提供的 LiveData 的作用是什么。

// Declaring it
val liveDataA = MutableLiveData<String>()
// Trigger the value change
liveDataA.value = someValue
  • 1
  • 2
  • 3
  • 4

错误回答:

它的存在是为了确保数据在 Activity 的生命周期中存活。当 Activity 在进程销毁返回时,数据将会自动恢复。

正确回答

LiveData 本身不能在进程销毁中存活。它是一种特殊类型的数据,根据观察者(Activity 或 Fragment)的生命周期来控制其发出的值。

ViewModel 在配置变更后仍然存在,所以 ViewModel 内部的 LiveData 也一样。这确保 LiveData 发射值按照下图控制。

然而 LiveData 本身不能在进程销毁中存活,当内存不足时,Activity 被系统杀死,ViewModel 本身也会被销毁。

为了解决这个问题,Google 在 ViewModel 中提供了一个名为 savedStateHandle 参数,该参数用于保存和恢复数据,以便数据在进程销毁后继续存在。

@HiltViewModel
class MyViewModel @Inject constructor(
    private val repository: Repository,
    savedStateHandle: SavedStateHandle
) : ViewModel {
    // Some other ViewModel Code
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

它是一种增强的机制,可以处理 IntentSavedInstanceState,在以前的时候,这些都是由 Activity 单独处理的。

为了确保 Livedata 保存下来,我们可以在 SavedStateHandle 中检查 Livedata 是否已经创建。

val liveData = savedStateHandle.getLiveData<String>(KEY)
  • 1

类似地,这也适用于 stateFlow,它可以在进程销毁中存活下来。

val stateFlow = savedStateHandle.getStateFlow<String>(KEY, 0)
  • 1

因此 LiveData 本身并不是用来恢复数据的。

面试题六:When is The View Destroyed But Not the Instance

问题:

在 Activity 或 Fragment 通常会有一个视图。你能给我提供一个场景,实例的视图被破坏了,但实例(例如 Activity 或 Fragment)还存在。

错误回答

  1. 当配置发生变化时(例如设备旋转)
  2. 当内存不足时,App 在后台运行,进程会杀死 App

正确回答

实际上 Activity 总是与其视图一起被销毁。因此,在 Activity 中没有 onDestroyView () 生命周期方法。

只有在 Fragment 中有 onDestroyView () 生命周期方法。在大多数情况下 Fragment 和它的视图一起被销毁。

但是通过 Fragment transaction 用一个 Fragment 替换另一个 Fragment 时,栈下面的 Fragment 仍然存在,但是它的视图被破坏了。

当一个 Fragment 被另一个 Fragment 替换时,会调用 onDestroyView () 方法,但不会调用 onDestroy ()onDetect () 方法。

正因为这种复杂性,在使用 Fragment 时,会遇到许多奇怪的问题。和 Fragment 相关的问题,将会在后面的文章中分享。

问题七:Lifecycle Aware Coroutine

在 App 中使用协程,如何确保它们的生命周期可感知。

错误回答

  1. 对于普通视图,只需在 ActivityFragment 中使用 lifecycleScope,在 ViewModel 中使用 viewModelScope
  2. 对于组合视图,需要使用 stateFlow 中的 collectAsState() 方法,因为当可组合函数不活动时,它不会收集

正确回答

对于普通视图,即使 lifecycleScope 是可用的,它在 ActivityFragment 的整个生命周期中都处于活动状态。因为有时我们希望某些场景只在 onStart()onResume() 之后处于活动状态。

为此,我们需要在 lifecycleScope 中使用像 repeatOnLifecycle 这样的 API 提供额外的作用域。

lifecycleScope.launch {
    repeatOnLifecycle(Lifecycle.State.STARTED) {
        viewModel.stateFlowValue.collect {
            // Do something
        }
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

它们的变体如下图所示。

对于组合视图 collectAsState() 不会确保在组合函数处于活动状态时安全使用数据,它也不会停止继续发送 StateFlow,这会导致资源浪费。

为了确保只在 ActivityFragment 处于正确的生命周期时,例如在 onStart () 之后发出,我们需要使用 collectAsStateWithLifecycle ()stateFlow 中的 WhileSubscribed (...)

当我们在研究 collectAsStateWithLifecycle() 源码时,发现它也在使用 repeatOnLifecycle(…)

全文到这里就结束了,感谢你的阅读,坚持原创不易,欢迎在看、点赞、分享给身边的小伙伴,我会持续分享原创干货!!!


我开了一个云同步编译工具(SyncKit),主要用于本地写代码,同步到远程设备,在远程设备上进行编译,最后将编译的结果同步到本地,代码已经上传到 Github,欢迎前往仓库 hi-dhl/SyncKit 查看。


最新文章

开源新项目

  • 云同步编译工具(SyncKit),本地写代码,远程编译,欢迎前去查看 SyncKit

  • KtKit 小巧而实用,用 Kotlin 语言编写的工具库,欢迎前去查看 KtKit

  • 最全、最新的 AndroidX Jetpack 相关组件的实战项目以及相关组件原理分析文章,正在逐渐增加 Jetpack 新成员,仓库持续更新,欢迎前去查看 AndroidX-Jetpack-Practice

  • LeetCode / 剑指 offer,包含多种解题思路、时间复杂度、空间复杂度分析,在线阅读

作者:程序员DHL
链接:https://juejin.cn/post/7166053395366117407

最后

如果想要成为架构师或想突破20~30K薪资范畴,那就不要局限在编码,业务,要会选型、扩展,提升编程思维。此外,良好的职业规划也很重要,学习的习惯很重要,但是最重要的还是要能持之以恒,任何不能坚持落实的计划都是空谈。

如果你没有方向,这里给大家分享一套由阿里高级架构师编写的《Android八大模块进阶笔记》,帮大家将杂乱、零散、碎片化的知识进行体系化的整理,让大家系统而高效地掌握Android开发的各个知识点。
在这里插入图片描述
相对于我们平时看的碎片化内容,这份笔记的知识点更系统化,更容易理解和记忆,是严格按照知识体系编排的。

全套视频资料:

一、面试合集
在这里插入图片描述
二、源码解析合集

在这里插入图片描述
三、开源框架合集

在这里插入图片描述
欢迎大家一键三连支持,若需要文中资料,直接点击文末CSDN官方认证微信卡片免费领取↓↓↓

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/你好赵伟/article/detail/267000?site
推荐阅读
相关标签
  

闽ICP备14008679号