You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何处理Fragment中WebView的屏幕旋转崩溃问题

屏幕旋转导致DialogFragment崩溃的解决方案

核心问题

Android屏幕旋转时,系统会销毁并重建Fragment,要求Fragment必须有无参构造函数。直接使用带参数的自定义构造函数会导致重建时无法实例化Fragment,引发崩溃。setRetainInstance已被弃用,官方推荐用ViewModel来管理跨配置变更的数据和对象。

具体实现步骤

1. 改造WebViewFragment,移除自定义构造函数,改用newInstance传递可序列化参数

删除Fragment的带参构造函数,用静态newInstance方法传递可序列化的参数(比如url)到Fragment的arguments Bundle中:

class WebViewFragment : DialogFragment() {
    companion object {
        private const val ARG_URL = "arg_url"
        private const val TAG = "WebViewFragment"

        // 只传递可序列化的url参数
        fun newInstance(url: String): WebViewFragment {
            return WebViewFragment().apply {
                arguments = Bundle().apply {
                    putString(ARG_URL, url)
                }
            }
        }
    }

    // 其他Fragment相关代码...
}

2. 创建ViewModel持有TokenProvider和业务逻辑

ViewModel的生命周期独立于Fragment的配置变更,可安全持有TokenProvider这类不可序列化的对象。同时用SavedStateHandle保存url,确保参数在重建时不丢失:

class WebViewViewModel(
    private val tokenProvider: TokenProvider,
    private val savedStateHandle: SavedStateHandle
) : ViewModel() {
    private val ARG_URL = "arg_url"

    // 从SavedStateHandle获取持久化的url
    val url: String? get() = savedStateHandle[ARG_URL]

    // 封装WebView需要的Token操作
    fun fetchToken(): String? {
        return tokenProvider.getToken()
    }
}

// 自定义ViewModelFactory,用于传入TokenProvider和SavedStateHandle
class WebViewViewModelFactory(
    private val tokenProvider: TokenProvider,
    private val savedStateHandle: SavedStateHandle
) : ViewModelProvider.Factory {
    @Suppress("UNCHECKED_CAST")
    override fun <T : ViewModel> create(modelClass: Class<T>): T {
        if (modelClass.isAssignableFrom(WebViewViewModel::class.java)) {
            return WebViewViewModel(tokenProvider, savedStateHandle) as T
        }
        throw IllegalArgumentException("Unknown ViewModel class")
    }
}

3. 在Fragment中初始化ViewModel,关联业务逻辑

在Fragment的onCreate方法中,通过自定义Factory创建ViewModel,同时从arguments中取出url存入SavedStateHandle:

class WebViewFragment : DialogFragment() {
    private lateinit var viewModel: WebViewViewModel
    private lateinit var tokenProvider: TokenProvider

    // 新增方法让外部传入TokenProvider
    fun setTokenProvider(provider: TokenProvider) {
        this.tokenProvider = provider
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        val url = arguments?.getString(ARG_URL)
        val savedStateHandle = SavedStateHandle().apply {
            set(ARG_URL, url)
        }
        // 用自定义Factory创建ViewModel
        viewModel = ViewModelProvider(
            this,
            WebViewViewModelFactory(tokenProvider, savedStateHandle)
        )[WebViewViewModel::class.java]
    }

    // 示例:WebView加载时使用ViewModel中的参数
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        val webView = WebView(requireContext())
        viewModel.url?.let { webView.loadUrl(it) }
        // 调用TokenProvider的逻辑
        viewModel.fetchToken()?.let { /* 处理Token,比如设置请求头 */ }
        return webView
    }

    fun showWithResult(): Result {
        show(requireActivity().supportFragmentManager, TAG)
        return Result.SHOWN
    }
}

4. 改造showWebView方法,正确初始化Fragment和参数

在类库的showWebView方法中,先创建Fragment实例,传入url,再设置TokenProvider,最后显示:

private fun showWebView(context: Context, tokenProvider: TokenProvider, url: String): Result {
    val fragmentActivity = context as FragmentActivity
    val fragment = WebViewFragment.newInstance(url)
    // 设置TokenProvider
    fragment.setTokenProvider(tokenProvider)
    return fragment.showWithResult()
}

替代方案:简化处理(不推荐复杂场景)

如果上述ViewModel方案过于繁琐,可根据场景选择:

  • 若TokenProvider结构简单,实现Parcelable接口,通过newInstance方法传入Bundle(但不适合持有复杂状态的对象);
  • 将TokenProvider设计为单例/全局对象,直接在Fragment中调用(需注意内存泄漏和线程安全)。

关键注意点

  • 永远不要给Fragment写带参构造函数,所有可序列化参数必须通过arguments传递;
  • ViewModel仅用于持有跨配置变更的数据/对象,不要持有Context或View引用,避免内存泄漏;
  • 不可序列化的对象(如TokenProvider)必须通过ViewModel的Factory传入,确保在Fragment重建时不会丢失。

内容的提问来源于stack exchange,提问作者Ayohaych

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.23 18:12:51