RoboBinding logo RoboBinding Follow @robobinding

1. Set up Eclipse development environment

1.1. Eclipse and plug-in installation

Eclipse Gradle settings
Figure 1. Eclipse Gradle settings

1.2. Import project

Firstly download the project. The project can be downloaded through Eclipse eGit , Git command line or other Git clients. Here is the Project.

After the project is downloaded to local, follow the steps below to import into Eclipse:

  • Goto the root directory of the project in command line, and run gradlew framework:cleanEclipse framework:eclipse.

  • In Eclipse, follow the Import Existing Projects into Workspace wizard to import the framework sub-project. After imported, the project should look similar to the screen shot below.

  • If you are interested in source code generation, you can import codegen sub-project in the same way.

eclipse_robobinding.png
Figure 2. RoboBinding project in Eclipse

2. RoboBinding architecture and implementation

RoboBinding is a data-binding Presentation Model framework for the Android platform. The aim of the project is to reduce the workload of Android application development, promote a clear project structure and make the project easier to test.

2.1. Observing from RoboBinding overall implementation

In the following section, a package start with . is a relative path to org.robobinding.

architecture.png
Figure 3. RoboBinding overall implementation chart

The above shows the overall implementation of the RoboBinding framework. The two blocks with yellow background (The Client and the PresentationModel) are the application code, and the three others are the framework code. Note that the figure only shows some core classes from a number of RoboBinding packages. A client(can be a Activity, a Fragment or a Menu) passes a layout to a ViewBinder(or MenuBinder). Then the ViewBinder inflates the given layout file to construct a view hierarchy(with the assistance of android.view.LayoutInflater) and extracts binding information. A root view may contain several sub-views, and a sub-view may have its child views, forming a tree structure. Each view may contain several view binding attributes. RoboBinding defines three types of view binding attributes, namely .viewattribute.property.[XX]PropertyViewAttribute, .viewattribute.event.EventViewAttribute, and .viewattribute.grouped.GroupedViewAttribute.

A [XX]PropertyViewAttribute implements the binding between a view attribute and a property of a PresentationModel. A EventViewAttribute implements the binding between a view event and a method of a PresentationModel. A GroupedViewAttribute, or a composite view binding attribute, consists of a number of sub-attributes, which can be PropertyViewAttributes and EventViewAttributes. A PropertyViewAttribute is mapped to a property or a dataset-property of the PresentationModel through a .property.PropertyValueModel(when it is a simple property) or a .property.DataSetValueModel(when it is a complicated dataset property) of the .presentationmodel.PresentationModelAdapter. A EventViewAttribute is mapped to a method of the PresentationModel through a .function.Function of the PresentationModelAdapter.

architecture_sample.png
Figure 4. Demonstrating RoboBinding internal running follow through an example

The chart above summarizes RoboBinding internal running follow through an example. The three blocks with yellow background are taken from RoboBinding-album-sample project’s source code with slight modification, while the other two are the instances of some core classes from the framework. ViewAlbumsActivity binds the layout to its presentationModel with the help of the framework(here is .binder.Binders class).

The part with the numbering [1] is an example of a simple view binding attribute(namely .viewattribute.property.PropertyViewAttribute). The framework creates a .widget.textview.TextAttribute instance for {windowsTitle}, and then the TextAttribute instance binds {windowTitle} to ViewAlbumsPresentationModel.windowTitle through a PropertyValueModel from the PresentationModelAdapter.

The part with the numbering [2] is an example of a composite view binding attribute(namely .viewattribute.grouped.GroupedViewAttribute). Its child attributes of the source and the itemLayout form a composite ListView binding attribute. The framework creates a composite view attribute instance, a .widget.adapterview.AdaptedDataSetAttributes, for {albums} and@layout/album_row. The composite view attribute contains two sub-view attributes, namely .widget.adapterview.SourceAttribute and .widget.adapterview.StaticLayoutAttribute, which map to {albums} and @layout/album_row respectively. The SourceAttribute instance binds {albums} to ViewAlbumsPresentationModel.albums with the help of a DataSetValueModel of the PresentationModelAdapter. The StaticLayoutAttribute links to the external file album_row.xml by parsing @layout/album_row.

The third one is an example of an event binding attribute(namely .viewattribute.event.EventViewAttribute). The framework creates a .widget.view.OnClickAttribute instance for createAlbum, and then the OnClickAttribute binds createAlbum to ViewAlbumsPresentationModel.createAlbum through a Function of the PresentationModelAdapter.

2.2. Observing from RoboBinding implementation details

2.2.1. Implementing view inflation and binding

The classes that implement view inflation and binding are in org.robobinding and org.robobinding.binder packages. In the following section, a package start with . is a relative path to org.robobinding.

developer_usecase_sequence.png

As shown above, application developers can use the framework in two potential ways(shown in two different colours), either through the utility class .binder.Binders or .binder.BinderFactoryBuilder(with customization capability). In either way, the final actual entry class is the same, BinderFactory. From BinderFactory, an instance of ViewBinder is created, which fulfils layout inflation and binding. Let us have a look at layout inflation process in the next.

inflateview_sequence.png

The figure shows the layout inflation process and the core classes involved. Carrying on with the ViewBinder mentioned above, it further passes the layout inflation task to .binder.BindingViewInflater and the latter propagate to org.robobinding.NonBindingViewInflater. The only responsibility of NonBindingViewInflater is to inflate layout, constructing the view hierarchy from the given layout. When a view is created during inflation, a callback with view attributes to the BindingViewInflater is fired, and out of these view attributes, the BindingViewInflater builds a set of pending view binding attributes, org.robobinding.PendingAttributesForView, with the help of .binder.BindingAttributeParser. Repeating the process, until all PendingAttributesForViews are parsed from the layout.

Then the PendingAttributesForViews are passed to .binder.BindingAttributeResolver, which further tries to resolve them. BindingAttributeResolver uses .binder.ByBindingAttributeMappingResolverFinder to find a set of matched view binding attribute mapping resolver, .binder.ByBindingAttributeMappingResolver, according to the view hierarchy from top to bottom (e.g., android.widget.EditText inherits from android.widget.TextView, and TextView inherits from android.view.View. So a EditText instance matches three ByBindingAttributeMappingResolvers: A EditText ByBindingAttributeMappingResolver, a TextView ByBindingAttributeMappingResolver and a view ByBindingAttributeMappingResolver in order). BindingAttributeResolver calls all the matched ByBindingAttributeMappingResolvers in order to get a final set of .binder.ResolvedBindingAttributesForViews and then returns to the BindingViewInflater.

Afterwards, the BindingViewInflater add all the predefined pending attribute mappings, org.robobinding.PredefinedPendingAttributesForView, if there is. And then call BindingAttributeResolver to resolve them also, which follows the same process as resolving PendingAttributesForView above. Finally it gets a set of ResolvedBindingAttributesForViews as well.

The BindingViewInflater eventually hands a .binder.InflatedView containing all the ResolvedBindingAttributesForViews over to ViewBinder. The whole layout inflation work ends and the next binding stage starts.

bindview_sequence.png

The figure shows the entire binding process and the core classes involved. The ViewBinder hands the binding task to its ViewBindingLifecycle. The ViewBindingLifecycle runs the binding life-cycle on the InflatedView. Firstly try to bind all the ResolvedBindingAttributesForViews in the InflatedView. Internally the ResolvedBindingAttributesForViews invoke each of its binding attributes to complete the binding. After completion of the binding, the ViewBindingLifecycle validates the result. When an error is found, the whole process is terminated and the errors are reported. If successful with no errors, pre-initializing the views is kicked off when specified. Pre-initializing views is to synchronize values from a PresentationModel to its views.

2.2.2. Implementing view attribute bindings

View attribute bindings are implemented by org.robobinding.viewattribute and its sub package. In the following section, a package start with . is a relative path to org.robobinding.viewattribute.

viewattribute_hierarchy.png
Figure 5. ViewAttribute class hierarchy diagram

There are three types of view attributes defined in the framework, namely a simple view attribute .property.PropertyViewAttribute, an event view attribute .event.EventViewAttribute and a composite view attribute .grouped.GroupedViewAttribute.

A PropertyViewAttribute implements binding for a simple view attribute. There are two kinds, which are single value-typed, property.PropertyViewAttribute, and multiple value-typed, property.MultiTypePropertyViewAttribute. A PropertyViewAttribute(in the figure with [1.1]marked, {enabledSwitch} and {selectedSwitch}) has only one possible value type such as EnabledAttribute(org.robobinding.widget.view.EnabledAttribute), having only one value type of Boolean. Whereas MultiTypePropertyViewAttribute(in the figure with [1.2] marked, {visibilitySwitch}) has multiple value types such as VisibilityAttribute(org.robobinding.widget.view.VisibilityAttribute), which can be a Integer or a Boolean.

A EventViewAttribute(in the figure with [2] marked, onClick and onLongClick) implements binding for a event view attribute, e.g., org.robobinding.widget.view.OnClickAttribute/OnLongClickAttribute and so on.

GroupedViewAttribute(in the figure with [3.1, 3.2] marked, {source}, @layout/item_row and {[text1.text:{name}]}; @layout/footer_layout, {footer} and {footerVisibility}) implements binding for a composite view attribute such as a org.robobinding.widget.adapterview.AdaptedDataSetAttributes which contains sub-attributes a source, an itemLayout and an optional itemMapping; a org.robobinding.widget.listview.FooterAttributes, which contains sub-attributes a footerLayout and two optional attributes, a footerPresentationModel and a footerVisibility.

org.robobinding.viewattribute package contains abstract classes of core concepts for view attributes, and org.robobinding.widget package contains various counterpart view attribute binding implementations for android.widget package. For example, org.robobinding.widget.textview package is for android.widget.TextView and org.robobinding.widget.seekbar package is for android.widget.SeekBar.

All kinds of view attributes have corresponding Binder classes. They work together to accomplish the binding task. For example, the counterpart of EventViewAttribute is EventViewAttributeBinder.

childviewattribute_relationship.png
Figure 6. ChildViewAttributes class diagram

In the following section, a package start with . is a relative path to org.robobinding.viewattribute.

The figure shows the relationship between a composite view attribute .grouped.GroupedViewAttribute and its child view attributes. A GroupedViewAttribute contains various types of child view attributes. Currently the kinds of child view attributes in use are .grouped.ChildViewAttribute, .grouped.DependentChildViewAttribute, .property.PropertyViewAttribute and .property.MultiTypePropertyViewAttribute.

A ChildViewAttribute is an ordinary child view attribute and it has a subclass of .grouped.ChildViewAttributeWithAttribute. A ChildViewAttributeWithAttribute is a ChildViewAttribute but with a required attribute value, which will be set when the ChildViewAttributeWithAttribute is created, such as org.robobinding.widget.adapterview.SourceAttribute/SubViewLayoutAttribute.

DependentChildViewAttribute is a dependent child view attribute. It cannot be created in an ordinary way, as it depends on some runtime information from previous child attributes. It is, therefore, created via a .grouped.ChildViewAttributeFactory. Take org.robobinding.widget.adapterview.SubViewAttributes.SubViewAttributeFactory for example. It needs subViewLayoutAttribute.getLayoutId() to create a SubViewAttribute.

PropertyViewAttribute and MultiTypePropertyViewAttribute are single value-typed and multi value-typed view attributes. They are already discussed earlier, and can be reused here as a child view attribute.

2.2.3. Implementing Presentation Model concepts

In the following section, a package start with . is a relative path to org.robobinding.

presentationmodel_classdiagram.png
Figure 7. PresentationModel class diagram

The four packages, org.robobinding.presentationmodel, org.robobinding.property, org.robobinding.itempresentationmodel and org.robobinding.function, are for wrapping a PresentationModel as a .presentationmodel.PresentationModelAdapter, which is used in binding process. The PresentationModelAdapter is the entry class of the packages. The framework creates a PresentationModelAdapter instance for each PresentationModel. Currently, there are two ways for the framework to identify a class as a PresentationModel through .presentation.PresentationModel annotation.

A PresentationModelAdapter is created to wrap a PresentationModel and acts as a provider of three pieces of information, namely wrapping SimpleProperty, DataSetProperty and Method, and then acting as a provider of .property.PropertyValueModel, .property.DataSetValueModel and .function.Function. On the right hand side of the figure above, the property of windowTitle is a SimpleProperty, the property of albums is a DataSetProperty and createAlbum() is a Method. DataSetProperty is a data set property. The framework will create a .itempresentationmodel.ItemPresentationModel instance for each data item in the collection. In the given figure above, an AlbumItemPresentationModel will be created for each album item in albums collection and will be rendered to the screen according to the given row layout of @layout/album_row.

2.2.4. View event listener aggregates

The concept of view event listener aggregates(org.robobinding.viewattribute.ViewListeners or its subclasses), is introduced to provide a workaround of registering multiple event listeners due to the fact that the Android framework can only allow register a single event listener in many cases(e.g., android.view.View.setOnFocusChangeListener). As an application may need to register multiple event listeners on a same event, to overcome the old listener not being erased by a new one accidentally, the concept of view event listener aggregate is implemented.

3. Contributing

  • We value code quality. So all source code committed requires to have proper unit tests along with it, especially public methods. If unit tests cannot be supplied for some reasons, explicit comments have to be given.

  • RoboBinding source code format needs to be followed, so that it is easier for contributions among us. It is basically Java standard code format.