利用ViewModel和LiveData进行数据管理

news2024/12/28 3:15:28

利用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,当数据发生变化时更新UI
        viewModel.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,当数据发生变化时更新UI
        viewModel.userData.observe(this, Observer { user ->
            // 更新UI
            textView.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处于活跃状态时更新UI
        viewModel.userData.observe(this, Observer { user ->
            // 更新UI
            textView.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操作符将数据乘以2
    val 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转换为搜索结果LiveData
    val 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.value
            value2?.let { value2 ->
                value = value1 + value2
            }
        }
        addSource(data2) { value2 ->
            val value1 = data1.value
            value1?.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>> = _data
    
    fun 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>> = _data
    
    fun 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 = adapter

        viewModel.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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/874915.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Python方式实现射后不管导弹的简易制导系统

1 问题 对QN-506上的S570智能反坦克制导导弹的射后不管产生了浓厚的兴趣&#xff0c;想用Python简易还原一下。 2 方法 之前查阅资料时了解到使用pygame库制作的贪吃蛇&#xff0c;是否有一种方法能让“贪吃蛇”一直跟着鼠标走呢&#xff1f;鼠标模拟行进中的坦克&#xff0c;“…

劝你入行嵌入式的五个理由

嵌入式技术作为一个备受瞩目的领域&#xff0c;已经吸引了越来越多的人才。如果您还在考虑是否要进入该领域&#xff0c;我建议您听听以下五个理由&#xff1a; 一、市场需求旺盛 随着科技的迅猛发展&#xff0c;嵌入式系统已经广泛渗透到我们生活的各个方面。从智能家居到智能…

rabbitmq的持久化

目录 队列实现持久化 如何删除队列​编辑 消息实现持久化 不公平分发 如何保障当 RabbitMQ 服务停掉以后消息生产者发送过来的消息不丢失。默认情况下 RabbitMQ 退出或由于某种原因崩溃时&#xff0c;它忽视队列和消息&#xff0c;除非告知它不要这样做。确保消息不会丢失需…

浅析前端请求登录与后台对接

首先确保前后端接口参数一致&#xff0c;我这里使用的是ant design Pro 前端框架 小技&#xff1a;shiftf6&#xff0c;全局重构&#xff0c;当接口不一致时很方便 前&#xff1a; 后&#xff1a; 前后端交互&#xff1a;前端需要向后端发送请求&#xff0c;前端ajax来请求后…

Ubuntu虚拟机增加交换内存

接下来增加交换内存。配置交换内存&#xff0c;需要用户根据自己的实际情况进行配置。这里举例分配 5G 交换内存。 在开始之前&#xff0c;使用命令检查一下您的 ubuntu 的 swap 分区&#xff0c;可以看到交换分区为 2G &#xff0c;如下图所示&#xff1a; sudo swapon …

【ChatGPT 指令大全】怎么使用ChatGPT来辅助知识学习

目录 概念解说 简易教学 深度教学 教学与测验 解释一个主题的背后原理 总结 在当今信息时代&#xff0c;互联网的快速发展为我们获取知识提供了前所未有的便利。而其中&#xff0c;人工智能技术的应用也为我们的学习和交流带来了新的可能性。作为一种基于自然语言处理的人…

(css)点击前隐藏icon图表 点击后显示

(css)点击前隐藏icon图表 点击后显示 效果 html <liv-for"(item,index) in sessionList":key"index"class"liClass":class"{ active: change2 index }"tabindex"2">...<el-tooltip class"item" effec…

双驱拖挂底盘的制作分享

1. 运动功能说明 本文示例将实现R331样机双驱拖挂底盘运动的功能。 2. 结构说明 双驱拖挂底盘是一种具有两个驱动轴的车辆底盘&#xff0c;它由两个驱动轴、悬挂系统、转向系统和连接拖车的结构组成。以下是一些关于双驱拖挂底盘的主要特点和优势&#xff1a; ① 强大的牵引能力…

网络基础-PosixAPI

文章目录 一.网络常用接口Linux协议栈与posix api的关系 背景1.1 socket1.8 close服务端posix接口1.2 bind1.3 listen1.4 accept1.5 connect1.6 send1.7 recv总结 二. 修改句柄属性2.1 fctl2.2 特殊作用2.2.1 设置阻塞非阻塞2.2.1 实例2.2.2设置获取记录锁2.2.2 实例 三、文件操…

苹果恢复已删除的照片,3种超实用教程!

【大家帮帮孩子吧&#xff01;去旅游拍的写真不小心被我删掉了&#xff0c;回收站也找不到&#xff0c;有什么办法能恢复回来吗&#xff1f;】 不小心误删了重要照片该怎么办&#xff1f;大家都知道&#xff0c;苹果手机【最近删除】里的照片只能保留30天&#xff0c;超过了这…

CAD版本怎么在线转换?教你在线转换CAD版本方法

CAD文件版本转换通常是由于文件的创建者使用了与接收者不同的CAD软件版本。此外&#xff0c;如果接收者使用的CAD软件版本与创建者使用的版本不兼容&#xff0c;则可能需要进行文件版本转换&#xff0c;以便接收者能够正确地查看和编辑文件。很多小伙伴喜欢在线处理文件格式&am…

《零基础实践深度学习》(第2版)学习笔记,(五)深度学习与计算机视觉

文章目录 1. 计算机视觉概述2. 图像分类3. 目标检测 1. 计算机视觉概述 图像分类 目标检测 2. 图像分类 3. 目标检测

Guitar Pro8专业版吉他学习、绘谱、创作软件

Guitar Pro 8 专业版更强大&#xff01;更优雅&#xff01;更完美&#xff01;Guitar Pro 8.0 五年磨一剑&#xff01;多达30项功能优化&#xff01;Guitar Pro8 版本一共更新近30项功能&#xff0c;令吉他打谱更出色&#xff01;Guitar Pro8 是自2017年4月发布7.0之后发布的最…

独立游戏开发者的工具栈【Lumnis作者】

作为一名单人游戏开发者&#xff0c;需要成为多面手。 如果你想避免委托给承包商&#xff0c;需要身兼数职&#xff0c;并精通艺术、编程、游戏设计、音乐创作、营销等&#xff0c;具体取决于你制作的游戏。 学习所有这些需要时间和练习&#xff0c;但也需要金钱。 大多数游戏…

thread 的join方法

join方法的底层执行&#xff1f; 看如测试用例&#xff1a; -> 两个红色的框分别说明了sleep方法前后test01的状态&#xff0c;这个不是重点&#xff0c;只是一个验证而已。 重要的是test02的状态&#xff0c;调用join方法后&#xff0c;test02变成了WAITING状态&#xff…

每日一题 92反转链表||

题目 给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], left 2, right 4 输出&#xff1a;…

冠达管理:股票成交量变小的含义?股价不涨是什么原因?

股票成交量是一个很重要的分析指标&#xff0c;遭到许多出资者的重视。那么股票成交量变小的意义&#xff1f;股价不涨是什么原因&#xff1f;冠达管理也为我们预备了相关内容&#xff0c;以供参考。 股票成交量变小的意义&#xff1f; 股票成交量是指在一定时间内&#xff0c…

2023 ChatGPT 3.5+4.0 + AI智能绘图-设计师专项源码 附安装教程

AI绘图专业设计 不得将程序用作任何违法违纪内容&#xff0c;不要让亲人两行泪 界面部分图解构&#xff1a; 前台show&#xff1a; 后台Show&#xff1a; 前端部署&#xff1a; 安装pm2管理器 点击设置 选择v16.19.1版本-切换版本 再新建一个网站 点击设置 添加反向代…

CSS变形与动画(一):transform变形 与 transition过渡动画 详解(用法 + 代码 + 例子 + 效果)

文章目录 变形与动画transform 变形translate 位移scale 缩放rotate 旋转skew 倾斜多种变形设置变形中心点 transition 过渡动画多种属性变化 变形与动画 transform 变形 包括&#xff1a;位移、旋转、缩放、倾斜。 下面的方法都是transform里的&#xff0c;记得加上。 展示效…

冠达管理:股票估值低为什么好于估值高?如何判断估值?

进行股票交易要掌握一些重要的相关常识&#xff0c;比如对股票估值状况的判别&#xff0c;假如出资者能清楚的得知买入的股票价格是否合适&#xff0c;那对于出资来说是大有益处的。但是&#xff0c;关于估值&#xff0c;会有估值低好于高一说&#xff0c;为什么估值低会好于估…