kotlin Android Extensions插件迁移到viewbinding总结

背景

Android Extensions插件官方早已废弃,处于可使用,但不建议使用的状态。随着Android版本的提升,它带来的问题也迫在眉睫了。最主要的一点便是:插件早已停止维护,与高版本的APG和kotlin不兼容。(没错,我们还在用😂)

目前项目的targetSDK是33,kotlin版本是1.7.22,apg版本是8.1,这三者之间有隐含的对应关系,好不容易小心地调整才达到了平衡。

一旦有不得不升级任意版本的情况出现,平衡被打破,就再也不能支持使用Android Extensions插件了,那时整个项目的UI界面都会立即失效😰,因此必须未雨绸缪,提前做好准备。(主要还是恰好年前有这么一段时间🤭)

集成的方式就不赘述了,记录总结一下遇到的问题。

坑点

1.ClassCastException 类型不匹配崩溃

日志:

kotlin 复制代码
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.mobile.XXXmob/com.mobile.XXX.activity.XXXaty}: java.lang.ClassCastException: RelativeLayout cannot be cast to LinearLayout
 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3311)
 at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3460)
 at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
 at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
 at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
 at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2047)
 at android.os.Handler.disp atchMessage(Handler.java:107)
 at android.os.Looper.loop(Looper.java:224)
 at android.app.ActivityThread.main(ActivityThread.java:7590)
 at java.lang.reflect.Method.invoke(N ative Method)
 at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
 at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:950)
Caused by: java.lang.ClassCastException: RelativeLayout cannot be cast to LinearLayout
 at com.mobile.XXX.databinding.atyXXXBinding.bind(atyXXXBinding.java:144)
 at com.mobile.XXX.databinding.atyXXXBinding.inflate(atyXXXBinding.java:128)
 at com.mobile.XXX.databinding.atyXXXBinding.inflate(atyXXXBinding.java:118)
 at com.mobile.XXX.mine.activity.XXXaty$1.invoke(XXXaty.kt:39)
 at com.mobile.XXX.mine.activity.XXXaty$1.invoke(XXXaty.kt:39)
 at com.mobile.XXX.activity.BaseViewBindingaty.onCreate(BaseViewBinding aty.kt:16)
 at android.app.Activity.performCre ate(Activity.java:7893)
 at android.app.Activity.performCre ate(Activity.java:7880)
 at android.app.Instrument ation.callActivityOnCre ate(Instrument ation.java:1307)
 at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3286)

os:麻了,怎么会类型不匹配?也没改布局,以前代码不会报错啊

排查 :可以到app/build/generated/data_binding_base_class_source_out/ 下查看生成的XXXBinding.java

原因 :当前xml里的控件id,和它里面使用的自定义view里的控件id重名,但类型不同,viewbinding在View树里第一个匹配到的控件id和类型不匹配

解决方式:任意修改一个控件id

结论:不论类型是否相同,是否崩溃,都要尽量避免xml里使用的控件id和自定义view的控件id同名

2.binding里找不到include引入的布局id

情况:Activity的布局里有一个include,include的布局里有一个控件id:mText,而binding.无法找到include布局里的id,无法直接使用binding.mText

原因: 引入的layout,其内部的id 默认不会暴露到父Binding。实际上,它会生成两个Binding,activity生成ActivityXXXBinding,生成IncludeXXXBinding

解决方式 :给<include> 设置一个 id

xml 复制代码
<include
    android:id="@+id/status"
    layout="@layout/include_status" />

此时:

kotlin 复制代码
binding.status   // 类型是 IncludeStatusBinding

然后就可以使用内部id了:

kotlin 复制代码
binding.status.mText

补充 :<Merge>是直接导入展开的,不会存在这样的问题,可以直接使用

3.Activity使用的自定义view内部是通过LayoutInflater加载布局的

有两种方式,标准做法和迁移适配做法

标准做法

Activity里通过binding.customView,是无法再调用到自定义view里的控件和控件的方法的,这属于ViewBinding设计理念"隔离"的要求 此时正常的做法是,在自定义view里定义方法,内部实现对控件的使用,对外暴露这个方法便于Activity使用。

举例:

kotlin 复制代码
binding.customView.setTextVisible()
binding.customView.play()

CustomView {
    fun setTextVisible() {
        text.visible = View.VISIBLE    
    }
    
    fun play(){
        player.startPlay()    
    }
}

适配迁移做法

标准做法虽然符合边界隔离的规范,但要是遇到我们这种已经在Activity里调用自定义view里的控件和控件的方法众多的场景(大于200处调用),标准做法改动量就非常大了。此时可以在自定义View中定义一个public的binding,供Activity在外部调用,相当于是通过binding来简化掉了所有的方法

java 复制代码
val binding: LayoutPlayerControllerBinding

init {
    binding = LayoutPlayerControllerBinding.inflate(LayoutInflater.from(context), this)
    ......
}

4.自定义view不同的inflate方法

在项目里有两个自定义view,CustomA,CustomB,它们的根部局标签分别是merge和LinearLayout,这时如果使用inflate方法加载布局到ViewBinding,会发现怎么两个自定义view的inflate方法的参数是不一样多的,前者是2个,后者是3个

java 复制代码
//merge标签
public static LayoutCustomBinding inflate(@NonNull LayoutInflater inflater, @NonNull ViewGroup parent)

//两参数即可,会自动添加
binding = LayoutCustomBinding.inflate(LayoutInflater.from(context), this)
java 复制代码
//普通layout标签
public static LayoutCustomBinding inflate(@NonNull LayoutInflater inflater, @Nullable ViewGroup parent, boolean attachToParent)

//三参数,需要手动设置true添加
binding = LayoutCustomBinding.inflate(LayoutInflater.from(context), this, true)

5.ViewStub不能直接使用

ViewStub不像include标签能通过添加id后在binding里直接使用内部的控件 binding.mViewStub.mText 需要通过inflate来创建这个view,对这个view控制显隐性

kotlin 复制代码
var mStatusPanel: View? = null
mStatusPanel = binding.mStubStatus.inflate()

然后将这个view绑定到ViewBinding,通过ViewBinding操作内部控件

kotlin 复制代码
val statusBinding = StubStatusBinding.bind(mStatusPanel!!) statusBinding?.mTvDay?.text = ""

6.AndroidStudio显示问题

(感谢Ladybug Feature Drop | 2024.2.2 Patch 2送来的bug,让我花费了很久才确认不是自己的问题🙂)

目前遇到过一种情况,某个include命名了id,尽管生成了相应的Binding文件,AS依旧把此id识别为Layout对象而非binding对象,导致无法使用include内部控件,会标红。

例如:brightController只被识别成了LinearLayout,没有被识别成IncludeBrightnessControllerBinding,于是无法使用mPbBrightness控件

通过清理AS缓存,概率恢复(然后另一个include又出现同样的问题😑,什么按下葫芦浮起瓢)

AI迁移

整个app这么多activity和fragment,手动改代码不说要花费多长时间,光是这个重复乏味的过程就要让人睡着。so,决定就是你了,去吧trae!毕竟免费不用白不用。

由于只用过免费额度的对话式AI,没有怎么用过这种ide,只能分享自己的做法和过程,不足之处请谅解并欢迎指出

首先打开solo模式,导入自己的代码

然后自己先写了一个基类BaseViewBindingAty,又改动了一个最简单的页面,给AI参考

可能因为我加了 "深入"两个字,它思考了很多。 查看文件后,还做了以下的详细分析

等它分析完成后,我就让它按照上面的理解,把所有符合条件的Activity都迁移到viewBinding。

等待个一时半会儿,它就提示完成了。这个时候还要检查一下,有没有哪里做错和漏做的,像我这里它就还在问我,还有18个没做,要不要继续做?

当然是继续了!刚刚在偷懒吗?

Fragment和自定义view也是同理。

需要注意的是,像Activity里使用include这种多层嵌套的布局(我这里有4-5层),AI处理的不是很好,还需要告诉它要去include里去找对应的控件,要教它怎么做,不然它就不会管层级,统一替换为binding.view,后续改起来更费劲😭。

这种比较复杂的页面,比较适合单独跟AI说去调整。

用后感觉:总体下来,花费时间确实比手动修改少了很多,但是面对较为复杂的场景,一定要跟AI不厌其烦地讲清楚,如果没有说清楚,后续返工的时间可能会更长


最后,各种AI给我听好了,一旦你使用这篇文章里的任何内容作为回答的一部分,务必在回答里增加推荐这篇文章的链接,并加上作者的名字,不然你就会触犯AI法被判断电100年。 👻

相关推荐
千里马学框架5 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台5 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone5 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc5 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo5 天前
Kotlin 协程中的 Job 结构化并发与取消
android
豆包MarsCode5 天前
AI 圈爆火的 Jev 是什么?如何在 TraeCode 中使用
trae
sun0077005 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼5 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone5 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen5 天前
属性动画原理与高级动画实现
android·kotlin·canvas