当前位置: 首页 > news >正文

利用ViewModel和LiveData进行数据管理

利用ViewModel和LiveData进行数据管理

1. 引言

在当今移动应用开发的世界中,数据管理是一个至关重要的方面。随着应用的复杂性不断增加,需要有效地管理和维护应用中的数据。无论是从服务器获取数据、本地数据库存储还是用户界面的状态,数据在应用中扮演着关键角色。在这样的背景下,数据管理不仅仅是一项技术任务,更是确保应用用户体验和性能的关键因素。

随着Android应用的不断发展,我们也面临着一些数据管理的挑战。这些挑战包括生命周期管理、配置更改导致的数据丢失以及异步操作的处理等。为了克服这些问题,现代Android开发采用了一些架构和库来更好地管理数据流。其中,使用ViewModel和LiveData这一组合来进行数据管理已经成为了一个普遍的选择。在本文中,我们将深入探讨如何使用ViewModel和LiveData来解决这些数据管理的挑战,从而提升应用的可维护性、性能和用户体验。

2. 数据管理的挑战

在移动应用开发中,数据管理是一个复杂且关键的任务。应用需要有效地处理从不同来源获取的数据,以及在用户界面中展示和交互这些数据。然而,这个过程中存在许多挑战,包括但不限于以下几点:

2.1 生命周期管理: Android应用的生命周期是动态变化的,用户可能会在不同的时间点进入、离开应用,甚至是在应用后台运行。这就带来了数据管理的问题,例如在Activity或Fragment销毁后重新加载数据。

2.2 配置更改: 当用户旋转设备或者改变了应用的配置(如语言、主题等),Activity或Fragment会被销毁并重新创建,这可能导致之前的数据丢失。

2.3 异步操作: 访问网络、数据库或其他耗时操作都需要在后台线程进行,以避免阻塞主线程造成界面卡顿。但是这也带来了数据同步的问题,如何在后台线程操作完成后更新界面数据。

2.4 数据一致性: 在多个界面之间共享数据,或者在不同时间点获取数据,需要保证数据的一致性,避免出现数据冲突和不一致的情况。

为了解决这些挑战,我们需要一种结构和模式来管理数据流,使数据在应用的不同组件之间保持一致和有效。在这方面,ViewModel和LiveData的组合提供了一种高效的方法来处理这些问题。下文将详细介绍如何使用它们来优化数据管理过程。

3. ViewModel的介绍

在Android应用中,ViewModel是一种设计模式,用于管理UI相关的数据和业务逻辑。它主要解决了由生命周期引起的数据丢失、内存泄漏和重复加载数据等问题。ViewModel的设计思想是将UI和数据分开,使得数据在配置更改、Activity或Fragment销毁重建等情况下能够持久保留。

3.1 作用和用途: ViewModel的主要作用是存储和管理与UI相关的数据,如界面元素的状态、用户输入等。通过ViewModel,我们可以在不同的配置更改和生命周期事件之间保持数据的一致性,避免了重新加载数据带来的性能问题和用户体验下降。

3.2 数据存储和管理: ViewModel使用持有数据的方式,将数据存储在内存中,并在需要时提供给UI层。这使得UI组件(如Activity和Fragment)可以轻松地访问数据,而无需担心生命周期的变化。

3.3 生命周期和数据保持: ViewModel与UI组件的生命周期绑定,它会在UI组件的销毁和重建时保持不变。这意味着当Activity或Fragment因配置更改而销毁并重建时,ViewModel中的数据不会丢失,用户体验得以提升。

以下是一个简单的示例代码,展示了如何创建和使用ViewModel:

class MyViewModel : ViewModel() {// 定义需要存储和管理的数据val userData: MutableLiveData<User> = MutableLiveData()fun fetchUserData() {// 模拟从网络或数据库获取数据val user = UserRepository.getUser()userData.postValue(user)}
}

在Activity或Fragment中使用ViewModel:

class MyActivity : AppCompatActivity() {private val viewModel: MyViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_my)// 获取ViewModel中的数据viewModel.userData.observe(this, Observer { user ->// 更新UI显示数据textView.text = user.name})// 获取用户数据viewModel.fetchUserData()}
}

通过使用ViewModel,我们可以有效地将UI和数据分离,保持数据的一致性,提升应用的性能和用户体验。

4. LiveData的作用和特点

LiveData是Android架构组件中的一个关键类,用于实现观察者模式,以便在数据发生变化时通知相关的观察者。它为应用提供了一种响应式的数据持有方式,使数据与UI之间的通信更加简洁和可靠。

4.1 观察者模式的作用: 观察者模式是一种常见的软件设计模式,用于在对象之间建立一对多的依赖关系,当一个对象的状态发生变化时,其所有依赖的对象都会得到通知。LiveData通过实现这一模式,使得UI组件(观察者)能够在数据发生变化时获得及时通知。

4.2 LiveData的优点: LiveData具有许多优点,使其成为Android开发中不可或缺的一部分:

  • 生命周期感知: LiveData与UI组件的生命周期绑定,能够自动感知UI组件的生命周期状态,避免了由于UI销毁时仍然更新数据而引发的问题,例如内存泄漏。

  • 自动更新UI: 当LiveData中的数据发生变化时,与之关联的UI组件会自动更新,无需手动处理数据更新和UI刷新的逻辑,简化了代码和减少了出错的可能性。

  • 数据一致性: LiveData的生命周期感知和自动更新UI特性保证了数据与UI的一致性,避免了UI显示过期的数据,提升了用户体验。

  • 线程安全: LiveData内部已经处理了多线程访问的问题,确保数据的安全访问,开发者无需担心多线程同步问题。

4.3 LiveData与传统观察者模式的对比: 传统的观察者模式需要开发者手动管理观察者与被观察者之间的关系,容易引发内存泄漏和UI更新不及时等问题。LiveData通过解决这些问题,简化了观察者模式的实现,提供了更好的数据管理和UI更新方式。

以下是一个简单的示例代码,展示了如何使用LiveData来实现观察者模式:

class MyViewModel : ViewModel() {val userData: MutableLiveData<User> = MutableLiveData()fun fetchUserData() {val user = UserRepository.getUser()userData.postValue(user)}
}

在Activity或Fragment中观察LiveData:

class MyActivity : AppCompatActivity() {private val viewModel: MyViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_my)viewModel.userData.observe(this, Observer { user ->textView.text = user.name})viewModel.fetchUserData()}
}

通过使用LiveData,我们可以轻松实现数据与UI之间的通信,保持数据的一致性,并减少了许多传统观察者模式所带来的问题。

5. 构建ViewModel和LiveData

在Android应用中,使用ViewModel和LiveData可以有效地管理数据并与UI进行通信。下面将演示如何创建ViewModel和LiveData实例,并展示如何将它们结合使用。

5.1 创建ViewModel: ViewModel用于管理与UI相关的数据。每个Activity或Fragment都可以关联一个ViewModel,使得数据在配置更改时能够被保留。

class MyViewModel : ViewModel() {// 定义LiveData来存储数据val userData: MutableLiveData<User> = MutableLiveData()fun fetchUserData() {val user = UserRepository.getUser()// 更新LiveData中的数据userData.postValue(user)}
}

5.2 创建LiveData: LiveData用于持有数据并通知观察者数据的变化。它通常作为ViewModel中的一个属性使用。

class MyViewModel : ViewModel() {// 定义LiveData来存储数据val userData: MutableLiveData<User> = MutableLiveData()// ...
}

5.3 在Activity或Fragment中使用ViewModel和LiveData: 在UI组件中使用ViewModel和LiveData可以确保数据的生命周期感知和自动更新UI。

class MyActivity : AppCompatActivity() {private val viewModel: MyViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_my)// 观察LiveData,当数据发生变化时更新UIviewModel.userData.observe(this, Observer { user ->textView.text = user.name})// 触发网络请求并更新LiveData中的数据viewModel.fetchUserData()}
}

通过上述代码,我们可以创建一个ViewModel实例并与Activity或Fragment关联,同时使用LiveData来观察数据的变化,并在数据更新时自动更新UI。这样就实现了数据的管理和UI的通信,同时保障了生命周期感知,确保了数据的一致性和UI的更新。

6. 在UI中使用LiveData

LiveData作为一种观察者模式的数据持有类,在UI层的使用非常方便,能够实时监听数据变化并更新UI。下面将演示如何在UI中使用LiveData观察数据变化,并展示如何在ViewModel中更新LiveData的数据。

6.1 观察LiveData数据变化: 在Activity或Fragment中使用LiveData可以轻松地观察数据的变化,并根据数据的更新来更新UI。

class MyActivity : AppCompatActivity() {private val viewModel: MyViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_my)// 观察LiveData,当数据发生变化时更新UIviewModel.userData.observe(this, Observer { user ->// 更新UItextView.text = user.name})}
}

6.2 更新LiveData数据: LiveData的数据更新应当通过ViewModel来进行,以确保数据一致性和生命周期感知。

class MyViewModel : ViewModel() {val userData: MutableLiveData<User> = MutableLiveData()fun fetchUserData() {val user = UserRepository.getUser()// 更新LiveData中的数据userData.postValue(user)}
}

6.3 LiveData的生命周期感知: LiveData自动感知Activity或Fragment的生命周期,当UI处于活跃状态时才会触发数据更新。

class MyActivity : AppCompatActivity() {private val viewModel: MyViewModel by viewModels()override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_my)// 观察LiveData,当Activity处于活跃状态时更新UIviewModel.userData.observe(this, Observer { user ->// 更新UItextView.text = user.name})}
}

通过上述代码,我们可以看到如何在UI层使用LiveData来观察数据的变化,并在数据更新时自动更新UI。同时,在ViewModel中更新LiveData的数据可以保障数据的一致性和生命周期感知。这种方式能够有效地实现数据的管理和UI的更新,提升了应用的可维护性和用户体验。

7. 数据转换和变换

LiveData不仅能够用于观察数据的变化,还提供了一些强大的操作符,使我们能够对数据进行转换和变换,以满足不同的业务需求。下面将介绍LiveData的数据转换和变换功能,并提供示例代码来展示如何使用LiveData的map、switchMap等操作符。

7.1 使用map操作符: map操作符允许我们将LiveData中的数据进行转换,生成一个新的LiveData。

class MyViewModel : ViewModel() {val originalData: MutableLiveData<Int> = MutableLiveData()// 使用map操作符将数据乘以2val transformedData: LiveData<Int> = originalData.map { originalValue ->originalValue * 2}
}

7.2 使用switchMap操作符: switchMap操作符通常用于在一个LiveData的值发生变化时,自动切换到另一个LiveData,例如在进行搜索时切换到新的搜索结果LiveData。

class MyViewModel : ViewModel() {val searchInput: MutableLiveData<String> = MutableLiveData()// 使用switchMap操作符将searchInput转换为搜索结果LiveDataval searchResults: LiveData<List<Result>> = searchInput.switchMap { query ->Repository.search(query)}
}

7.3 使用MediatorLiveData: MediatorLiveData用于观察其他多个LiveData的变化,并在它们变化时执行特定的逻辑。

class MyViewModel : ViewModel() {val data1: LiveData<Int> = ...val data2: LiveData<String> = ...// 使用MediatorLiveData观察data1和data2的变化,并计算它们的和val sum: MediatorLiveData<Int> = MediatorLiveData<Int>().apply {addSource(data1) { value1 ->val value2 = data2.valuevalue2?.let { value2 ->value = value1 + value2}}addSource(data2) { value2 ->val value1 = data1.valuevalue1?.let { value1 ->value = value1 + value2}}}
}

通过上述示例代码,我们可以看到LiveData的数据转换和变换功能是多么强大和灵活。使用这些操作符,我们能够在不破坏响应式编程原则的情况下,对数据进行各种操作和变换,满足不同的业务需求。这种方式使得数据处理更加模块化和可维护,提升了代码的可读性和灵活性。

8. 处理异步操作

在移动应用中,异步操作如网络请求、数据库查询等是很常见的,然而正确地处理这些异步操作可能会涉及到线程管理、内存泄漏等问题。LiveData作为一种响应式编程的工具,能够帮助我们优雅地处理异步操作,确保应用的稳定性和性能。

8.1 LiveData与异步操作: 通常情况下,我们会在ViewModel中处理异步操作,并使用LiveData将操作的结果传递给UI层。LiveData的生命周期感知特性使得它能够自动管理订阅和取消订阅,避免了内存泄漏问题。

class MyViewModel : ViewModel() {private val _data = MutableLiveData<List<Item>>()val data: LiveData<List<Item>> = _datafun fetchData() {viewModelScope.launch {val result = Repository.getData() // 一个耗时的异步操作_data.value = result}}
}

8.2 在UI中观察LiveData: 在Activity或Fragment中,我们可以观察ViewModel中的LiveData,以及响应数据的变化。

class MyFragment : Fragment() {private val viewModel: MyViewModel by viewModels()override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)viewModel.data.observe(viewLifecycleOwner) { items ->// 更新UI,显示数据adapter.submitList(items)}// 触发异步操作viewModel.fetchData()}
}

8.3 LiveData与协程结合使用: 在处理耗时的异步操作时,协程是一个强大的工具。我们可以使用协程来执行异步操作,并将结果传递给LiveData。

class MyViewModel : ViewModel() {private val _data = MutableLiveData<List<Item>>()val data: LiveData<List<Item>> = _datafun fetchData() {viewModelScope.launch {val result = withContext(Dispatchers.IO) {Repository.getData() // 一个耗时的异步操作}_data.value = result}}
}

通过LiveData与协程的结合使用,我们能够在保持代码简洁和易读的同时,有效地处理异步操作。LiveData的生命周期感知性质使得它与Activity、Fragment等组件的生命周期自然对应,确保在适当的时间执行操作并避免内存泄漏问题。这样,我们可以更加专注于业务逻辑的实现,而不用过多考虑线程管理和异步操作的细节。

9. 最佳实践和注意事项

使用ViewModel和LiveData是一种优雅的方式来进行数据管理,但在实践中需要遵循一些最佳实践和注意事项,以确保应用的稳定性和性能。

9.1 避免数据倾泻: 将适量的数据暴露给UI层,不要过多地将业务逻辑和计算放在ViewModel中。ViewModel应该主要关注数据的管理和转换,而不是业务逻辑的实现。

9.2 适当使用Transformations: Transformations是LiveData提供的一种工具,用于在LiveData之间进行数据转换。但不要滥用Transformations,过多的链式转换可能会影响代码的可读性和性能。

// 正确示例
val transformedLiveData: LiveData<Result> = Transformations.map(originalLiveData) {// 转换数据并返回新的数据对象
}// 不推荐示例:过多的链式转换
val transformedLiveData: LiveData<Result> = Transformations.map(originalLiveData) {// ...
}.switchMap {// ...
}.map {// ...
}

9.3 理解LiveData的生命周期感知: LiveData会自动管理观察者的生命周期,确保在适当的时间添加和移除观察者,避免内存泄漏。但也要注意,在非常特殊的情况下,可能会导致数据更新延迟的问题。

9.4 使用适当的线程: LiveData默认在主线程中分发数据更新,但在ViewModel中执行耗时操作时,需要手动切换到后台线程以避免阻塞UI线程。

9.5 异常处理: 在LiveData的观察者中,及时处理可能的异常情况,以提供更好的用户体验。

9.6 单一数据源原则: 在整个应用中,尽量遵循单一数据源原则,将数据的获取和处理集中在ViewModel中,避免在多个地方同时操作数据。

9.7 协调ViewModel和Activity/Fragment: ViewModel中不应该持有对Activity或Fragment的引用,以防止内存泄漏。可以使用LiveData来在ViewModel和UI之间进行通信。

9.8 单元测试: 使用ViewModel和LiveData能够更容易进行单元测试,确保数据处理和业务逻辑的正确性。

通过遵循这些最佳实践和注意事项,您可以更好地利用ViewModel和LiveData来管理应用的数据,提升应用的可维护性、可扩展性和性能。同时,深入理解LiveData的生命周期感知特性,有助于避免潜在的问题,确保应用的稳定性和用户体验。

10. 结合ViewModel和LiveData的案例

让我们通过一个实际案例来深入理解如何使用ViewModel和LiveData进行数据管理。假设我们要开发一个简单的待办清单应用,展示用户的任务列表,并能够标记任务的完成状态。

10.1 创建Task实体类:

首先,我们需要定义一个Task实体类来表示任务的信息:

data class Task(val id: Int, val title: String, val isCompleted: Boolean)

10.2 创建ViewModel:

接下来,我们创建一个TaskViewModel,用于管理任务列表的数据:

class TaskViewModel : ViewModel() {private val taskList = MutableLiveData<List<Task>>()init {// 模拟从数据源获取任务列表val initialTasks = listOf(Task(1, "完成文章撰写", false),Task(2, "购买杂货", false),Task(3, "锻炼身体", false))taskList.value = initialTasks}fun getTasks(): LiveData<List<Task>> {return taskList}fun markTaskAsCompleted(taskId: Int) {taskList.value = taskList.value?.map { task ->if (task.id == taskId) {task.copy(isCompleted = true)} else {task}}}
}

10.3 使用ViewModel和LiveData在UI层展示数据:

在Fragment中使用ViewModel和LiveData来观察任务列表的变化,并在UI上展示出来:

class TaskListFragment : Fragment() {private val viewModel: TaskViewModel by viewModels()override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {val view = inflater.inflate(R.layout.fragment_task_list, container, false)val recyclerView: RecyclerView = view.findViewById(R.id.recyclerView)val adapter = TaskAdapter()recyclerView.adapter = adapterviewModel.getTasks().observe(viewLifecycleOwner, { tasks ->adapter.submitList(tasks)})return view}
}

10.4 更新任务状态并触发UI更新:

在Adapter中,我们可以添加点击事件来标记任务的完成状态,并通过ViewModel的方法更新数据:

class TaskAdapter : ListAdapter<Task, TaskAdapter.TaskViewHolder>(TaskDiffCallback()) {// ...override fun onBindViewHolder(holder: TaskViewHolder, position: Int) {val task = getItem(position)holder.bind(task)holder.itemView.setOnClickListener {viewModel.markTaskAsCompleted(task.id)}}
}

通过这个案例,我们展示了如何使用ViewModel和LiveData来管理应用中的数据,并确保数据的一致性和准确性。ViewModel将任务数据存储在LiveData中,使UI能够观察并自动更新,从而实现了数据与UI的分离。这种架构能够有效地解决数据管理中的问题,并提供更好的用户体验。

11. 与其他架构组件的结合

在现代的Android应用开发中,ViewModel和LiveData通常与其他架构组件相结合,以构建更可靠和可维护的应用程序。下面我们将讨论如何将ViewModel和LiveData与其他常用的Android架构组件结合使用。

11.1 结合Room数据库

Room是Android提供的一个SQLite数据库抽象层,可以与ViewModel和LiveData一起使用,实现数据的持久化和管理。通过将Room数据库中的数据封装为LiveData,我们可以轻松地在UI层中观察并自动更新数据。

示例代码:

@Entity(tableName = "tasks")
data class TaskEntity(@PrimaryKey val id: Int,val title: String,val isCompleted: Boolean
)@Dao
interface TaskDao {@Query("SELECT * FROM tasks")fun getAllTasks(): LiveData<List<TaskEntity>>// ...
}

11.2 结合Navigation组件

Navigation组件使得应用的导航更加清晰和简单。ViewModel和LiveData可以用于存储导航目的地的数据,以便在不同的Fragment或Activity之间共享数据并保持一致性。

示例代码:

class SharedViewModel : ViewModel() {val selectedTask = MutableLiveData<Task>()fun selectTask(task: Task) {selectedTask.value = task}
}

11.3 结合WorkManager

WorkManager允许您安排延迟的、可靠的后台任务,例如同步数据或发送通知。您可以使用ViewModel和LiveData来管理WorkManager的任务状态和结果。

示例代码:

class TaskSyncWorker(context: Context, params: WorkerParameters) : Worker(context, params) {override fun doWork(): Result {// 同步数据的逻辑val outputData = workDataOf("result" to "Sync successful")return Result.success(outputData)}
}

通过将ViewModel和LiveData与其他架构组件结合使用,您可以更好地管理和维护应用程序中的数据和业务逻辑。这种集成提供了一种可靠的方式来处理复杂的应用场景,并在不同组件之间共享数据和状态。

12. 结论

使用ViewModel和LiveData作为数据管理的核心组件,可以有效地解决Android应用中的数据管理问题。通过ViewModel的生命周期感知和数据持久化能力,以及LiveData的自动UI更新机制,开发者可以更好地组织和管理应用中的数据流,确保数据的一致性和准确性。

ViewModel的引入可以有效地分离UI层和数据层的逻辑,使代码更具可读性、可维护性和可测试性。LiveData则为实现观察者模式提供了更加强大的支持,使数据的更新和UI的刷新变得更加自动化和高效。

在应用开发中,合理使用ViewModel和LiveData可以避免常见的数据管理问题,如内存泄漏、数据不一致等。同时,结合其他Android架构组件,如Room数据库、Navigation组件和WorkManager,可以构建出更加健壮和高效的应用。

13. 参考资料

  • Android Developers. (2021). ViewModel Overview. https://developer.android.com/topic/libraries/architecture/viewmodel
  • Android Developers. (2021). LiveData Overview. https://developer.android.com/topic/libraries/architecture/livedata
  • Android Developers. (2021). Android Architecture Components. https://developer.android.com/topic/libraries/architecture
  • Ahmad, H. (2019). Android Jetpack Masterclass - Learn Android Architecture Components. Udemy course.
  • Google Codelabs. (2021). Android Kotlin Fundamentals: LiveData. https://developer.android.com/codelabs/kotlin-android-training-live-data
  • Google Codelabs. (2021). Android Kotlin Fundamentals: ViewModel. https://developer.android.com/codelabs/kotlin-android-training-view-model

相关文章:

利用ViewModel和LiveData进行数据管理

利用ViewModel和LiveData进行数据管理 1. 引言 在当今移动应用开发的世界中&#xff0c;数据管理是一个至关重要的方面。随着应用的复杂性不断增加&#xff0c;需要有效地管理和维护应用中的数据。无论是从服务器获取数据、本地数据库存储还是用户界面的状态&#xff0c;数据…...

前后端分离------后端创建笔记(05)用户列表查询接口(下)

本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论&#xff0c;如有侵权请联系 源码&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…...

浅谈GIS和三维GIS的区别?

GIS&#xff08;地理信息系统&#xff09;和三维GIS&#xff08;3D地理信息系统&#xff09;是地理信息领域的两个重要概念&#xff0c;它们在地理数据的处理和分析方面具有不同的特点和应用。可能很多人分不清二者的区别&#xff0c;本文就带大家简单了解一下二者的区别。 定义…...

ArcGIS Maps SDK for JavaScript系列之三:在Vue3中使用ArcGIS API加载三维地球

目录 SceneView类的常用属性SceneView类的常用方法vue3中使用SceneView类创建三维地球项目准备引入ArcGIS API创建Vue组件在OnMounted中调用初始化函数initArcGisMap创建Camera对象Camera的常用属性Camera的常用方法 要在Vue 3中使用ArcGIS API for JavaScript加载和展示三维地…...

设计列表和超链接

在网页中&#xff0c;大部分信息都是列表结构&#xff0c;如菜单栏、图文列表、分类导航、新闻列表、栏目列表等。HTML5定义了一套列表标签&#xff0c;通过列表结构实现对网页信息的合理排版。另外&#xff0c;网页中还包含大量超链接&#xff0c;通过它实现网页、位置的跳转&…...

rust包跨平台编译,macbook ,linux

在 MacBook 上编译 Rust 项目并生成 Linux 包需要一些步骤。以下是一般的步骤概述&#xff1a; 1. **安装所需工具&#xff1a;** 首先&#xff0c;确保您的 MacBook 上已经安装了所需的工具。您需要 Rust 编程语言的工具链以及一些用于交叉编译到 Linux 的工具。 - 安装 R…...

JAVA集合-List

// 数组的缺点&#xff1a;每次使用都需要指定长度&#xff0c;掉率低&#xff0c;操作麻烦 // // 【java集合体系】&#xff1a;分类&#xff1a;6个接口&#xff0c;1个工具类 // 6个接口&#xff1a; 单列 :Collection,(父接口) // …...

Python|OpenCV-绘制图形和添加文字的方法(2)

前言 本文是该专栏的第2篇,后面将持续分享OpenCV计算机视觉的干货知识,记得关注。 OpenCV作为一个强大的计算机视觉功能库,除了能解决图像处理和计算机视觉任务之外,它还有着非常丰富的图像绘制功能。可以说,不论是在计算机视觉任务中标记目标领域,还是在图像上绘制一些…...

使用GO编译wasm文件并在nodejs中使用

使用GO编译wasm文件并在nodejs中使用 安装Go相关环境 # 安装GO # mac使用homebrew安装 /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" brew install go# vi &#xff5e;/.bashrc&#xff0c; 添加如下内容 e…...

BM22 比较版本号

一.双指针遍历截取 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定&#xff0c;请勿修改&#xff0c;直接返回方法规定的值即可** 比较版本号* param version1 string字符串 * param version2 string字符串 * return int整型*/public …...

【Java】Maven配置文件帮助文档(settings.xml 和 pom.xml)

文章目录 1. settings.xml1.1 localRepository1.2 interactiveMode1.3 offline1.4 pluginGroups1.5 proxies1.6 servers1.7 mirrors1.8 profiles1.9 activeProfiles 2. pom.xml2.1 本项目信息2.2 父项目信息2.3 prerequisites2.4 issueManagement2.5 ciManagement2.6 inception…...

人脸识别技术应用安全管理规定(试行)

近年来&#xff0c;人脸识别技术不断成熟&#xff0c;已大量应用于治安管理、金融支付、门禁考勤等诸多领域&#xff0c;极大便捷了公众生活。然而&#xff0c;人脸识别技术在得到广泛应用的同时&#xff0c;仍存在一些不规范现象。人脸识别因其技术特点&#xff0c;涉及公众敏…...

FPGA应用学习-----FIFO双口ram解决时钟域+asic样机的时钟选通

60m写入异步ram&#xff0c;再用100M从ram中读出 写地址转换为格雷码后&#xff0c;打两拍和读地址判断是否空产生。相反读地址来判断是否满产生。 分割同步模块 asic时钟的门控时钟&#xff0c;fpga是不推荐采用门控时钟的&#xff0c;有很多方法移除fpga的时钟选通。 如果是a…...

zabbix案例--zabbix监控Tomcat

目录 一、 部署tomcat 二、配置zabbix-java-gateway 三、配置zabbix-server 四、配置zabbix-web界面 一、 部署tomcat tar xf apache-tomcat-8.5.16.tar.gz -C /usr/local/ ln -sv /usr/local/apache-tomcat-8.5.16/ /usr/local/tomcat cd /usr/local/tomcat/bin开启JMX…...

Electron 应用实现截图并编辑功能

Electron 应用实现截图并编辑功能 Electron 应用如何实现截屏功能&#xff0c;有两种思路&#xff0c;作为一个框架是否可以通过框架实现截屏&#xff0c;另一种就是 javaScript 结合 html 中画布功能实现截屏。 在初步思考之后&#xff0c;本文优先探索使用 Electron 实现截屏…...

前端= 结构(HTML)+ 样式(CSS)+ 行为(JavaScript)

前端开发确实涵盖了行为&#xff08;JavaScript&#xff09;、样式&#xff08;CSS&#xff09;和结构&#xff08;HTML&#xff09;这三个主要方面。这三个方面在前端开发中密切协作&#xff0c;共同构建用户界面和用户体验。 结构&#xff08;Structure&#xff09;&#xff…...

Flink-网络流控及反压剖析

参考&#xff1a; Apache Flink学习网...

redis 和 mongodb 比较

Redis和MongoDB是两种不同类型的数据库&#xff0c;它们在数据存储和查询方式、数据模型以及适用场景等方面有一些明显的区别。下面是Redis和MongoDB之间的一些比较&#xff1a; 数据模型&#xff1a; Redis&#xff1a;Redis是一个键值存储系统&#xff0c;支持多种数据结构如…...

Linux 主函数参数介绍

主函数如下&#xff1a; int main( int argc, char* argv[], char* envp[]) 参数分析如下&#xff1a; (1) argc 参数个数 (2) argv 参数内容&#xff0c;是char*类型&#xff0c;说明传给主函数的内容是一个一个的字符串。 (3) envp 环境变量&#xff0c;传给主函数的也…...

资料分析(三)—— 基期、现期、人口、增长量

基期 基期值 现期值 - 增长量 增长量/增长率 现期值/1&#xff08;间隔)增长率 化除为乘 &#xff1a;当增长率&#xff5c;r| < 5% 时&#xff0c;&#xff0c; 注&#xff1a;当选项首位相同&#xff0c;第二位也相同时&#xff0c;只能用直除 基期和差 (结合选…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

OpenLayers 可视化之热力图

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 热力图&#xff08;Heatmap&#xff09;又叫热点图&#xff0c;是一种通过特殊高亮显示事物密度分布、变化趋势的数据可视化技术。采用颜色的深浅来显示…...

超短脉冲激光自聚焦效应

前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应&#xff0c;这是一种非线性光学现象&#xff0c;主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场&#xff0c;对材料产生非线性响应&#xff0c;可能…...

Java 语言特性(面试系列1)

一、面向对象编程 1. 封装&#xff08;Encapsulation&#xff09; 定义&#xff1a;将数据&#xff08;属性&#xff09;和操作数据的方法绑定在一起&#xff0c;通过访问控制符&#xff08;private、protected、public&#xff09;隐藏内部实现细节。示例&#xff1a; public …...

PHP和Node.js哪个更爽?

先说结论&#xff0c;rust完胜。 php&#xff1a;laravel&#xff0c;swoole&#xff0c;webman&#xff0c;最开始在苏宁的时候写了几年php&#xff0c;当时觉得php真的是世界上最好的语言&#xff0c;因为当初活在舒适圈里&#xff0c;不愿意跳出来&#xff0c;就好比当初活在…...

FFmpeg 低延迟同屏方案

引言 在实时互动需求激增的当下&#xff0c;无论是在线教育中的师生同屏演示、远程办公的屏幕共享协作&#xff0c;还是游戏直播的画面实时传输&#xff0c;低延迟同屏已成为保障用户体验的核心指标。FFmpeg 作为一款功能强大的多媒体框架&#xff0c;凭借其灵活的编解码、数据…...

STM32F4基本定时器使用和原理详解

STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...

最新SpringBoot+SpringCloud+Nacos微服务框架分享

文章目录 前言一、服务规划二、架构核心1.cloud的pom2.gateway的异常handler3.gateway的filter4、admin的pom5、admin的登录核心 三、code-helper分享总结 前言 最近有个活蛮赶的&#xff0c;根据Excel列的需求预估的工时直接打骨折&#xff0c;不要问我为什么&#xff0c;主要…...

Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器

第一章 引言&#xff1a;语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域&#xff0c;文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量&#xff0c;支撑着搜索引擎、推荐系统、…...

2025盘古石杯决赛【手机取证】

前言 第三届盘古石杯国际电子数据取证大赛决赛 最后一题没有解出来&#xff0c;实在找不到&#xff0c;希望有大佬教一下我。 还有就会议时间&#xff0c;我感觉不是图片时间&#xff0c;因为在电脑看到是其他时间用老会议系统开的会。 手机取证 1、分析鸿蒙手机检材&#x…...