hi 大家好,我是 DHL。大众号:ByteCode ,共享有用、专心有用、风趣的硬核原创内容,Kotlin、Jetpack、功能优化、体系源码、算法及数据结构、大厂面经。
- 面试题来源: medium.com/mobile-app-…
- 已收录于库房 Technical-Article-Translation
习惯性的每天都会打开 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()
}
}
过错的答复
- 没有运用 KTX 添加
Fragment
- 应该运用
.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()
}
}
}
面试题二:Create Fragment with Constructor
问题:
假如往 Fragment 结构函数中添加参数,花几秒钟考虑一下,下面的代码会有什么问题?
supportFragmentManager
.beginTransaction()
.replace(R.id.container, MainFragment())
.commit()
class MainFragment(private val repository: Repository): Fragment() {
}
过错的答复
- 咱们能够运用
.replace(R.id.container, MainFragment(repository))
办法来代替 - 它不会编译和运转
正确的答复
咱们不应该直接用带参数的结构函数实例化任何 Fragment()
,假如想运用带参数的结构函数实例化 Fragment()
,能够运用 FragmentFactory
解决这个问题,这是在 AndriodX Fragment 1.2.0
中新添加的 API,详情能够检查另外一篇文章 Google 建议运用这些 Fragment 的新特性。
假如不运用 AndriodX Fragment
库,默许情况下体系是不支持的,虽然上面的代码能够正常编译运转,但是在运转进程傍边,由于装备更改,导致在毁掉康复的进程中会溃散,过错信息如下所示。
Caused by: java.lang.NoSuchMethodException: MainFragment.<init>
这是由于体系需求在某些情况下从头初始化它,比如装备更改,例如设备被旋转时,导致 Fragment 被毁掉,假如没有默许空的结构函数,体系不知道怎么从头初始化 Fragment 实例。
因而,咱们总是需求保证实例化 Fragment 的时候有一个空的结构函数。
面试题三:Instantiate ViewModel Directly
ViewModel
是 Jetpack 架构组件成员之一,花几秒钟考虑一下,下面的代码会有什么问题?
class MainActivity: AppCompatActivity() {
private val viewModel = MainViewModel()
}
class MainViewModel(): ViewModel {
}
过错答复
- 没有什么问题,能够正常编译运转
- 咱们还需求向它注入一些依赖项,例如
MainViewModel (repository)
正确答复
咱们不应该直接实例化 ViewModel
。 ViewModel
是 Jetpack 架构组件成员之一,意味着它能够在装备更改时存活,例如设备旋转时,它比 Activity 有更长的生命周期。
假如咱们在代码中直接实例化 ViewModel
,尽管它能够作业,但它将会变成一个一般的 ViewModel
,失去原本拥有的特性。
因而,要实例化 ViewModel
,建议运用 ViewModel KTX
,代码如下所示。
class MainActivity: AppCompatActivity() {
private val viewMode:MainViewModel by viewModels()
}
-
by viewModels ()
会在从头启动或从已杀死的进程中康复时,实例化一个新的ViewModel
。假如有装备更改,例如设备被旋转时,它将检查ViewModel
是否现已创立,而不从头创立它
-
ViewModels()
会根据需求主动注入SavedInstancestate
(例如 Activity 中的SavedInstanceState
和Intent
),假如咱们有其他依赖是通过Dagger Hilt
注入,它将与ViewModel
一同运用下面的参数
@HiltViewModel
class MyViewModel @Inject constructor(
private val repository: Repository,
savedStateHandle: SavedStateHandle
) : ViewModel {
}
面试题四:ViewModel as StateRestoration Solution
问题:
Jetpack 架构组件供给的 ViewModel
的作用是什么?
class MainActivity: AppCompatActivity() {
private val viewMode:MainViewModel by viewModels()
// Some other Activity Code
}
过错答复
ViewModel
是用于状况康复,例如当 Activity
被杀死并从头启动时,ViewModel
是用来协助康复到原始状况。
正确答复
ViewModel
实际上是 google 供给的 Jetpack 架构组件之一,它鼓舞 Android 开发者运用 MVVM 设计模式。
它还有其它重要的功能,例如设备旋转时,即便 Activity
和 Fragment
被毁掉,它们各自的 ViewModel
仍会保留,Google 在 ViewModel
中供给了一个名为 savedStateHandle
参数,该参数用于保存和康复数据。
面试题五:LiveData as State Restoration Solution
问题:
Jetpack 架构组件供给的 LiveData
的作用是什么。
// Declaring it
val liveDataA = MutableLiveData<String>()
// Trigger the value change
liveDataA.value = someValue
过错答复:
它的存在是为了保证数据在 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
}
它是一种增强的机制,能够处理 Intent
和 SavedInstanceState
,在曾经的时候,这些都是由 Activity
独自处理的。
为了保证 Livedata
保存下来,咱们能够在 SavedStateHandle
中检查 Livedata
是否现已创立。
val liveData = savedStateHandle.getLiveData<String>(KEY)
类似地,这也适用于 stateFlow
,它能够在进程毁掉中存活下来。
val stateFlow = savedStateHandle.getStateFlow<String>(KEY, 0)
因而 LiveData
自身并不是用来康复数据的。
面试题六:When is The View Destroyed But Not the Instance
问题:
在 Activity 或 Fragment 通常会有一个视图。你能给我供给一个场景,实例的视图被破坏了,但实例(例如 Activity 或 Fragment)还存在。
过错答复
- 当装备发生变化时(例如设备旋转)
- 当内存不足时,App 在后台运转,进程会杀死 App
正确答复
实际上 Activity
总是与其视图一同被毁掉。因而,在 Activity 中没有 onDestroyView ()
生命周期办法。
只要在 Fragment
中有 onDestroyView ()
生命周期办法。在大多数情况下 Fragment
和它的视图一同被毁掉。
但是通过 Fragment transaction
用一个 Fragment
替换另一个 Fragment
时,栈下面的 Fragment
依然存在,但是它的视图被破坏了。
当一个 Fragment
被另一个 Fragment
替换时,会调用 onDestroyView () 办法,但不会调用 onDestroy () 或 onDetect () 办法。
正由于这种复杂性,在运用 Fragment
时,会遇到许多古怪的问题。和 Fragment
相关的问题,将会在后面的文章中共享。
问题七:Lifecycle Aware Coroutine
在 App 中运用协程,怎么保证它们的生命周期可感知。
过错答复
- 关于一般视图,只需在
Activity
或Fragment
中运用lifecycleScope
,在ViewModel
中运用viewModelScope
- 关于组合视图,需求运用
stateFlow
中的collectAsState()
办法,由于当可组合函数不活动时,它不会收集
正确答复
关于一般视图,即便 lifecycleScope
是可用的,它在 Activity
或 Fragment
的整个生命周期中都处于活动状况。由于有时咱们希望某些场景只在 onStart()
或 onResume()
之后处于活动状况。
为此,咱们需求在 lifecycleScope
中运用像 repeatOnLifecycle
这样的 API 供给额外的作用域。
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.stateFlowValue.collect {
// Do something
}
}
}
它们的变体如下图所示。
关于组合视图 collectAsState()
不会保证在组合函数处于活动状况时安全运用数据,它也不会中止继续发送 StateFlow
,这会导致资源浪费。
为了保证只在 Activity
或 Fragment
处于正确的生命周期时,例如在 onStart ()
之后宣布,咱们需求运用 collectAsStateWithLifecycle ()
和 stateFlow
中的 WhileSubscribed (...)
。
当咱们在研讨 collectAsStateWithLifecycle()
源码时,发现它也在运用 repeatOnLifecycle(…)
。
全文到这儿就完毕了,感谢你的阅览,坚持原创不易,欢迎在看、点赞、共享给身边的小伙伴,我会继续共享原创干货!!!
我开了一个云同步编译东西(SyncKit),主要用于本地写代码,同步到长途设备,在长途设备上进行编译,最后将编译的结果同步到本地,代码现已上传到 Github,欢迎前往库房 hi-dhl/SyncKit 检查。
- 库房 SyncKit:https://github.com/hi-dhl/SyncKit
- 下载地址:https://github.com/hi-dhl/SyncKit/releases
真诚引荐你重视我,大众号:ByteCode ,继续共享硬核原创内容,Kotlin、Jetpack、功能优化、体系源码、算法及数据结构、动画、大厂面经。
哔哩哔哩 | 博客 | Github |
最新文章
- Android 13这些权限废弃,你的使用受影响了吗?
- Android 12 已来,你的 App 溃散了吗?
- Android 利器,我开发了云同步编译东西
- Twitter 上风趣的代码
- 谁动了我的内存,揭秘 OOM 溃散下降 90% 的秘密
- 反射技巧让你的功能提高 N 倍
- 90%人不懂的泛型局限性,泛型擦除,星投影
- 揭秘反射真的很耗时吗,射 10 万次耗时多久
- Google 宣布废弃 LiveData.observe 办法
- 影响功能的 Kotlin 代码(一)
- 揭秘 Kotlin 中的 == 和 ===
开源新项目
-
云同步编译东西(SyncKit),本地写代码,长途编译,欢迎前去检查 SyncKit
-
KtKit 细巧而实用,用 Kotlin 语言编写的东西库,欢迎前去检查 KtKit
-
最全、最新的 AndroidX Jetpack 相关组件的实战项目以及相关组件原理剖析文章,正在逐步添加 Jetpack 新成员,库房继续更新,欢迎前去检查 AndroidX-Jetpack-Practice
-
LeetCode / 剑指 offer,包含多种解题思路、时间复杂度、空间复杂度剖析,在线阅览