gzu-liyujiang / AndroidPicker

安卓选择器类库,包括日期及时间选择器(可用于出生日期、营业时间等)、单项选择器(可用于性别、民族、职业、学历、星座等)、二三级联动选择器(可用于车牌号、基金定投日期等)、城市地址选择器(分省级、地市级及区县级)、数字选择器(可用于年龄、身高、体重、温度等)、日历选日期择器(可用于酒店及机票预定日期)、颜色选择器、文件及目录选择器、图片选择器等……WheelPicker/DatePicker/TimePicker/OptionPicker/NumberPicker/LinkagePicker/AddressPicker/CarPlatePicker/CalendarPicker/ColorPicker/FilePicker/ImagePicker etc.
Other
6.44k stars 1.46k forks source link

存在 windowLeaked 的问题 #294

Open jjamesllong opened 2 years ago

jjamesllong commented 2 years ago

bug 描述

注意到此项目的所有 dialog 都继承于 Dialog 而非 DialogFragment,所以我在配置更改(如屏幕旋转)时对正在显示的 dialog 主动执行 dismiss(),然而在屏幕旋转时 windowLeaked 的问题依然存在,可能和 dialog 内部维护的 view 相关。

配置代码

对 dialog 的配置代码如下:

// 在 **fragment** 中
private fun configureDatePicker() {
        val datePicker = DatePicker(requireActivity())
        datePicker.wheelLayout.setRange(
            DateEntity.target(1970, 1, 1),
            DateEntity.target(3000, 12, 31),
            DateEntity.today()
        )
        datePicker.setOnDatePickedListener { year, month, day ->
            viewModel.refreshDate(Date(year, month, day))
        }
        val observer = object : DefaultLifecycleObserver {
            override fun onDestroy(owner: LifecycleOwner) {
                super.onDestroy(owner)
                if (datePicker.isShowing) {
                    datePicker.dismiss()
                }
                owner.lifecycle.removeObserver(this)
            }
        }
        viewLifecycleOwner.lifecycle.addObserver(observer)
        datePicker.show()
    }
liyujiang-gzu commented 2 years ago

@aarthurddragon 本项目的所有窗体的确是基于Dialog的,BaseDialog只简单的复写了dismiss在里面try……catch来避免闪退,屏幕旋转时Dialog所在的Activity销毁重建了,导致windowLeaked。将Dialog迁移到DialogFragment需要联动改动很多,暂时没计划迁移。你可以试试将条件改为:

Activity activity = requireActivity();
...
if (activity.isFinishing() || activity.isDestroyed()) {
    return;
}
dialog.dismiss();
jjamesllong commented 2 years ago

我取消了使用 lifecycle 组件,使用 lifycycle 组件处理 dialog 的消失问题反而出现了更多问题,新的配置代码如下,但还是存在 windowLeaked 的问题。

class MyFragment : Fragment() {

    var datePicker: DatePicker? = null

    private fun configureDatePicker() {
        val datePicker = DatePicker(requireActivity())
        // ...
        // 省略无关的配置代码
        // ...
        this.datePicker = datePicker
        datePicker.show()
    }

    override fun onDestroyView() {
        datePicker?.let { if (it.isShowing) it.dismiss() }
        datePicker = null
    }
}

现在我主动在 onDestroyView() 中判断 datePicker 是否显示,如果显示,则执行 dismiss(),并且主动将引用赋值为 null,但是在屏幕旋转时依然存在 windowLeaked 的问题。日志如下(包名已使用 ...... 省略):

E/WindowManager: android.view.WindowLeaked: Activity ...... has leaked window android.view.View{18b9bd3 V.ED..... ........ 0,0-1080,2136} that was originally added here
        at android.view.ViewRootImpl.<init>(ViewRootImpl.java:536)
        at android.view.WindowManagerGlobal.addView(WindowManagerGlobal.java:349)
        at android.view.WindowManagerImpl.addView(WindowManagerImpl.java:94)
        at com.github.gzuliyujiang.dialog.BottomDialog.addMaskView(BottomDialog.java:101)
        at com.github.gzuliyujiang.dialog.BottomDialog.onShow(BottomDialog.java:61)
        at android.app.Dialog$ListenersHandler.handleMessage(Dialog.java:1434)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7035)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:493)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:937)
liyujiang-gzu commented 2 years ago
 at com.github.gzuliyujiang.dialog.BottomDialog.addMaskView(BottomDialog.java:101)
 at com.github.gzuliyujiang.dialog.BottomDialog.onShow(BottomDialog.java:61)

怕不是dismiss的问题吧,你看datePicker.show()是否是在onViewCreate里调用?