question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Better Databinding Support

See original GitHub issue

Playing around with databinding in a side project made me think about this a bit more. Sorry for the wall of text 😃

Databinding is a fairly complex topic especially on when you want to support field change notifications. Right now it requires quite a lot of boilerplate: https://github.com/Zhuinden/realm-databind-experiment/blob/master/app/src/main/java/com/zhuinden/realmdatabind/realm/objects/Post.java

And property notifications needs to be called from a custom listener as well:

// Notify about writes should be done from listeners
Person p = realm.where(Person.class).findFirst();
p.addChangeListner(new RealmObjectChangeListener() {
    @Override
    public void onChange(Person p, ObjectChangeSet changeSet) {
        if (changeSet.isFieldChanged("name")) {
            p.notifyPropertyChanged(BR.name);
        }
    }
});

There is at least 3 ways we can improve this, 2 that are very easy, and one slightly more difficult, but it would make databinding support rather nice.

1. Support BaseObservable class

public class Person extends BaseObservable implements RealmModel {

    private String name;

    @Bindable
    public String getName() {
        return name;
    }

    @Bindable({"name"})
    public String getDisplayName() {
        return name.toLowerCase();
    }
}

// Usage
MyFragmentBinding binder = MyFragmentBinding.inflate(inflater);
binder.setPerson(realm.where(Person.class).findFirst());

Requirements:

  1. We must allow people to extend from BaseObservable, or more generally from any non-realm super class. Right now this would actually also work with our type system rather nicely, since people couldn’t query for the abstract classes since they wouldn’t be implementing RealmModel. This will probably change with #575 though where we would loosen the generics. The quick solution would be to just make an exception for BaseObservable.
  2. People must use the RealmModel interface approach (annoying if you don’t use Kotlin).

2. Support new RealmBaseObservable class

More convenient:

public class Person extends RealmBaseObservable {

    private String name;

    @Bindable
    public String getName() {
        return name;
    }

    @Bindable({"name"})
    public String getDisplayName() {
        return name.toLowerCase();
    }
}

Requirements:

  1. We must provide a RealmBaseObservable class that is our implementation of BaseObservable which also extends RealmObject. This means people could still use the more fluent methods on RealmObject.
  2. This class should probably be in realm-android-adapters.
  3. We need to extend the annotation processor to be aware of this class, and we need to prevent people from making queries on it.

3. Automatically create RealmObjectChangelistener

Databinding annotation processors will consume the @Bindable annotations which prevents us from creating one that process only those classes, but we can create one similar to https://github.com/cmelchior/realmfieldnameshelper that will process all Realm Model class and check for the @Bindable annotation that way.

Field observing in databinding would be a 1-liner and really showcase how useful object listeners can be.

// This class could be created by an annotation processor since we ca deduce the field/BR name from the @Bindable method
public class PersonObjectChangeListener implements RealmObjectChangeListener<Person> {
    @Override
    public void onChange(Person obj, ObjectChangeSet objectChangeSet) {
        if (objectChangeSet.isFieldChanged("name")) {
            obj.notifyPropertyChanged(dk.ilios.realmviewmodels.example.BR.name);
        }
    }
}

// Usage options
// Like asObservable() but will automatically attach the listener
// Will mean our core library need to know about Databinding
Person p = person.asViewModel(); 

// This could be implemented as a completely seperate project or more likely in realm-android-adapters
Person p = ViewModelFactory.create(person); 

// Perhaps instead
Person p = RealmAndroid.getViewModel(person);

// Just create public classes and let people hook them up themselves
Person p = getPerson();
p.addChangeListener(new PersonObjectChangeListener());

// Example of usage in a fragment/activity
MyFragmentBinding binder = MyFragmentBinding.inflate(inflater);
binder.setPerson(realm.where(Person.class).findFirst().asViewModel());

Requirements:

  1. We need an extra annotation processor somewhere. Either as part of our standard one or a new one in realm-android-adapters.
  2. That annotation processor need to know the manifest package name which is rather hard without hacks or a gradle plugin, which only our main project uses. See e.g. https://github.com/androidannotations/androidannotations/blob/558b9088fdc2cd82988445ed86d60340b143a95f/AndroidAnnotations/androidannotations-core/androidannotations/src/main/java/org/androidannotations/internal/helper/AndroidManifestFinder.java

Conclussion

1/2 seems rather straight forward and easy to implement. 3) is doable as well, the biggest issue is around how we actually want to distribute it and how much we want to expose Android specific concepts in our API’s.

Issue Analytics

  • State:open
  • Created 6 years ago
  • Reactions:13
  • Comments:10 (8 by maintainers)

github_iconTop GitHub Comments

1reaction
nirinchevcommented, Oct 19, 2017

As the person who was driving the databinding support for .NET, I can briefly explain the motivation for our decisions. Databinding is somewhat magical in Xaml - there’s a set of interfaces you need to implement (that Realm objects and collections implement those out of the box) and the databinding engine will subscribe for events, update the view and so on. On the other hand, when the UI changes, it will call the property setters directly, and those are generated by the Realm SDK, so the user can’t add their custom logic there. This leaves users with no opportunity to begin/commit transactions in response to databinding events, which would be very poor experience.

The way two-way databinding works in the .NET SDK is, we implement an additional interface that is used by the Xaml engine and provide custom property setters that the databinding engine will invoke instead of the regular ones. In those property setters, we check if the Realm is in transaction and if it is, we just call the original setter. If it’s not, we wrap the setter call in a transaction, that is committed immediately.

Our reasoning was that

  1. We still give people chance to “optimize” their transactions. If you expect a lot of properties to be changed and don’t care for preserving partial changes/drafts (e.g. create new User screen), you can begin a transaction when the screen is displayed, have the user set all fields and commit the transaction on save button click. In that case, you’ll group everything in a single transaction, and if the user clicks cancel, or leaves the app, nothing will be persisted in Realm.

  2. We allow people to not worry about starting transactions when they are on a screen where they don’t expect a lot of data changes or they are interested in persisting every change - e.g. settings screen. Regarding performance, it’s a bit wasteful to do that, indeed, but the performance cost of starting and committing a transaction is negligible compared to the cost of user interaction and the binding engine redrawing stuff.

In conclusion, we feel that the potential performance hit from automating transactions in databinding scenarios is many times offset by the ease of use of the SDK. Here’s a blog post I wrote around the time we launched this feature, that showcases the simplicity of the API when using this feature. Hope this helps 😃

1reaction
Zhuindencommented, Apr 27, 2017

Depends. If you use an intermediate object, it may as well be an unmanaged RealmObject.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Data Binding Library - Android Developers
The Data Binding Library is a support library that allows you to bind UI components in your layouts to data sources in your...
Read more >
Databinding Support - IntelliJ IDEA & Android Studio Plugin
Intellij plugin that support Android Data Binding Library. This plugin has following features: Convert non-databinding layout to databinding layout.
Read more >
Applying Data Binding for Views | CodePath Android Cliffnotes
Android has now released a stable data-binding library which allows you to connect views with data in a much more powerful way than...
Read more >
Data Binding Vs. View Binding - Medium
Data binding is more flexible than view binding but can also be more ... View binding also supports two-way data binding, meaning changes...
Read more >
Data Binding plugin uses old version of Support library
@GeorgeMount: Simply put, that has not happened. – CommonsWare. Jan 31, 2018 at 20:01. | Show 1 more comment ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found