首页 最新 热门 推荐

  • 首页
  • 最新
  • 热门
  • 推荐

Android Jetpack原理相关面试题分享(二)

  • 25-04-16 15:01
  • 2656
  • 6821
juejin.cn

一、LiveData和RxJava有什么区别?

LiveData 和 RxJava 是 Android 开发中常用的两种数据观察和响应式编程工具,但它们的设计目标、使用场景和特性有显著区别。以下是它们的对比:

1. 设计目标

  • LiveData:

    • 专为 Android 设计,主要用于在 UI 层观察数据变化。
    • 与 Android 生命周期紧密集成,确保数据更新只在 UI 处于活跃状态时触发,避免内存泄漏。
    • 简单易用,适合处理 UI 相关的数据流。
  • RxJava:

    • 是一个通用的响应式编程库,适用于任何 Java 项目,不限于 Android。
    • 提供了强大的数据流操作符(如 map、filter、flatMap 等),适合处理复杂的异步任务和数据流。
    • 需要手动管理生命周期,否则可能导致内存泄漏。

2. 生命周期感知

  • LiveData:

    • 自动感知生命周期,确保观察者只在 STARTED 或 RESUMED 状态下接收数据更新。
    • 无需手动处理生命周期,避免内存泄漏。
  • RxJava:

    • 不直接支持生命周期感知,需要借助 RxLifecycle 或 AutoDispose 等第三方库来管理生命周期。
    • 如果不处理生命周期,可能导致内存泄漏。

3. 数据流处理能力

  • LiveData:

    • 功能较为简单,主要用于观察单一数据源的变化。
    • 不支持复杂的数据流操作(如线程切换、数据转换等)。
    • 适合简单的 UI 数据绑定。
  • RxJava:

    • 提供了丰富的操作符(如 map、filter、flatMap、zip 等),可以轻松处理复杂的数据流。
    • 支持线程切换(如 subscribeOn、observeOn),方便处理异步任务。
    • 适合处理复杂的业务逻辑和数据流。

4. 线程管理

  • LiveData:

    • 默认在主线程中触发数据更新。
    • 如果需要在后台线程更新数据,可以使用 postValue 方法。
  • RxJava:

    • 提供了强大的线程调度功能,可以通过 subscribeOn 和 observeOn 灵活切换线程。
    • 适合处理多线程任务。

5. 使用场景

  • LiveData:

    • 适合简单的 UI 数据绑定,例如从 ViewModel 向 UI 层传递数据。
    • 适合与 ViewModel 结合使用,实现 MVVM 架构。
  • RxJava:

    • 适合处理复杂的异步任务和数据流,例如网络请求、数据库操作、事件总线等。
    • 适合需要大量数据转换和组合的场景。

6. 学习曲线

  • LiveData:

    • 学习曲线较低,API 简单易用。
    • 适合初学者或不需要复杂数据流处理的场景。
  • RxJava:

    • 学习曲线较高,需要掌握响应式编程的概念和大量操作符。
    • 适合有经验的开发者或需要处理复杂逻辑的场景。

7. 性能

  • LiveData:

    • 轻量级,性能开销较小。
    • 适合简单的 UI 数据更新。
  • RxJava:

    • 功能强大,但性能开销相对较大,尤其是在处理复杂数据流时。
    • 需要合理使用操作符和线程调度,避免性能问题。

8. 代码示例

LiveData 示例:

java
代码解读
复制代码
// ViewModel MutableLiveData liveData = new MutableLiveData<>(); // 更新数据 liveData.setValue("Hello, LiveData!"); // 观察数据 liveData.observe(this, new Observer() { @Override public void onChanged(String value) { // 更新 UI textView.setText(value); } });

RxJava 示例:

java
代码解读
复制代码
// 创建 Observable Observable observable = Observable.just("Hello, RxJava!"); // 订阅并观察数据 observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(new Observer() { @Override public void onNext(String value) { // 更新 UI textView.setText(value); } @Override public void onError(Throwable e) { // 处理错误 } @Override public void onComplete() { // 完成 } });

总结

特性LiveDataRxJava
生命周期感知是需要第三方库支持
数据流处理能力简单强大
线程管理有限灵活
使用场景简单 UI 数据绑定复杂异步任务和数据流
学习曲线低高
性能轻量级较重
  • 如果只是简单的 UI 数据绑定,推荐使用 LiveData。
  • 如果需要处理复杂的异步任务或数据流,推荐使用 RxJava。

二、如何避免LiveData粘性事件?

LiveData 的“粘性事件”是指当一个新的观察者开始观察 LiveData 时,它会立即接收到 LiveData 中最后一条数据,即使这条数据是在观察者注册之前更新的。这种行为在某些场景下可能会导致问题(例如重复处理数据或触发不必要的 UI 更新)。以下是避免 LiveData 粘性事件的几种方法:

1. 使用 SingleLiveEvent

SingleLiveEvent 是一个自定义的 LiveData 实现,确保每个事件只被观察一次,避免粘性事件。

实现代码:

java
代码解读
复制代码
public class SingleLiveEvent extends MutableLiveData { private final AtomicBoolean mPending = new AtomicBoolean(false); @Override public void observe(@NonNull LifecycleOwner owner, @NonNull Observersuper T> observer) { super.observe(owner, t -> { if (mPending.compareAndSet(true, false)) { observer.onChanged(t); } }); } @Override public void setValue(T value) { mPending.set(true); super.setValue(value); } public void call() { setValue(null); } }

使用方式:

java
代码解读
复制代码
// ViewModel private SingleLiveEvent singleLiveEvent = new SingleLiveEvent<>(); public SingleLiveEvent getSingleLiveEvent() { return singleLiveEvent; } // 触发事件 singleLiveEvent.setValue("Hello, SingleLiveEvent!"); // 观察事件 viewModel.getSingleLiveEvent().observe(this, value -> { // 处理事件 });

2. 使用 Event 包装类

通过将数据包装在一个 Event 类中,可以标记数据是否已经被处理,从而避免重复触发。

实现代码:

java
代码解读
复制代码
public class Event { private T content; private boolean hasBeenHandled = false; public Event(T content) { this.content = content; } public T getContentIfNotHandled() { if (hasBeenHandled) { return null; } else { hasBeenHandled = true; return content; } } public T peekContent() { return content; } }

使用方式:

java
代码解读
复制代码
// ViewModel private MutableLiveData> liveData = new MutableLiveData<>(); public MutableLiveData> getLiveData() { return liveData; } // 触发事件 liveData.setValue(new Event<>("Hello, Event!")); // 观察事件 viewModel.getLiveData().observe(this, event -> { String content = event.getContentIfNotHandled(); if (content != null) { // 处理事件 } });

3. 使用 MediatorLiveData

MediatorLiveData 可以监听其他 LiveData 的变化,并在需要时过滤掉粘性事件。

实现代码:

java
代码解读
复制代码
// ViewModel private MutableLiveData sourceLiveData = new MutableLiveData<>(); private MediatorLiveData mediatorLiveData = new MediatorLiveData<>(); public MediatorLiveData getMediatorLiveData() { return mediatorLiveData; } public void triggerEvent(String value) { sourceLiveData.setValue(value); } { mediatorLiveData.addSource(sourceLiveData, value -> { mediatorLiveData.setValue(value); mediatorLiveData.removeSource(sourceLiveData); // 移除源以避免粘性事件 }); } // 观察事件 viewModel.getMediatorLiveData().observe(this, value -> { // 处理事件 });

4. 使用 Kotlin 的 SharedFlow(推荐)

如果你使用 Kotlin,可以使用 SharedFlow 替代 LiveData,它天然支持非粘性事件。

实现代码:

kotlin
代码解读
复制代码
// ViewModel private val _events = MutableSharedFlow() val events: SharedFlow = _events fun triggerEvent(value: String) { viewModelScope.launch { _events.emit(value) } } // 观察事件 viewModel.events.collect { value -> // 处理事件 }

5. 手动清除数据

在观察者处理完数据后,手动清除 LiveData 中的数据,避免重复触发。

java
代码解读
复制代码
// ViewModel private MutableLiveData liveData = new MutableLiveData<>(); public MutableLiveData getLiveData() { return liveData; } public void clearLiveData() { liveData.setValue(null); } // 观察事件 viewModel.getLiveData().observe(this, value -> { if (value != null) { // 处理事件 viewModel.clearLiveData(); // 清除数据 } });

总结

方法优点缺点
SingleLiveEvent简单易用,适合一次性事件只能处理单个观察者
Event 包装类灵活,适合多种场景需要手动调用 getContentIfNotHandled
MediatorLiveData可以监听多个 LiveData实现稍复杂
SharedFlow(Kotlin)天然支持非粘性事件,功能强大仅适用于 Kotlin
手动清除数据简单直接需要手动管理,容易遗漏

根据具体场景选择合适的方法,推荐优先使用 Event 包装类或 SharedFlow(如果使用 Kotlin)。

三、ViewModel如何穿透生命周期?

ViewModel 是 Android 架构组件的一部分,它的设计目的是为了在配置更改(如屏幕旋转)时保留数据,从而避免重新加载数据或重新初始化 UI 状态。然而,ViewModel 的生命周期是与 Activity 或 Fragment 绑定的,当 Activity 或 Fragment 被销毁时(例如用户按返回键退出),ViewModel 也会被清除。如果需要在 Activity 或 Fragment 销毁后仍然保留数据,可以通过以下方法实现“穿透生命周期”的效果。

1. 使用 SavedStateHandle

SavedStateHandle 是 ViewModel 的一个扩展功能,允许在 Activity 或 Fragment 被系统销毁并重建时(例如配置更改或进程被杀死后恢复)保留数据。

实现代码:

kotlin
代码解读
复制代码
class MyViewModel(private val savedStateHandle: SavedStateHandle) : ViewModel() { // 保存和恢复数据 var myData: String get() = savedStateHandle.get("myData") ?: "" set(value) = savedStateHandle.set("myData", value) }

在 Activity 或 Fragment 中使用:

kotlin
代码解读
复制代码
class MyActivity : AppCompatActivity() { private lateinit var viewModel: MyViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = ViewModelProvider(this).get(MyViewModel::class.java) // 使用 viewModel.myData } }

优点:

  • 支持进程被杀死后恢复数据。
  • 简单易用,适合保存少量数据。

缺点:

  • 只能保存简单的数据类型(如 String、Int 等),不适合保存复杂对象。

2. 使用持久化存储

如果需要在 Activity 或 Fragment 完全销毁后仍然保留数据,可以将数据保存到持久化存储中,例如 SharedPreferences、数据库或文件。

使用 SharedPreferences 示例:

kotlin
代码解读
复制代码
class MyViewModel(application: Application) : AndroidViewModel(application) { private val sharedPreferences = application.getSharedPreferences("my_prefs", Context.MODE_PRIVATE) fun saveData(data: String) { sharedPreferences.edit().putString("myData", data).apply() } fun loadData(): String { return sharedPreferences.getString("myData", "") ?: "" } }

在 Activity 或 Fragment 中使用:

kotlin
代码解读
复制代码
class MyActivity : AppCompatActivity() { private lateinit var viewModel: MyViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = ViewModelProvider(this).get(MyViewModel::class.java) // 保存数据 viewModel.saveData("Hello, World!") // 加载数据 val data = viewModel.loadData() } }

优点:

  • 数据持久化,即使应用被杀死也能保留。
  • 适合保存大量数据或复杂对象。

缺点:

  • 需要手动管理数据的存储和加载。
  • 性能开销较大,不适合频繁读写。

3. 使用单例模式

通过单例模式将数据保存在内存中,即使 ViewModel 被销毁,数据仍然保留。

实现代码:

kotlin
代码解读
复制代码
object DataHolder { var myData: String? = null } class MyViewModel : ViewModel() { fun saveData(data: String) { DataHolder.myData = data } fun loadData(): String? { return DataHolder.myData } }

在 Activity 或 Fragment 中使用:

kotlin
代码解读
复制代码
class MyActivity : AppCompatActivity() { private lateinit var viewModel: MyViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = ViewModelProvider(this).get(MyViewModel::class.java) // 保存数据 viewModel.saveData("Hello, World!") // 加载数据 val data = viewModel.loadData() } }

优点:

  • 数据保存在内存中,访问速度快。
  • 简单易用。

缺点:

  • 数据不会持久化,应用进程被杀死后数据丢失。
  • 可能导致内存泄漏,需要谨慎使用。

4. 使用 Application 类

通过自定义 Application 类保存全局数据,即使 Activity 或 Fragment 被销毁,数据仍然保留。

实现代码:

kotlin
代码解读
复制代码
class MyApplication : Application() { var myData: String? = null } class MyViewModel(application: Application) : AndroidViewModel(application) { private val app = application as MyApplication fun saveData(data: String) { app.myData = data } fun loadData(): String? { return app.myData } }

在 Activity 或 Fragment 中使用:

kotlin
代码解读
复制代码
class MyActivity : AppCompatActivity() { private lateinit var viewModel: MyViewModel override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewModel = ViewModelProvider(this).get(MyViewModel::class.java) // 保存数据 viewModel.saveData("Hello, World!") // 加载数据 val data = viewModel.loadData() } }

优点:

  • 数据保存在内存中,访问速度快。
  • 适合保存全局数据。

缺点:

  • 数据不会持久化,应用进程被杀死后数据丢失。
  • 可能导致内存泄漏,需要谨慎使用。

5. 使用 Navigation 组件

如果是在 Fragment 之间共享数据,可以使用 Navigation 组件的 SavedStateHandle 或 ViewModel 共享数据。

实现代码:

kotlin
代码解读
复制代码
val navController = findNavController() val viewModel = navController.getViewModel()

优点:

  • 适合 Fragment 之间的数据共享。
  • 与 Navigation 组件无缝集成。

缺点:

  • 仅限于 Fragment 之间使用。

总结

方法优点缺点
SavedStateHandle支持配置更改和进程恢复只能保存简单数据
持久化存储数据持久化,适合大量数据性能开销较大
单例模式简单易用,访问速度快数据不持久化,可能导致内存泄漏
Application 类适合保存全局数据数据不持久化,可能导致内存泄漏
Navigation 组件适合 Fragment 之间共享数据仅限于 Fragment 使用

根据具体需求选择合适的方法:

  • 如果需要支持配置更改和进程恢复,使用 SavedStateHandle。
  • 如果需要持久化数据,使用持久化存储。
  • 如果需要全局共享数据,使用单例模式或 Application 类。

更多分享

  1. Android Jetpack相关面试题分享(一)
  2. Android 架构以及优化相关面试题分享
  3. Android Kotlin协程相关面试题分享
  4. Android 互联网大厂,高频重点面试题集分享(一)
注:本文转载自juejin.cn的QING618的文章"https://juejin.cn/post/7475504940652691456"。版权归原作者所有,此博客不拥有其著作权,亦不承担相应法律责任。如有侵权,请联系我们删除。
复制链接
复制链接
相关推荐
发表评论
登录后才能发表评论和回复 注册

/ 登录

评论记录:

未查询到任何数据!
回复评论:

分类栏目

后端 (14832) 前端 (14280) 移动开发 (3760) 编程语言 (3851) Java (3904) Python (3298) 人工智能 (10119) AIGC (2810) 大数据 (3499) 数据库 (3945) 数据结构与算法 (3757) 音视频 (2669) 云原生 (3145) 云平台 (2965) 前沿技术 (2993) 开源 (2160) 小程序 (2860) 运维 (2533) 服务器 (2698) 操作系统 (2325) 硬件开发 (2492) 嵌入式 (2955) 微软技术 (2769) 软件工程 (2056) 测试 (2865) 网络空间安全 (2948) 网络与通信 (2797) 用户体验设计 (2592) 学习和成长 (2593) 搜索 (2744) 开发工具 (7108) 游戏 (2829) HarmonyOS (2935) 区块链 (2782) 数学 (3112) 3C硬件 (2759) 资讯 (2909) Android (4709) iOS (1850) 代码人生 (3043) 阅读 (2841)

热门文章

101
推荐
关于我们 隐私政策 免责声明 联系我们
Copyright © 2020-2024 蚁人论坛 (iYenn.com) All Rights Reserved.
Scroll to Top