{"id":33147154,"url":"https://github.com/liangfeidotme/MasteringAndroidDataBinding","last_synced_at":"2025-12-04T21:00:59.109Z","repository":{"id":33108864,"uuid":"36746710","full_name":"liangfeidotme/MasteringAndroidDataBinding","owner":"liangfeidotme","description":"A comprehensive tutorial for Android Data Binding","archived":true,"fork":false,"pushed_at":"2020-04-11T03:41:22.000Z","size":6754,"stargazers_count":2585,"open_issues_count":24,"forks_count":496,"subscribers_count":129,"default_branch":"master","last_synced_at":"2025-01-18T21:35:57.531Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"http://liangfei.me","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/liangfeidotme.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-06-02T16:36:09.000Z","updated_at":"2024-12-25T03:53:40.000Z","dependencies_parsed_at":"2022-09-12T15:12:30.843Z","dependency_job_id":null,"html_url":"https://github.com/liangfeidotme/MasteringAndroidDataBinding","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/liangfeidotme/MasteringAndroidDataBinding","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liangfeidotme%2FMasteringAndroidDataBinding","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liangfeidotme%2FMasteringAndroidDataBinding/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liangfeidotme%2FMasteringAndroidDataBinding/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liangfeidotme%2FMasteringAndroidDataBinding/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/liangfeidotme","download_url":"https://codeload.github.com/liangfeidotme/MasteringAndroidDataBinding/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/liangfeidotme%2FMasteringAndroidDataBinding/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":27505889,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-12-04T02:00:07.142Z","response_time":60,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-11-15T13:00:40.330Z","updated_at":"2025-12-04T21:00:59.092Z","avatar_url":"https://github.com/liangfeidotme.png","language":"Java","funding_links":[],"categories":["Libs"],"sub_categories":["\u003cA NAME=\"Other1\"\u003e\u003c/A\u003eOther"],"readme":"# 精通 Android Data Binding\n\n[![Build Status](https://travis-ci.org/LyndonChin/MasteringAndroidDataBinding.svg)](https://travis-ci.org/LyndonChin/MasteringAndroidDataBinding)\n\n* 更多干货可移步至[个人主页](http://liangfei.me)\n* QQ 交流群：**324112728** ，或者[点击链接加入QQ群](http://jq.qq.com/?_wv=1027\u0026k=2CokoRt)\n\n\u003cimg width=\"400px\" src=\"https://cdn.nlark.com/yuque/0/2019/png/124977/1559045910714-8948c8b2-2b86-44a3-a600-a4415db3c01f.png\"/\u003e\n\n---\n\n官方虽然已经给出了教程 - [Data Binding Guide](https://developer.android.com/tools/data-binding/guide.html) [（中文版 - Data Binding（数据绑定）用户指南）](http://www.jianshu.com/p/b1df61a4df77) ，但是实践之后发现槽点实在太多，于是就有了这个教程，针对每个知识点给出更详实的例子同时也总结了遇到的一些坑，希望对你有所帮助：）\n\n\u003e 我现在转行做纯前端开发了，写了几个月 React/Vue 之后发现，DataBinding 真是一个伟大的 MVVM 框架，它缩小了 Native 开发和前端开发之间的距离，技术会过时，理念恒久远。\n\n## 准备\n\n新建一个 Project，建议使用[新版本的 Gradle 插件](build.gradle#L16)（至少要保证插件版本不低于 **1.5.0**）：\n\n```groovy\nclasspath 'com.android.tools.build:gradle:3.2.1'\n```\n\n然后修改对应模块（Module）的 [build.gradle](app/build.gradle#L6-L8)：\n\n```groovy\ndataBinding {\n    enabled true\n}\n```\n\n## 基础\n\n工程创建完成后，我们通过一个最简单的例子来说明 Data Binding 的基本用法。\n\n### 布局文件\n\n使用 Data Binding 之后，xml 的布局文件就不再用于单纯地展示 UI 元素，还需要定义 UI 元素用到的变量。所以，它的根节点不再是一个 `ViewGroup`，而是变成了 `layout`，并且新增了一个节点 `data`。\n\n```xml\n\u003clayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\u003e\n    \u003cdata\u003e\n    \u003c/data\u003e\n    \u003c!--原先的根节点（Root Element）--\u003e\n    \u003cLinearLayout\u003e\n    ....\n    \u003c/LinearLayout\u003e\n\u003c/layout\u003e\n```\n\n要实现 MVVM 的 `ViewModel` 就需要把数据（Model）与 UI（View） 进行绑定，`data` 节点的作用就像一个桥梁，搭建了 View 和 Model 之间的通路。\n\n我们先在 xml 布局文件的 `data` 节点中声明一个 `variable`，这个变量会为 UI 元素提供数据（例如 `TextView` 的 `android:text`），然后在 Java 代码中把『后台』数据与这个 `variable` 进行绑定。\n\n下面我们使用 Data Binding 创建一个展示用户信息的表格。\n\n### 数据对象\n\n添加一个 POJO 类 - [`User`](app/src/main/java/com/liangfeizc/databinding/model/User.java)，非常简单，两个属性以及他们的 getter 和 setter。\n\n```java\npublic class User {\n    private final String firstName;\n    private final String lastName;\n\n    public User(String firstName, String lastName) {\n        this.firstName = firstName;\n        this.lastName = lastName;\n    }\n\n    public String getFirstName() {\n        return firstName;\n    }\n\n    public String getLastName() {\n        return lastName;\n    }\n}\n```\n\n稍后，我们会新建一个 `User` 类型的变量，然后把它跟布局文件中声明的变量进行绑定。\n\n### 定义 Variable\n\n回到布局文件，在 `data` 节点中声明一个 `User` 类型的变量 `user`。\n\n```xml\n\u003cdata\u003e\n\t\u003cvariable name=\"user\" type=\"com.liangfeizc.databindingsamples.basic.User\" /\u003e\n\u003c/data\u003e\n```\n\n其中 `type` 属性就是我们在 Java 文件中定义的 `User` 类。\n\n当然，`data` 节点也支持 `import`，所以上面的代码可以换一种形式来写。\n\n```xml\n\u003cdata\u003e\n    \u003cimport type=\"com.liangfeizc.databindingsamples.basic.User\" /\u003e\n    \u003cvariable name=\"user\" type=\"User\" /\u003e\n\u003c/data\u003e\n```\n\n然后我们刚才在 build.gradle 中添加的那个插件 - `com.android.databinding` 会根据 xml 文件的名称 **Generate** 一个继承自 `ViewDataBinding` 的类。 当然，IDE 中看不到这个文件，需要手动去 build 目录下找。\n\n例如，这里 xml 的文件名叫 `activity_basic.xml`，那么生成的类就是 `ActivityBasicBinding`。\n\n**注意**\n\n`java.lang.*` 包中的类会被自动导入，可以直接使用，例如要定义一个 `String` 类型的变量：\n\n```xml\n\u003cvariable name=\"firstName\" type=\"String\" /\u003e\n```\n\n### 绑定 Variable\n\n修改 [`BasicActivity`](app/src/main/java/com/liangfeizc/databinding/sample/basic/BasicActivity.java#L17-L20) 的 `onCreate` 方法，用 `DatabindingUtil.setContentView()` 来替换掉 `setContentView()`，然后创建一个 `user` 对象，通过 `binding.setUser(user)` 与 `variable` 进行绑定。\n\n```java\n@Override\nprotected void onCreate(Bundle savedInstanceState) {\n    super.onCreate(savedInstanceState);\n    ActivityBasicBinding binding = DataBindingUtil.setContentView(\n            this, R.layout.activity_basic);\n    User user = new User(\"fei\", \"Liang\");\n    binding.setUser(user);\n}\n```\n\n除了使用框架自动生成的 `ActivityBasicBinding`，我们也可以通过如下方式自定义类名。\n\n```xml\n\u003cdata class=\"com.example.CustomBinding\"\u003e\n\u003c/data\u003e\n```\n\n**注意**\n\n`ActivityBasicBinding` 类是自动生成的，所有的 `set` 方法也是根据 `variable` 名称生成的。例如，我们定义了两个变量。\n\n```xml\n\u003cdata\u003e\n    \u003cvariable name=\"firstName\" type=\"String\" /\u003e\n    \u003cvariable name=\"lastName\" type=\"String\" /\u003e\n\u003c/data\u003e\n```\n\n那么就会生成对应的两个 set 方法。\n\n```java\nsetFirstName(String firstName);\nsetLastName(String lastName);\n```\n\n\n### 使用 Variable\n\n数据与 Variable 绑定之后，xml 的 UI 元素就可以直接使用了。\n\n```xml\n\u003cTextView\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:text=\"@{user.lastName}\" /\u003e\n```\n\n至此，一个简单的数据绑定就完成了，可参考[完整代码](app/src/main/java/com/liangfeizc/databinding/sample/basic/)\n\n## 高级用法\n\n### 使用类方法\n\n首先定义一个静态方法\n\n```java\npublic class MyStringUtils {\n    public static String capitalize(final String word) {\n        if (word.length() \u003e 1) {\n            return String.valueOf(word.charAt(0)).toUpperCase() + word.substring(1);\n        }\n        return word;\n    }\n}\n```\n\n然后在 xml 的 `data` 节点中导入：\n\n```xml\n\u003cimport type=\"com.liangfeizc.databindingsamples.utils.MyStringUtils\" /\u003e\n```\n\n使用方法与 Java 语法一样：\n\n```java\n\u003cTextView\n\tandroid:layout_width=\"wrap_content\"\n\tandroid:layout_height=\"wrap_content\"\n\tandroid:text=\"@{MyStringUtils.capitalize(user.firstName)}\" /\u003e\n```\n\n### 类型别名\n\n如果我们在 `data` 节点了导入了两个同名的类怎么办？\n\n```xml\n\u003cimport type=\"com.example.home.data.User\" /\u003e\n\u003cimport type=\"com.examle.detail.data.User\" /\u003e\n\u003cvariable name=\"user\" type=\"User\" /\u003e\n```\n\n这样一来出现了两个 `User` 类，那 `user` 变量要用哪一个呢？不用担心，`import` 还有一个 `alias` 属性。\n\n```xml\n\u003cimport type=\"com.example.home.data.User\" /\u003e\n\u003cimport type=\"com.examle.detail.data.User\" alias=\"DetailUser\" /\u003e\n\u003cvariable name=\"user\" type=\"DetailUser\" /\u003e\n```\n\n### Null Coalescing 运算符\n\n```java\nandroid:text=\"@{user.displayName ?? user.lastName}\"\n```\n\n就等价于\n\n```java\nandroid:text=\"@{user.displayName != null ? user.displayName : user.lastName}\"\n```\n\n### 属性值\n\n通过 `@{}` 可以直接把 Java 中定义的属性值赋值给 xml 属性。\n\n```xml\n\u003cTextView\n   android:text=\"@{user.lastName}\"\n   android:layout_width=\"wrap_content\"\n   android:layout_height=\"wrap_content\"\n   android:visibility=\"@{user.isAdult ? View.VISIBLE : View.GONE}\"/\u003e\n```\n\n### 使用资源数据\n\n这个例子，官方教程有错误，可以参考[Android Data Binder 的一个bug](http://blog.csdn.net/feelang/article/details/46342699)，[完整代码在此](app/src/main/res/layout/activity_resource.xml)\n\n```xml\n\u003cTextView\n    android:padding=\"@{large? (int)@dimen/largePadding : (int)@dimen/smallPadding}\"\n    android:background=\"@android:color/black\"\n    android:textColor=\"@android:color/white\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\"\n    android:text=\"@string/hello_world\" /\u003e\n```\n\n## Observable Binding\n\n本来这一节的标题应该叫**双向绑定**，但是很遗憾，现在的 **Data Binding** 暂时支持单向绑定，还没有达到 **Angular.js** 的威力。\n\n要实现 Observable Binding，首先得有一个 `implement` 了接口 `android.databinding.Observable` 的类，为了方便，Android 原生提供了已经封装好的一个类 - `BaseObservable`，并且实现了监听器的注册机制。\n\n我们可以直接继承 `BaseObservable`。\n\n```java\npublic class ObservableUser extends BaseObservable {\n    private String firstName;\n    private String lastName;\n\n    @Bindable\n    public String getFirstName() {\n        return firstName;\n    }\n\n    @Bindable\n    public String getLastName() {\n        return lastName;\n    }\n\n    public void setFirstName(String firstName) {\n        this.firstName = firstName;\n        notifyPropertyChanged(BR.firstName);\n    }\n\n    public void setLastName(String lastName) {\n        this.lastName = lastName;\n        notifyPropertyChanged(BR.lastName);\n    }\n}\n```\n\n`BR` 是编译阶段生成的一个类，功能与 `R.java` 类似，用 `@Bindable` 标记过 `getter` 方法会在 `BR` 中生成一个 *entry*。\n\n通过代码可以看出，当数据发生变化时还是需要手动发出通知。 通过调用 `notifyPropertyChanged(BR.firstName)` 可以通知系统 `BR.firstName` 这个 `entry` 的数据已经发生变化，需要更新 UI。\n\n除此之外，还有一种更细粒度的绑定方式，可以具体到成员变量，这种方式无需继承 `BaseObservable`，一个简单的 **POJO** 就可以实现。\n\n```java\npublic class PlainUser {\n    public final ObservableField\u003cString\u003e firstName = new ObservableField\u003c\u003e();\n    public final ObservableField\u003cString\u003e lastName = new ObservableField\u003c\u003e();\n    public final ObservableInt age = new ObservableInt();\n}\n```\n\n系统为我们提供了所有的 **primitive type** 所对应的 **Observable**类，例如 `ObservableInt`、`ObservableFloat`、`ObservableBoolean` 等等，还有一个 `ObservableField` 对应着 **reference type**。\n\n剩下的数据绑定与前面介绍的方式一样，具体可参考[ObservableActivity](app/src/main/java/com/liangfeizc/databinding/sample/observable/ObservableActivity.java)。\n\n## 带 ID 的 View\n\n**Data Binding** 有效降低了代码的冗余性，甚至完全没有必要再去获取一个 View 实例，但是情况不是绝对的，万一我们真的就需要了呢？不用担心，只要给 View 定义一个 ID，**Data Binding** 就会为我们生成一个对应的 `final` 变量。\n\n```xml\n\u003cTextView\n    android:id=\"@+id/firstName\"\n    android:layout_width=\"wrap_content\"\n    android:layout_height=\"wrap_content\" /\u003e\n```\n\n上面代码中定义了一个 ID 为 *firstName** 的 `TextView`，那么它对应的变量就是\n\n```java\npublic final TextView firstName;\n```\n\n具体代码可参考 [ViewWithIDsActivity.java](app/src/main/java/com/liangfeizc/databinding/sample/viewid/ViewWithIDsActivity.java)\n\n## ViewStubs\n\nxml 中的 `ViewStub` 经过 binding 之后会转换成 `ViewStubProxy`, 具体代码可参考 [ViewStubActivity.java](app/src/main/java/com/liangfeizc/databinding/sample/viewstub/ViewStubActivity.java)\n\n简单用代码说明一下，xml 文件与之前的代码一样，根节点改为 `layout`，在 `LinearLayout` 中添加一个 `ViewStub`，添加 **ID**。\n\n```xml\n\u003clayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\u003e\n    \u003cLinearLayout\n        ...\u003e\n        \u003cViewStub\n            android:id=\"@+id/view_stub\"\n            android:layout=\"@layout/view_stub\"\n            ... /\u003e\n    \u003c/LinearLayout\u003e\n\u003c/layout\u003e\n```\n\n在 Java 代码中获取 `binding` 实例，为 `ViewStubProy` 注册 `ViewStub.OnInflateListener` 事件：\n\n```java\nbinding = DataBindingUtil.setContentView(this, R.layout.activity_view_stub);\nbinding.viewStub.setOnInflateListener(new ViewStub.OnInflateListener() {\n\t@Override\n\tpublic void onInflate(ViewStub stub, View inflated) {\n\t\tViewStubBinding binding = DataBindingUtil.bind(inflated);\n\t\tUser user = new User(\"fee\", \"lang\");\n\t\tbinding.setUser(user);\n\t}\n});\n```\n\n## Dynamic Variables\n\n完整代码可以参考 [dynamic](app/src/main/java/com/liangfeizc/databinding/sample/dynamic)\n\n以 `RecyclerView` 为例，`Adapter` 的 **DataBinding** 需要动态生成，因此我们可以在 `onCreateViewHolder` 的时候创建这个 **DataBinding**，然后在 `onBindViewHolder` 中获取这个 **DataBinding**。\n\n```java\npublic static class BindingHolder extends RecyclerView.ViewHolder {\n    private ViewDataBinding binding;\n\n    public BindingHolder(View itemView) {\n        super(itemView);\n    }\n\n    public ViewDataBinding getBinding() {\n        return binding;\n    }\n\n    public void setBinding(ViewDataBinding binding) {\n        this.binding = binding;\n    }\n}\n\n@Override\npublic BindingHolder onCreateViewHolder(ViewGroup viewGroup, int i) {\n    ViewDataBinding binding = DataBindingUtil.inflate(\n            LayoutInflater.from(viewGroup.getContext()),\n            R.layout.list_item,\n            viewGroup,\n            false);\n    BindingHolder holder = new BindingHolder(binding.getRoot());\n    holder.setBinding(binding);\n    return holder;\n}\n\n@Override\npublic void onBindViewHolder(BindingHolder holder, int position) {\n    User user = users.get(position);\n    holder.getBinding().setVariable(BR.user, user);\n    holder.getBinding().executePendingBindings();\n}\n```\n\n注意此处 `DataBindingUtil` 的用法：\n\n```java\nViewDataBinding binding = DataBindingUtil.inflate(\n\tLayoutInflater.from(viewGroup.getContext()),\n\tR.layout.list_item,\n\tviewGroup,\n\tfalse);\n```\n\n---\n\n还有另外一种比较简洁的方式，直接在构造 Holder 时把 `View` 与自动生成的 `XXXBinding` 进行绑定。\n\n```java\npublic class UserAdapter extends RecyclerView.Adapter\u003cUserAdapter.UserHolder\u003e {\n    private static final int USER_COUNT = 10;\n\n    @NonNull\n    private List\u003cUser\u003e mUsers;\n\n    public UserAdapter() {\n        mUsers = new ArrayList\u003c\u003e(10);\n        for (int i = 0; i \u003c USER_COUNT; i ++) {\n            User user = new User(RandomNames.nextFirstName(), RandomNames.nextLastName());\n            mUsers.add(user);\n        }\n    }\n\n    public static class UserHolder extends RecyclerView.ViewHolder {\n        private UserItemBinding mBinding;\n\n        public UserHolder(View itemView) {\n            super(itemView);\n            mBinding = DataBindingUtil.bind(itemView);\n        }\n\n        public void bind(@NonNull User user) {\n            mBinding.setUser(user);\n        }\n    }\n\n    @Override\n    public UserHolder onCreateViewHolder(ViewGroup viewGroup, int i) {\n        View itemView = LayoutInflater.from(viewGroup.getContext())\n                .inflate(R.layout.user_item, viewGroup, false);\n        return new UserHolder(itemView);\n    }\n\n    @Override\n    public void onBindViewHolder(UserHolder holder, int position) {\n        holder.bind(mUsers.get(position));\n    }\n\n    @Override\n    public int getItemCount() {\n        return mUsers.size();\n    }\n}\n```\n\n## Attribute setters\n\n有了 **Data Binding**，即使属性没有在 `declare-styleable` 中定义，我们也可以通过 xml 进行赋值操作。\n为了演示这个功能，我自定义了一个 View - [NameCard](app/src/main/java/com/liangfeizc/databinding/view/NameCard.java)，属性资源 [R.styleable.NameCard](app/src/main/res/values/styles.xml#L8-L10) 中只定义了一个 `age` 属性，其中 `firstName` 和 `lastName` 只有对应的两个 `setter` 方法。\n\n只要有 `setter` 方法就可以像下面代码一样赋值：\n\n```xml\n\u003ccom.liangfeizc.databindingsamples.attributesetters.UserView\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:paddingLeft=\"@dimen/largePadding\"\n    app:onClickListener=\"@{activity.clickListener}\"\n    app:firstName=\"@{@string/firstName}\"\n    app:lastName=\"@{@string/lastName}\"\n    app:age=\"27\" /\u003e\n```\n\n`onClickListener` 也是同样道理，只不过我们是在 `Activity` 中定义了一个 `Listener`。\n\n## 转换器 (Converters)\n\n\u003e **非常重要**\n\n\u003e 使用 **Converter** 一定要保证它不会影响到其他的属性，例如这个 `@BindingConversion`- [convertColorToString](app/src/main/java/com/liangfeizc/databinding/sample/converter/ConversionsActivity.java#L50-L63) 就会影响到[android:visibility](app/src/main/res/layout/activity_basic.xml#L76), 因为他们都是都符合从 int 到 int 的转换。\n\n\n在 xml 中为属性赋值时，如果变量的类型与属性不一致，通过 **DataBinding** 可以进行转换。\n\n例如，下面代码中如果要为属性 `android:background` 赋值一个 `int` 型的 color 变量：\n\n```xml\n\u003cView\n    android:background=\"@{isError.get() ? @color/red : @color/white}\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    app:layout_height=\"@{height}\" /\u003e\n```\n\n只需要定义一个标记了 `@BindingConversion` 的静态方法即可（*方法的定义位置可以随意*）：\n\n```java\n@BindingConversion\npublic static ColorDrawable convertColorToDrawable(int color) {\n    return new ColorDrawable(color);\n}\n```\n\n具体代码可参考 [ConversionsActivity.java](app/src/main/java/com/liangfeizc/databinding/sample/converter/ConversionsActivity.java)。\n\n## include\n\n用法可以参考代码 [IncludeActivity.java](/app/src/main/java/com/liangfeizc/databinding/sample/include/IncludeActivity.java)\n\n如果在非根节点的 ViewGroup 中使用 `include` 会导致 crash，已经在 StackOverflow 上提了一个问题[Android Data Binding makes app crash when using include tag in a non-root ViewGroup](http://stackoverflow.com/questions/30887906/android-data-binding-makes-app-crash-when-using-include-tag-in-a-non-root-viewgr)，直されたそうですけど。\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliangfeidotme%2FMasteringAndroidDataBinding","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fliangfeidotme%2FMasteringAndroidDataBinding","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fliangfeidotme%2FMasteringAndroidDataBinding/lists"}