iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android开发Viewbinding委托实例详解
  • 863
分享到

Android开发Viewbinding委托实例详解

2024-04-02 19:04:59 863人浏览 独家记忆
摘要

目录背景从Crash到有意思的源码有趣的代码另外一些有意思的地方结尾背景 前一阵子我们在使用viewbinding的委托的时候碰到了点crash问题,然后发现了一个比较有意思的解决方

背景

前一阵子我们在使用viewbinding的委托的时候碰到了点crash问题,然后发现了一个比较有意思的解决方案,就和大家展开聊聊。

另外一点就是我后面打算将Kotlin extensions这个插件统一移除掉。

估计大家应该对Viewbinding的委托应该都有一定的了解,好几个大佬分享过类似的文章,但是大佬们的代码貌似也有一阵子都没有维护了,所以我找到了一个外国大佬写的仓库,其实应该算是一个相对来说比较稳定的库了,而且也一直处于一个持续更新迭代的状态。

仓库地址 ViewBindingPropertyDelegate

从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委托的资料请关注编程网其它相关文章!

--结束END--

本文标题: Android开发Viewbinding委托实例详解

本文链接: https://www.lsjlt.com/news/152200.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android开发Viewbinding委托实例详解
    目录背景从Crash到有意思的源码有趣的代码另外一些有意思的地方结尾背景 前一阵子我们在使用viewbinding的委托的时候碰到了点crash问题,然后发现了一个比较有意思的解决方...
    99+
    2024-04-02
  • Android开发Viewbinding委托怎么实现
    本篇内容介绍了“Android开发Viewbinding委托怎么实现”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!从Crash到有意思的源码...
    99+
    2023-07-02
  • Android开发之Kotlin委托的原理与使用详解
    目录前言一、接口/类委托二、属性委托三、延迟委托四、观察者委托五、Map委托总结前言 在设计模式中,委托模式(Delegate Pattern)与代理模式都是我们常用的设计模式(Pr...
    99+
    2023-03-23
    Kotlin委托原理 Kotlin委托使用 Android Kotlin委托 Kotlin委托
  • JavaScript中事件委托的示例详解
    目录事件流事件委托结尾大家好,我是前端西瓜哥。今天我们来认识一下事件委托。 所谓事件委托,就是将原本应该在当前元素绑定的事件,放到它的祖先元素上,让祖先元素来委托处理。 事件流 事件...
    99+
    2024-04-02
  • js事件流、事件委托与事件阶段实例详解
    目录前言1、事件流2、事件处理程序3、事件对象4、跨浏览器事件处理5、事件委托总结前言 JavaScript 与 HTML 的交互是通过事件实现的,事件代表文档或浏览器窗口中某个有意...
    99+
    2024-04-02
  • C#开发中事件与委托机制的示例分析
    这篇文章给大家分享的是有关C#开发中事件与委托机制的示例分析的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。概述C#中的委托类似于C或C++中的函数指针。使用委托使程序员可以将方法引用封装在委托对象内。然后可以将该...
    99+
    2023-06-17
  • JavaScript事件的委托(代理)的用法示例详解
    目录简介示例:事件委托写法1:事件委托写法2:每个子元素都绑定事件示例:新增元素写法1:事件委托写法2:每个子元素都绑定事件简介 说明 本文用示例介绍JavaScript中的事件(E...
    99+
    2024-04-02
  • Android开发之Kotlin委托的原理与使用方法是什么
    今天小编给大家分享一下Android开发之Kotlin委托的原理与使用方法是什么的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧...
    99+
    2023-07-05
  • Android Kotlin开发实例(Hello World!)及语法详解
    Android Kotlin开发实例及语法详解前言Kotlin是一种在 Java虚拟机上执行的静态型别编程语言,它主要是由俄罗斯圣彼得堡的JetBrains开发团队所发展出来的编程语言。该语言有几个优势 简洁 它大大减少你需要写的样板代码...
    99+
    2023-05-31
    android kotlin roi
  • Android开发Jetpack组件WorkManager用例详解
    目录一、简介二、导入三、基本使用3.1 定义后台任务3.2 配置任务运行条件3.2.1 只需执行一次的任务3.2.2 周期性执行的任务3.3 将任务传给 WorkManager四、高...
    99+
    2024-04-02
  • Android开发Jetpack组件DataBinding用例详解
    目录简介使用方式1. build.gradle 中添加 kapt,并启用dataBinding2.修改布局文件,添加 layout 和 data 标签3.使用 DataBinding...
    99+
    2024-04-02
  • Android NFC开发详解 总结和NFC读卡实例解析
    文章目录 前言一、什么是NFC?二、基础知识1.什么是NDEF?2.NFC技术的操作模式3.标签的技术类型4.实现方式的分类5.流程 三、获取标签内容1.检查环境2.获取NFC标签2.1 Manifest中注册的方式获取Tag2...
    99+
    2023-08-24
    android 物联网 嵌入式硬件 android studio java
  • Android开发ScaleAnimation详解
    ScaleAnimation是Android中的一种动画效果,用于对View进行缩放操作。通过ScaleAnimation我们可以实...
    99+
    2023-09-08
    Android
  • Android开发Kotlin实现圆弧计步器示例详解
    目录效果图定义控件的样式自定义StepView绘制文本坐标Android获取中线到基线距离效果图 定义控件的样式 看完效果后,我们先定义控件的样式 <!-- 自定义View的...
    99+
    2024-04-02
  • android studio开发app实例
    以下是一个简单的Android Studio开发App的实例: 打开Android Studio,并创建一个新项目。 选择一个适当的应用程序名称和包名称,然后选择目标API级别和默认Activity的模板。 在MainActivity...
    99+
    2023-09-05
    android studio android ide
  • Android开发之AppWidget详解
    Android通知系统是它的一大特色,而其中,AppWidget是其中一个亮点。在开发应用的中,很多时候可以为其添加一个AppWidget显示在桌面中,及时方便的与用户进行 交互。这...
    99+
    2024-04-02
  • Maven聚合开发实例详解
    目录一、Maven聚合开发_继承关系二、Maven聚合案例1. 搭建dao模块2. 搭建service模块  3. 搭建web模块 4. 运行项目一、M...
    99+
    2023-03-20
    Maven聚合开发 Maven聚合
  • Android开发使用RecyclerView添加点击事件实例详解
    目录引言一、RecyclerView基本使用1. 添加适配器Adapter2. 创建列表的每个项的item_layout.xml文件3. 在activity中使用二、Recycler...
    99+
    2024-04-02
  • Android startActivityForResult实例详解
    Android startActivityForResult实例详解startActivityForResult用于两个activity之间的数据传递,Activity1传值给Activity2,Activity2再返回值给Activity...
    99+
    2023-05-31
    android startactivityforresult art
  • Android 实例开发一个学生管理系统流程详解
    目录效果演示实现功能总览代码登录与忘记密码界面 一、添加布局文件二、添加标题文字三、绑定适配器注册界面 一、创建两个Drawable文件 二、将其添加数组内三、动态变化背景考勤界面 ...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作