温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

Android开发Viewbinding委托怎么实现

发布时间:2022-06-22 09:31:37 来源:亿速云 阅读:206 作者:iii 栏目:开发技术

本篇内容介绍了“Android开发Viewbinding委托怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!

    从Crash到有意思的源码

    委托模式是软件设计模式中的一项基本技巧。在委托模式中,有两个对象参与处理同一个请求,接受请求的对象将请求委托给另一个对象来处理。

    Kotlin 直接支持委托模式,更加优雅,简洁。Kotlin 通过关键字 by 实现委托。

    上述是kotlin对于委托的释义,Viewbinding委托就是把生成Viewbinding实例的过程交给委托类去完成,然后让使用方可以忽略掉其中的细节,是一种非常好玩的模式了。

    但是由于Viewbinding的特殊性,它其实就会和当前的lifecycle绑定在一起。因为我们要在销毁的情况下把实例重置为空。否则当我们页面重新生成的情况下,就会出现view并不是当前的页面的困扰。

    作者在定义的时候就将Viewbinding委托获取的实例定义为了非空,这里我和我的同事其实是存在一些分歧的,我认为非空其实挺合理的,但是对方并不认为。

    恰巧这种空非空的问题,在实际的使用中就出现了很多不可预期的crash问题。比如说在一个异步操作中获取viewbinding实例然后进行赋值操作,就会出现空指针异常。另外由于使用的是lifecycle的页面销毁方法,如果我们复写了销毁方法之后在设置这个值,也会出现崩溃问题。

    上述问题我在几个我之前参考的库中其实都发现了对应的问题。我参考了Binding,还有之前彭旭说的那个也有类似的情况。

    另外在fragment中,其实问题尤其的明显。因为我们很多时候使用的fragment相关的LifecycleOwner是fragment本身,但是Android官方其实推荐我们使用的是fragment内部的view相关的LifecycleOwner。因为fragment相比较于activity,存在的问题就是多了几个生命周期,比如createView,和onDestroyView。其中出现最多问题的也就是onDestroyView和onDestroy。

    有趣的代码

    接下来我们看下这个作者是如何解决这些奇奇怪怪的问题的哦。

    private class FragmentViewBindingProperty<in F : Fragment, out T : ViewBinding>(
        private val viewNeedInitialization: Boolean,
        viewBinder: (F) -> T,
        onViewDestroyed: (T) -> Unit,
    ) : LifecycleViewBindingProperty<F, T>(viewBinder, onViewDestroyed) {
        private var fragmentLifecycleCallbacks: FragmentManager.FragmentLifecycleCallbacks? = null
        private var fragmentManager: Reference<FragmentManager>? = null
        // 赋值操作
        override fun getValue(thisRef: F, property: KProperty<*>): T {
            val viewBinding = super.getValue(thisRef, property)
            registerFragmentLifecycleCallbacksIfNeeded(thisRef)
            return viewBinding
        }
        private fun registerFragmentLifecycleCallbacksIfNeeded(fragment: Fragment) {
            if (fragmentLifecycleCallbacks != null) return
            val fragmentManager = fragment.parentFragmentManager.also { fm ->
                this.fragmentManager = WeakReference(fm)
            }
            fragmentLifecycleCallbacks = ClearOnDestroy(fragment).also { callbacks ->
                fragmentManager.registerFragmentLifecycleCallbacks(callbacks, false)
            }
        }
        override fun isViewInitialized(thisRef: F): Boolean {
            if (!viewNeedInitialization) return true
            if (thisRef !is DialogFragment) {
                return thisRef.view != null
            } else {
                return super.isViewInitialized(thisRef)
            }
        }
        override fun clear() {
            super.clear()
            fragmentManager?.get()?.let { fragmentManager ->
                fragmentLifecycleCallbacks?.let(fragmentManager::unregisterFragmentLifecycleCallbacks)
            }
            fragmentManager = null
            fragmentLifecycleCallbacks = null
        }
        override fun getLifecycleOwner(thisRef: F): LifecycleOwner {
            try {
                return thisRef.viewLifecycleOwner
            } catch (ignored: IllegalStateException) {
                error("Fragment doesn't have view associated with it or the view has been destroyed")
            }
        }
        // 有意思的代码
        private inner class ClearOnDestroy(
            fragment: Fragment
        ) : FragmentManager.FragmentLifecycleCallbacks() {
            private var fragment: Reference<Fragment> = WeakReference(fragment)
            override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) {
                // Fix for destroying view for case with issue of navigation
                if (fragment.get() === f) {
                    postClear()
                }
            }
        }
    }

    从上述代码上我们可以看出来,其中获取的LifecycleOwner就是我上文说的viewLifecycleOwner。这个就其实已经非常精彩了。

    另外我们可以看下他在内部定义了ClearOnDestroy这个类,然后当onFragmentDestroyed触发的时候调用postClear方法。而这个方法就是解决当我们在Destroyed中还执行了ViewBinding内的对象的操作的空指针问题。

    经典面试题的真实使用场景,Handler.post执行。很多人觉得Handler相关的面试题都是八股文,这次我们就通过这个真是场景来给大家说说这个有意思的问题。

    首先从onFragmentDestroyed方法会执行在Fragment本身的onDestroyView之前,原来我们会在这个方法下执行引用清空的操作。然后当onDestroyView执行的时候就会出现空指针异常了。那么Lifecycle有没有提供一个在onDestroyView之后的方法呢?我们是不是可以考虑自己造一个呢?面试中,我们知道所有生命周期方法都是有主线程Handler来负责调度的,这也就是说活我么可以把生命周期方法认为就是一个Message,当onFragmentDestroyed执行的时候,onDestroyView也已经被添加到主线程的MessageQueue中,这个时候我们在post一个runnable,那么他的排序规则上来说,就必然在onDestroyView之后了。

    另外一些有意思的地方

    这个库另外一个优点就是他同时支持反射和非反射的写法。同时也支持了Activity,Fragment,View,FragmentDialog,ViewHolder等等。反射写法是基于非反射写法的,所以也保证了底层库的一致性。

        //非反射写法
        private val viewBinding by viewBinding(ViewProfileBinding::bind)
        //反射写法
        private val viewBinding: ItemProfileBinding by viewBinding()

    同时他的反射相关的混淆配置文件也非常有意思。

    allowoptimization 指定对象可能会被优化,即使他们被keep选项保留。所指定对象可能会被改变(优化步骤),但可能不会被混淆或者删除。该修饰符只对实现异常要求有用。

    -keep,allowoptimization class * implements androidx.viewbinding.ViewBinding {
        public static *** bind(android.view.View);
        public static *** inflate(...);
    }

    它只会keep实现了ViewBinding的类的bind和inflate方法。因为ViewBinding会将所有的xml转化成一个类实例,如果不删除掉没有实际被调用的类的情况下就会导致dex包变大,大家对于包体积优化都是有追求的吗。然后用了-keep,allowoptimization,这样在混淆的代码优化过程中就可以删除掉没有被调用的ViewBinding类了。

    “Android开发Viewbinding委托怎么实现”的内容就介绍到这里了,感谢大家的阅读。如果想了解更多行业相关的知识可以关注亿速云网站,小编将为大家输出更多高质量的实用文章!

    向AI问一下细节

    免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

    AI