Android ViewPager2+Fragment viewModelScope问题

核心代码

class MainActivity : AppCompatActivity() {
    private lateinit var tabLayout: TabLayout
    private lateinit var viewPager2: ViewPager2

    private val titles = mutableListOf<String>()
    private val fragments = mutableListOf<MyFragment>()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        tabLayout = findViewById(R.id.tab_layout)
        viewPager2 = findViewById(R.id.view_pager2)
        for (i in 'A'..'F') {
            titles.add(i.toString())
            fragments.add(MyFragment.newInstance(i.toString()))
        }
        viewPager2.adapter = MyAdapter(this, fragments)
        viewPager2.offscreenPageLimit = 1

        TabLayoutMediator(
            tabLayout,
            viewPager2,
            object : TabLayoutMediator.TabConfigurationStrategy {
                override fun onConfigureTab(
                    tab: TabLayout.Tab,
                    position: Int
                ) {
                    tab.text = titles[position]
                }
            }).attach()
    }

    class MyAdapter(fragmentActivity: FragmentActivity, val fragments: List<MyFragment>) :
        FragmentStateAdapter(fragmentActivity) {

        override fun createFragment(position: Int): Fragment {
            return fragments[position]
        }

        override fun getItemCount(): Int {
            return fragments.size
        }
    }
}
class MyFragment : Fragment() {
    private val viewModel: MyFragmentViewModel by viewModels()
    private var title = ""

    companion object {
        fun newInstance(title: String) =
            MyFragment().apply {
                arguments = Bundle().apply {
                    putString("title", title)
                }
            }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        arguments?.let {
            title = it.getString("title") ?: ""
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        Log.e("TAG", "Fragment ${title} 创建")
        return inflater.inflate(R.layout.fragment_my, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val textView = view.findViewById<TextView>(R.id.text_view)
        textView.text = "Fragment $title"
        viewModel.load(title)
    }

    override fun onDestroyView() {
        super.onDestroyView()
        Log.e("TAG", "Fragment ${title} 销毁")
    }
}
class MyFragmentViewModel : ViewModel() {

    fun load(title: String) {
        Log.e("TAG", "$title 开始加载")
        Log.e("TAG", "$title viewModelScope是否可用:${viewModelScope.isActive}")
        viewModelScope.launch {
            delay(1000L)
            Log.e("TAG", "$title 结束加载")
        }
    }
}

问题

首先点击 Fragment A,会执行 Fragment 的 ViewModel 的 load() 方法。接着点击 Fragment F,Fragment A 会被销毁掉,然后再点击 Fragment A,这时 Fragment A会被重新创建,而对应的 ViewModel 的 isActive 却是 false,导致 viewModelScope.launch 无法执行。

解决一

使用 Activity 的ViewModel。

class MyFragment : Fragment() {
    private val viewModel: MyFragmentViewModel by activityViewModels()
}

解决二

使用 Fragment 中的 lifecycleScope。

class MyFragment : Fragment() {
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        val textView = view.findViewById<TextView>(R.id.text_view)
        textView.text = "Fragment $title"
        viewModel.load(lifecycleScope, title)
    }
}

解决三

不缓存实例,每次都重新创建 Fragment。

class MainActivity : AppCompatActivity() {
    private val fragments = mutableListOf<() -> Fragment>()

    override fun onCreate(savedInstanceState: Bundle?) {
        for (i in 'A'..'F') {
            titles.add(i.toString())
            fragments.add({ MyFragment.newInstance(i.toString()) })
        }
    }

    class MyAdapter(fragmentActivity: FragmentActivity, val fragments: List<() -> Fragment>) :
														    FragmentStateAdapter(fragmentActivity) {

        override fun createFragment(position: Int): Fragment {
            return fragments[position].invoke()
        }

        override fun getItemCount(): Int {
            return fragments.size
        }
    }
}
Logo

火山引擎开发者社区是火山引擎打造的AI技术生态平台,聚焦Agent与大模型开发,提供豆包系列模型(图像/视频/视觉)、智能分析与会话工具,并配套评测集、动手实验室及行业案例库。社区通过技术沙龙、挑战赛等活动促进开发者成长,新用户可领50万Tokens权益,助力构建智能应用。

更多推荐