由于协程生命周期范围内的无限循环而导致 Fragment 内存泄漏

问题描述 投票:0回答:1

描述: 我在应用程序中遇到了一个问题,性能随着时间的推移而下降,并且似乎与潜在的内存泄漏有关。设置如下:

场景:

主屏幕有两个片段:一个用于无限循环模拟车速,另一个用于各种功能。 包含片段的左侧屏幕保留在主线程上并且始终打开。 在屏幕右侧,导航图加载了其他六个片段。

问题: 应用程序运行很长一段时间后,我注意到整体性能逐渐下降。 我担心的是,左侧屏幕及其用于模拟车速的无限循环可能会导致此问题,并可能导致内存泄漏。

要求: 我将非常感谢任何解决此问题的见解、建议或最佳实践。具体来说:

处理片段内无限循环的建议,以避免性能下降和内存泄漏。 优化主线程性能的技巧,特别是当屏幕的一侧始终打开时。 使用导航图和加载多个片段时确保高效内存使用并防止内存泄漏的策略。 我渴望学习您的专业知识并实施必要的更改,以在不影响应用程序性能的情况下保持流畅的用户体验。

预先感谢您的协助!

我的XML代码如下:

<?xml version="1.0" encoding="utf-8"?>
<layout>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/constraintMain"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/black"
    tools:context=".MainActivity">
    <androidx.fragment.app.FragmentContainerView
        android:id="@+id/frameLayout"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="5dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toTopOf="@+id/rvBottomMenu"
        app:layout_constraintEnd_toStartOf="@+id/guidelineCenter"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:name="androidx.navigation.fragment.NavHostFragment"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_speed" />
    <androidx.constraintlayout.widget.Guideline
        android:id="@+id/guidelineCenter"
        android:layout_width="wrap_content"
        android:layout_height="0dp"
        android:orientation="vertical"
        app:layout_constraintBottom_toTopOf="@+id/rvBottomMenu"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintGuide_begin="@dimen/guide_begin_main"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btnExpand"
        android:layout_width="60dp"
        android:layout_height="150dp"
        android:layout_marginStart="16dp"
        android:background="@drawable/bg_btn_expand_shrink"
        app:layout_constraintTop_toBottomOf="@+id/frameLayout"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
    <androidx.appcompat.widget.AppCompatButton
        android:id="@+id/btnMic"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:layout_marginEnd="16dp"
        android:background="@drawable/ic_mic"
        app:layout_constraintTop_toBottomOf="@+id/frameLayout"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/clViewPager"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:padding="5dp"
        android:background="@drawable/bg_border_main"
        android:layout_marginStart="5dp"
        android:layout_marginEnd="10dp"
        android:layout_marginTop="10dp"
        android:layout_marginBottom="10dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toEndOf="@+id/guidelineCenter"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toTopOf="@+id/rvBottomMenu">
        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/nav_host_fragment"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:name="androidx.navigation.fragment.NavHostFragment"
            app:defaultNavHost="true"
            app:navGraph="@navigation/nav_fragments" />

    </androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/rvBottomMenu"
        android:layout_width="wrap_content"
        android:layout_height="100dp"
        android:background="@color/black"
        app:itemIconSize="@dimen/tab_icon_size"
        app:tabIndicatorColor="#EF0C4C"
        app:tabIndicatorHeight="0dp"
        app:tabIndicatorFullWidth="true"
        android:orientation="horizontal"
        tools:listitem="@layout/items_bottom_menu"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

Kotlin 代码如下

  @SuppressLint("SetTextI18n", "UseCompatLoadingForDrawables")
      private suspend fun readSpeed(){
           var speedValue = 1
                while (true){
                    speedValue += 1
                    binding.tvSpeed.text = ""
                    binding.tvSpeed.text = speedValue.toString()
                    delay(200)
                    if (speedValue == 140){
                        speedValue = 1
                    }
                    if (speedValue <= 45){
                        val newDrawable = getApplicationContext().resources.getDrawable(R.drawable.green_bg, null)
                        val transitionDrawable = TransitionDrawable(arrayOf(binding.clSpeedometerMain.background, newDrawable))
                        binding.clSpeedometerMain.background = transitionDrawable
                        transitionDrawable.startTransition(3000)
    
                    }else if(speedValue in 46 .. 80){
                        val newDrawable = getApplicationContext().resources.getDrawable(R.drawable.city_bg, null)
                        val transitionDrawable = TransitionDrawable(arrayOf(binding.clSpeedometerMain.background, newDrawable))
                        binding.clSpeedometerMain.background = transitionDrawable
                        transitionDrawable.startTransition(3000)
                    }else {
                        val newDrawable = getApplicationContext().resources.getDrawable(R.drawable.red_bg, null)
                        val transitionDrawable = TransitionDrawable(arrayOf(binding.clSpeedometerMain.background, newDrawable))
                        binding.clSpeedometerMain.background = transitionDrawable
                        transitionDrawable.startTransition(3000)                }
    
                }
        }

上面的方法调用如下

private fun getSpeed() {
        this.lifecycleScope.launch(Main) {
            readSpeed()
        }
    }

并且 getSpeed() 函数正在片段的 onResume() 回调中调用。

override fun onResume() {
    super.onResume()
    getSpeed()
}
android android-fragments memory-leaks coroutine
1个回答
0
投票

到目前为止,我通过在每个特定时间间隔后刷新片段并通过以编程方式清除应用程序的缓存来解决这个问题。

刷新片段时,生命周期范围正在取消 自动,防止内存泄漏,并且应用程序以 expext 方式运行。 但仍在寻找一些最佳实践

我正在使用的代码如下

在 Vewmodel 中:

 suspend fun clearCache(callback: (Int) -> Unit) {
        var cacheTime = 1
        while (true) {
            cacheTime += 1
            callback(cacheTime)
            delay(1000)
            if (cacheTime == 35) {
                cacheTime = 1
            }

        }
    }

在片段类中:

this.lifecycleScope.launch(Main) {
            speedViewModel.clearCache { cacheTime ->
                clearCache(cacheTime)
            }
        }


private fun clearCache(cacheTime: Int) {
        if (cacheTime == 34) {    
            val fragmentId = findNavController().currentDestination?.id
            findNavController().popBackStack(fragmentId!!, true)
            findNavController().navigate(fragmentId)
        }
    }
© www.soinside.com 2019 - 2024. All rights reserved.