DataBinding原理深度研究

本文详细探讨了Android中的DataBinding原理,包括其在MVVM架构中的作用,如何实现视图与ViewModel的双向绑定,以及Java和Kotlin版本的实现差异。同时,解释了DataBinding布局处理过程,如何避免性能瓶颈,并介绍了生命周期owner的设置及其对性能优化的影响。文章还解答了关于死循环避免、反射使用、tag功能等相关问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

DataBinding原理深度研究

本文链接:https://blog.youkuaiyun.com/feather_wch/article/details/131819166

设计模式

1、目前安卓常用设计模式有MVC、MVP、MVVM

2、MVC

翻遍了整个安卓开发文档,都没有说安卓是mvc的。
但是Android就是MVC的

3、MVVM中VM和ViewModel什么关系?

  1. vm是viewmodel层,和jectpack组件viewmodel是两回事

DataBinding

4、DataBinding的作用是什么?

  1. databinding实现view和vm,双向绑定。

基本使用

5、双向绑定和单向绑定
例如,假设有一个 ViewModel 中的属性 name,我们想将其绑定到一个 TextView 上:

  • 单向绑定示例:android:text="@{user.name}",这会将 name 属性的值展示在 TextView 上,当 name 的值发生变化时,TextView 会自动更新。
  • 双向绑定示例:android:text="@={user.name}",这除了在单向绑定的基础上将 name 的值展示在 TextView 上,还可以在用户编辑 TextView 的内容时,将更改的内容反映回 name 属性。

6、Java版本User类

  1. BaseObservable + Bindable + notifyPropertyChanged(BR.xxx);
package com.personal.tax;
import androidx.databinding.BaseObservable;
import androidx.databinding.Bindable;
public class User extends BaseObservable {
    String name;
    int age;
    public User(String name, int age) {
        this.name = name;
        this.age = age;
    }
    @Bindable
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(BR.name);
    }
    @Bindable
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
        notifyPropertyChanged(BR.age);
    }
}

7、kt版本Student类

  1. get,不能用@Bindable注解,怎么办?
  2. set,@Bindable才会产生BR.xxx
// 不可行
class Student : BaseObservable(){
    var name:String? = null
    // get,不能用@Bindable注解,怎么办?
    get(){
        return field
    }
}
// 可行
class Student{
    val name:ObservableField<String> by lazy{ ObservableField<String>()}
    val age:ObservableField<Int> by lazy{ ObservableField<Int>()}
}

8、DataBinding中报错,95%是布局xml错误
9、设置点击事件

//MyViewModel.java
public void appendNumber(String number){
xxx
}

xml中设置DataBinding的点击事件

Android:click=“@{()->vm.appendNumber(String.valueOf(1))}”

原理

1、DatBinding的布局是如何处理的?

  1. Andorid并不认识layout标签的布局
  2. DataBinding采用的APT注解处理器,会在编译期间进行处理
  3. 将布局拆分为两部分:第一部分layout部分,第二部分是我们实现的布局
  4. 核心点,两个布局用andorid:tag进行匹配

2、DataBinding会将原有布局拆分

  1. 原有布局拆activity_main_view.xml分为activity_main_view-layout.xmlactivity_main_view.xml
  2. DataBinding会根据第一个布局,加载第二个布局
  3. 每个控件用tag标记绑定在一起
    原有布局:activity_main_view.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>
        <variable
            name="user"
            type="com.personal.tax.User" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainViewActivity">

        <TextView
            android:id="@+id/name_txt"
            android:text="@{user.name}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

        <TextView
            android:id="@+id/age_txt"
            android:text="@{user.age}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

拆分后的DataBinding布局

// 目录:build\intermediates\data_binding_layout_info_type_merge\release\out\activity_main_view-layout.xml
<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<Layout directory="layout" filePath="app\src\main\res\layout\activity_main_view.xml"
    isBindingData="true" isMerge="false" layout="activity_main_view"
    modulePackage="com.personal.tax" rootNodeType="androidx.constraintlayout.widget.ConstraintLayout">
    <Variables name="user" declared="true" type="com.personal.tax.User">
        <location endLine="8" endOffset="42" startLine="6" startOffset="8" />
    </Variables>
    <Targets>
        <Target tag="layout/activity_main_view_0"
            view="androidx.constraintlayout.widget.ConstraintLayout">
            <Expressions />
            <location endLine="32" endOffset="55" startLine="11" startOffset="4" />
        </Target>
        <Target id="@+id/name_txt" tag="binding_1" view="TextView">
            <Expressions>
                <Expression attribute="android:text" text="user.name">
                    <Location endLine="18" endOffset="38" startLine="18" startOffset="12" />
                    <TwoWay>false</TwoWay>
                    <ValueLocation endLine="18" endOffset="36" startLine="18" startOffset="28" />
                </Expression>
            </Expressions>
            <location endLine="22" endOffset="54" startLine="16" startOffset="8" />
        </Target>
        <Target id="@+id/age_txt" tag="binding_2" view="TextView">
            <Expressions>
                <Expression attribute="android:text" text="user.age">
                    <Location endLine="26" endOffset="37" startLine="26" startOffset="12" />
                    <TwoWay>false</TwoWay>
                    <ValueLocation endLine="26" endOffset="35" startLine="26" startOffset="28" />
                </Expression>
            </Expressions>
            <location endLine="30" endOffset="54" startLine="24" startOffset="8" />
        </Target>
    </Targets>
</Layout>

拆分后的View xml布局

// 目录:\build\intermediates\incremental\release\mergeReleaseResources\stripped.dir\layout\activity_main_view.xml

<?xml version="1.0" encoding="utf-8"?>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainViewActivity" android:tag="layout/activity_main_view_0" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools">

        <TextView
            android:id="@+id/name_txt"
            android:tag="binding_1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

        <TextView
            android:id="@+id/age_txt"
            android:tag="binding_2"   
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintRight_toRightOf="parent"
            app:layout_constraintTop_toTopOf="parent"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

3、variable的作用是什么?建立绑定

    <data>
        <variable
            name="user"
            type="com.personal.tax.User" />
    </data>

4、DataBindingUtil.setContentView流程

#【DataBindingUtil】
setContentView() {
-->activity.setContentView(layoutId);
-->decorView = activity.getWindow().getDecorView();
-->contentView = decorView.findViewById(android.R.id.content);
-->bindToAddedViews(xxx, contentView, 0, layoutId)
  -->bind(xxx, childView, layoutId)
    -->sMapper.getDataBinder();
      #【DataBinderMapperImpl】- 找到apt生成的
        -->getDataBinder(xxx, View view, int layoutId) 
          -->tag = view.getTag(); // TAG 找到 目标布局
          -->if ("layout/activity_main_view_0".equals(tag)) return new ActivityMainViewBindingImpl(component, view);
            #【ActivityMainViewBindingImpl】
            -->mapBindings(xxx, 3, xxx) // 3代表有3个tag
              -->两个layout对应解析,生成Object[] // 大量消耗性能
            --> 构造方法()
              -->super(xxx, root, 1, (android.widget.TextView)bindings[2], (android.widget.TextView)bindings[1]); // 静态代码块绑定控件
                #【ViewDataBinding】
                -->静态代码块中:OnAttachStateChangeListener()->binding.mRebindRunnable.run() // 控件只要变化就会执行Runnable,100个控件100个Runnable执行
                |———————————————控件改变后触发———————————————————|
                |-->mRebindRunnable.run()
                |  -->executePendingBindings();
                |   #【ActivityMainViewBindingImpl】
                |    -->executeBindings()
                |      -->this.ageTxt.setText(userAge);
                |      -->双向绑定@={user.name} 特殊处理:给控件设置监听器XXXBindingAdapter,一但修改控件,就拿到值,修改到其他控件中。
              -->this.mboundView0 = (androidx.constraintlayout.widget.ConstraintLayout) bindings[0];
              -->this.mboundView0.setTag(null);
              --》this.ageTxt.setTag(null);

        this.nameTxt.setTag(null);
        setRootTag(root);
        // listeners
        invalidateAll();

5、Databinding耗时点:

  1. ActivityMainViewBindingImpl构造方法里面会解析两个布局形成object[]
  2. ViewDataBinding#静态代码块中:控件只要变化就会执行Runnable,100个控件100个Runnable执行
OnAttachStateChangeListener()->binding.mRebindRunnable.run()
// OnAttachStateChangeListener()是View的接口

6、binding.lifecycleOwner = this

lifecycleOwner.getLifecycle().addObserver(this.mOnStartListener);
OnStartListener
@OnLifecycleEvent(Event.ON_START)
->public void onStart() {
-->dataBinding = (ViewDataBinding)this.mBinding.get();
-->dataBinding.executePendingBindings();
-->会遍历绑定请求队列,计算所有待处理的绑定表达式,并将结果应用于View

7、为什么会有待处理的绑定请求?

  1. 优化性能:触发View更新的请求会放到请求队列中,不会立即执行View更新
  2. 调用 dataBinding.executePendingBindings() 时会立即执行所有待处理的绑定请求

问题

1、为什么双向绑定不会死循环

  1. 数据改变->控件A改变->拿到值->控件B改变->不会再设置监听器了

2、type="com.personal.tax.User"这里全类名是使用的反射吗?

  1. 不是!
    3、为什么有的有tag有的没有?
    4、tag有什么用?
    5、为什么DataBinding之前没人用?很卡顿,经过了多年的优化,才好。
    6、DataBinding中有哪些设计模式?
    7、DataBinding中有哪些巧妙的技术?
    8、DataBinding APT如何检查到xml中有layout标签?
<think>好的,我现在需要帮用户解答如何学习安卓应用开发的问题。首先,我得回想用户之前的对话历史,之前他们问过API是否是函数,现在转向安卓开发,可能是一位刚开始学习编程或者想转行的人。用户可能需要一个结构化的学习路径,涵盖从基础到进阶的内容,以及实际项目经验和资源推荐。 首先,我应该确定用户的基础。如果他们完全没有编程经验,可能需要先学习Java或Kotlin。但考虑到现在Kotlin已经是安卓开发的官方语言,可能推荐Kotlin为主。不过有些旧项目可能还在用Java,所以也要提到Java的基础知识。 接下来,安卓开发的基础知识,比如Android Studio的使用、项目结构、四大组件(Activity、Service、Broadcast Receiver、Content Provider)。这些是核心概念,必须详细解释,但可能需要分步骤学习,避免信息过载。 然后,UI设计部分,XML布局和Jetpack Compose。现在Jetpack Compose是趋势,但传统XML布局依然重要,两者都需要覆盖。可能建议先学XML,再过渡到Compose,或者根据用户的学习目标选择。 数据存储部分,SharedPreferences、SQLite、Room数据库,网络请求如Retrofit和Volley,这些是实际开发中常用的技术,需要强调实践,通过项目来巩固。 进阶内容可能包括架构模式(MVVM、MVP)、性能优化、第三方SDK集成、测试等。这些对提升应用质量和开发效率很重要,但可能对新手来说有些难度,需要分阶段学习。 学习资源方面,官方文档、在线课程、书籍、社区论坛都是必不可少的。特别是官方文档,应该强调其重要性,因为这是最权威的资料。 最后,项目实战是关键,建议从小项目开始,逐步增加复杂度,参与开源项目或实际案例,积累经验。同时,持续学习新技术,关注安卓生态的更新,比如每年的Google I/O大会。 需要确保回答结构清晰,分步骤,每个部分有具体的学习点和推荐资源。避免使用过于专业的术语,保持易懂,同时提供足够的深度。另外,用户可能关心就业或实际应用,所以强调项目经验和持续学习的重要性。 总结下来,回答的结构应该是: 1. 确定学习基础(语言选择) 2. 安卓开发基础知识 3. UI开发 4. 数据存储与网络 5. 进阶技能 6. 学习资源推荐 7. 项目实战 8. 持续学习 每个部分需要简明扼要,给出关键点和资源,帮助用户逐步学习。同时,可能需要提醒用户实践的重要性,避免只看不练。</think>--- ### **安卓App开发学习路线(2024年最新实践指南)** --- #### **一、确定技术栈基础** 1. **编程语言优先级** - **Kotlin**(官方首选):掌握`函数式编程`、`扩展函数`、`协程` 示例: ```kotlin // 协程实现异步网络请求 GlobalScope.launch { val data = withContext(Dispatchers.IO) { fetchData() } updateUI(data) } ``` - **Java**(历史项目维护):理解`面向对象`、`泛型`、`多线程` 两者关系:$\text{现代安卓开发} \approx 70\%\text{Kotlin} + 30\%\text{Java}$ 2. **XML基础** - 布局文件编写规范 ```xml <ConstraintLayout> <TextView android:id="@+id/tv_title" android:layout_width="wrap_content" android:layout_height="0dp" app:layout_constraintHeight_percent="0.3" /> </ConstraintLayout> ``` --- #### **二、Android开发核心知识体系** 1. **开发环境搭建** - Android Studio:配置`Gradle 8.0+`、`Kotlin DSL` - 模拟器优化:使用`ARM镜像`加速、开启`硬件加速` 2. **四大组件深度解析** | 组件 | 生命周期关键方法 | 典型应用场景 | |-------------------|---------------------------------|------------------------------| | **Activity** | `onCreate()` → `onDestroy()` | 用户交互界面 | | **Service** | `onStartCommand()` | 后台音乐播放 | | **Broadcast** | `onReceive()` | 监听网络状态变化 | | **ContentProvider** | `query()`/`insert()` | 跨应用数据共享 | 3. **现代UI开发技术栈** - **传统XML布局** → **Jetpack Compose**(声明式UI框架) 代码对比: ```kotlin // Compose实现按钮 Button( onClick = { /* 点击事件 */ }, modifier = Modifier.padding(16.dp) ) { Text("Submit") } ``` --- #### **三、数据与网络层关键技术** 1. **本地数据存储方案** - **Room数据库**(SQLite抽象层) ```kotlin @Dao interface UserDao { @Query("SELECT * FROM user") fun getAll(): Flow<List<User>> } ``` - **DataStore**(替代SharedPreferences) ```kotlin val dataStore: DataStore<Preferences> = context.createDataStore(name = "settings") ``` 2. **网络通信实践** - **Retrofit + Kotlin协程** ```kotlin interface ApiService { @GET("users/{id}") suspend fun getUser(@Path("id") userId: String): User } ``` - **JSON解析**:`kotlinx.serialization`或`Gson` --- #### **四、进阶技能图谱** 1. **架构模式选择** - **MVVM**(官方推荐): $$ \text{View} \leftrightarrow \text{ViewModel} \leftrightarrow \text{Model} $$ 实现工具:`LiveData`、`ViewModel`、`DataBinding` 2. **性能优化关键指标** | 优化方向 | 工具 | 目标 | |-----------------|---------------------|---------------------------| | 内存泄漏 | LeakCanary | 检测Activity/Fragment泄漏 | | 渲染耗时 | Profile GPU Rendering | 确保每帧渲染≤16ms | | 启动速度 | Firebase Performance | 冷启动时间<1.5秒 | 3. **跨平台技术探索** - **KMM**(Kotlin Multiplatform Mobile) - **Flutter**(UI跨平台方案) --- #### **五、学习资源推荐** 1. **官方学习路径** - [Android开发者官网](https://developer.android.com/) - **Codelabs实践课程**:手把手完成真实项目 2. **高质量课程** - Udacity安卓纳米学位 - 谷歌官方《Android Basics in Kotlin》 3. **工具链精通** - **调试工具**:Android Profiler、Layout Inspector - **版本控制**:Git + GitHub(学习分支管理策略) --- #### **六、项目实战策略** 1. **分阶段项目设计** | 阶段 | 项目类型 | 技术要点 | |--------|-------------------------|-------------------------------| | 初级 | 天气预报App | API调用、RecyclerView | | 中级 | 社交App原型 | Firebase集成、实时聊天 | | 高级 | 电商App完整架构 | 支付SDK、性能优化、CI/CD | 2. **开源协作** - 参与`Android Open Source Project (AOSP)` - 在GitHub贡献代码(如修复知名库的issue) --- #### **七、持续学习机制** 1. **技术更新追踪** - 订阅Google官方博客 - 关注每年的`Google I/O`大会新技术(如Compose新特性) 2. **开发者社区** - Stack Overflow高频问题研究 - 参加本地GDG(Google Developer Group)活动 --- ### **学习效果评估模型** $$ \text{能力指数} = 0.4 \times \text{代码量} + 0.3 \times \text{项目复杂度} + 0.2 \times \text{架构理解} + 0.1 \times \text{新技术敏感度} $$ **建议**:每月完成至少2000行有效代码,参与1次Code Review --- 通过系统化学习+高强度实践,通常6-12个月可达到**中级安卓工程师**水平。关键路径在于:**深度理解系统原理 + 商业级项目锤炼**。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

猎羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值