Introduction (0:00)
We are George Mount and Yigit Boyar, and we work on the Android UI Toolkit team. We have a lot of information about Data Binding to share with you, and lots of code to go with it. We’ll discuss the important aspects of how Data Binding works, how to integrate it into your app, how it works with other components, and we’ll mention some best practices.
Why Data Binding? (0:44)
You may wonder why we decided to implement this library. Here’s an example of a common use case.
<LinearLayout …>
<TextView android:id="@+id/name"/>
<TextView android:id="@+id/lastName"/>
</LinearLayout>
This is an Android UI you see all the time. Say you have a bunch of videos with IDs. Your designer comes and says, “Okay, let’s try adding new information to this layout,” so that when you add any video, you need to tack on another ID. You go back to your Java code in order to modify the UI.
private TextView mName
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
mName = (TextView) findViewById(R.id.name);
}
public void updateUI(User user) {
if (user == null) {
mName.setText(null);
} else {
mName.setText(user.getName());
}
}
You write a new TextView, you find it from the UI, and you set your logic so that whenever you need to update your user, you have to set the information on the TextView.
private TextView mName
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
mName = (TextView) findViewById(R.id.name);
mLastName = (TextView) findViewById(R.id.lastName);
}
public void updateUI(User user) {
if (user == null) {
mName.setText(null);
mLastName.setText(null);
} else {
mName.setText(user.getName());
mLastName.setText(user.getLastName());
}
}
All in all, that is a lot of things you have to do just to add one view to your UI. It seems like too much stupid boilerplate code that doesn’t require any brainpower.
There are already some really nice libraries to make this easier and more solid. For example, if you use ButterKnife, you could get two of those ugly viewByIds, making it much easier to read. You can get rid of the extra code, telling ButterKnife to delete it for you.
private TextView mName
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
}
public void updateUI(User user) {
if (user == null) {
mName.setText(null);
mLastName.setText(null);
} else {
mName.setText(user.getName());
mLastName.setText(user.getLastName());
}
}
It’s a good step forward, but we can go one step further. We can say “Okay, why do I need to create items for these? Something can just generate it. I have a layout file, I have ID’s.” So you can use Holdr, which does that for you. It processes your files and then creates views for them. You initiate from Holdr, which converts the IDs you entered into field names.
private Holdr_ActivityMain holder;
protected void onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
holder = new Holdr_ActivityMain(findViewById(content));
}
public void updateUI(User user) {
if (user == null) {
holder.name.setText(null);
holder.lastName.setText(null);
} else {
holder.name.setText(user.getName());
holder.lastName.setText(user.getLastName());
}
}
This is better again, but there’s still something unnecessary in this code. There’s a huge part that I never touched, where I was unable to reduce the amount of code. It’s all very simple code, too: I have a user object, I just want to move the data inside of this object to the view class. How many times have you made a mistake when you see code like this? You remember to change one thing, but forget to change another, and end up with a crash on production. This is the part we want to focus on: we want to get through all the boilerplate code.
When you use Data Binding, it’s very similar to using Holdr, but you have to do a lot less work. Data Binding figures the rest out.
private ActivityMainBinding mBinding;
protected void onCreate(Bundle savedInstanceState) {
mBinding = DataBindingUtil.setContentView(this,
R.layout.activity_main);
}
public void updateUI(User user) {
mBinding.setUser(user);
}
Behind the Scenes (3:53)
How does Data Binding work behind the scenes? Take a look at the layout file from before:
<LinearLayout …>
<TextView android:id="@id/name" />
<TextView android:id="@id/lastName" />
</LinearLayout>
I have these IDs, but why do I need them if I could find them back in my Java code? I actually don’t need them anymore, so I can get rid of them. In their place, I put the most obvious thing I want to display.
<LinearLayout …>
<TextView android:text="@{user.name}"/>
<TextView android:text="@{user.lastName}"/>
</LinearLayout>
Now, when I look at this layout file, I know what the TextView shows. It has become very obvious, so I don’t need to go back to read my Java code. We designed the Data Binding library in a way that didn’t include any magic that wasn’t easy to explain. If you are using something in your layout file, you need to tell Data Binding what it is. You simply say, “We are labeling this layout file with this type of user, and now we are going to find it.” If your designer asks you to add another view, you simply add one more line and show your new view, with no other code changes.
<layout>
<data>
<variable name="user"
type="com.android.example.User"/>
</data>
<LinearLayout …>
<TextView android:text="@{user.name}"/>
<TextView android:text="@{user.lastName}"/>
<TextView android:text='@{"" + user.age}'/>
</LinearLayout>
</layout>
It’s also really easy to find bugs. You can look at something like the above code and and say, “Oh, look! Empty string plus user.age!” You just set text on the integer, and then bang! We did that many times, it just happens.
But How Does It Work? (5:57)
The first thing the Data Binding library does is process your layout files. By “process,” I mean that it goes into to your layout files when your application is being compiled, finds everything about Data Binding, grabs that information and deletes it. We delete it because the view system doesn’t know about it, so it disappears.
The second step is to parse these expressions by running it through a grammar. For example, in this case:
<TextView android:visibility="@user.isAdmin ? View.VISIBLE : View.GONE}"/>
The user
is an ID, the View
is an ID, and the other View
is an ID. They’re identifiers, like real objects, but we don’t really know what they are yet at this point. The other things are invisible or gone. There is field access, and the whole thing’s a ternary. That’s what we have understood so far. We parse things from a file, and understand what’s inside.
The third step is resolving dependencies, which happens when your code is being compiled. In this step, for example, we look at user.isAdmin
and figure out what it means. We think “Okay, this method turns a boolean inside that user class. I know this expression means some sort of boolean at run time.”
The final step is writing data binders. We write the classes that YOU don’t need to write anymore. In short, final step: profit