Vorstellung
Ich bin Geschäftsführer der PartMaster GmbH und als Java-Architekt mit den Schwerpunkten Rich Client Platform, Data Binding und Modelling Framework in Software-Projekten tätig.
PartMaster GmbH
Lagerstraße 44/45
18055 Rostock
fon +49 381-20373995
fax +49 381-20373994
email info@partmaster.de
In the last days I did some work to familiarize myself with programming Android. As introduction project I choosed to port the sample application from my Observable Data Binding blog series to Android. Tom Schindl once reported in his blog article, that he had successful tested the connection between Android and the Eclipse Data Binding. That has encouraged myself, to undertake the same.
So I checked out the Eclipse Databinding projects and their dependencies from the Eclipse CVS, removed the Equinox-artifacts (Bundle-Activator-Classes) from the sources, exported the results as JARs and added them to an Android hello world projekt.
Enumeration 1: Eclipse Data Binding Bundles
Later it turned out, that one can use the original Eclipse Databinding Bundles for Android without modifying the sources. But the original bundles cause problems when it comes to shrink the App size with Proguard because of unresolvable class references. So I think, the adaption on the source code level is the right approach.
So far it was fairly good without any problems. The next task was to write the class AndroidObservables and use the classes SWTObservables and SwingObservables as pattern. The amount of implementations of the interface IObservableValue for the Android widget properties needed for my sample application was handy and straightforward. With the help of the antetypes from the UFaceKit project they were written quickly. In essence the both functions doSetValue and doGetValue have to be implemented and the notification listeners have to be called on changes of the property value.
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.value.IObservableValue;
import android.view.View;
import android.widget.CompoundButton;
import android.widget.TextView;
import android.widget.ToggleButton;
public class AndroidObservables {
private static Realm realm = new AndroidRealm();
public static Realm getRealm() {
return realm;
}
public static IObservableValue observeEnabled(View control) {
return new ControlObservableValue(control, AndroidProperties.ENABLED);
}
public static IObservableValue observeSelection(CompoundButton button) {
return new ButtonObservableValue(button);
}
public static IObservableValue observeText(TextView control) {
return new TextViewObservableText(control);
}
public static IObservableValue observeToggleText(ToggleButton control) {
return new ToggleButtonObservableText(control);
}
public static IObservableValue observeTextOff(ToggleButton control) {
return new ButtonObservableTextOff(control);
}
public static IObservableValue observeText(TextView control,
int updateEventType) {
return new TextObservableValue(control, updateEventType);
}
}
Listing 1: Class AndroidObservables
import org.eclipse.core.databinding.observable.Diffs;
import org.eclipse.core.databinding.observable.Realm;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
/**
* IAndroidObservableValue implementation to observe if compound button (ToggleButton....) is checked or not.
*/
public class ButtonObservableValue extends AbstractAndroidObservableValue {
private final CompoundButton button;
private boolean selectionValue;
private boolean updating = false;
private OnCheckedChangeListener updateListener = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView,
boolean isChecked) {
if (updating)
return;
boolean oldSelectionValue = selectionValue;
selectionValue = isChecked;
notifyIfChanged(oldSelectionValue, selectionValue);
}
};
public ButtonObservableValue(CompoundButton button) {
super(button);
this.button = button;
init();
}
public ButtonObservableValue(Realm realm, CompoundButton button) {
super(realm, button);
this.button = button;
init();
}
private void init() {
this.selectionValue = button.isChecked();
button.setOnCheckedChangeListener(updateListener);
}
public void doSetValue(final Object value) {
try {
updating = true;
boolean oldSelectionValue = selectionValue;
selectionValue = value == null ? false : ((Boolean) value).booleanValue();
button.setChecked(selectionValue);
notifyIfChanged(oldSelectionValue, selectionValue);
} finally {
updating = false;
}
}
public Object doGetValue() {
return Boolean.valueOf(button.isChecked());
}
public Object getValueType() {
return Boolean.TYPE;
}
public synchronized void dispose() {
super.dispose();
button.setOnCheckedChangeListener(null);
}
private void notifyIfChanged(boolean oldValue, boolean newValue) {
if (oldValue != newValue) {
fireValueChange(Diffs.createValueDiff(Boolean.valueOf(oldValue), Boolean.valueOf(newValue)));
}
}
}
Listing 2: Class ButtonObservableValue
A bit more difficult was the implementation of the class AndroidRealm, but with the studies of the documentation of the classes Looper and Handler from the Android SDK I've got the information needed to implement the three methods, syncExec, asyncExec and timerExec.
import org.eclipse.core.databinding.observable.Realm;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
/**
* Android Implementation Realm.
*
*/
public class AndroidRealm extends Realm {
private static final Handler handler = new Handler(Looper.getMainLooper());
public static void createDefault() {
setDefault(new AndroidRealm());
}
public boolean isCurrent() {
return Thread.currentThread() == Looper.getMainLooper().getThread();
}
@Override
public void asyncExec(Runnable runnable) {
handler.post(runnable);
}
@Override
protected void syncExec(final Runnable runnable) {
SynchronizedRunnable synchronizedRunnable = new SynchronizedRunnable(
runnable);
handler.post(synchronizedRunnable);
synchronizedRunnable.doWait();
}
@Override
public void timerExec(int milliseconds, Runnable runnable) {
+ runnable + ")");
handler.postDelayed(runnable, milliseconds);
}
}
Listing 3: Class AndroidRealm
On the model side it came to problems with the Java-Bean, more precisely with the Bean DataBinding from Eclipse. Its implementation uses the Java reflection mechanism. Because my focus was at the moment on view databinding and not on model databinding I simply replaced the JavaBean in my sample application with one the is based on UBeans from the UFaceKit project, which works without Java-Reflection and provides a junction with the Eclipse Databinding. With the UBeans and their DataBinding the model side runs error-free on Android.
Enumeration 2: UFaceKit UBean Bundles
import org.eclipse.ufacekit.core.ubean.UArrayBean;
public class ClockUBean extends UArrayBean {
public static final int TIME = 1;
public static final int MODE = 2;
public ClockUBean() {
setMode(ClockMode.RUN);
setTime(0);
}
@SuppressWarnings("unchecked")
public >t extends="" object=""< T getValueType(int featureId) {
switch (featureId) {
case TIME:
return (T) long.class;
case MODE:
return (T) ClockMode.class;
default:
return null;
}
}
public void setTime(long value) {
set(TIME, Long.valueOf(value));
}
public long getTime() {
return ((Long) get(TIME)).longValue();
}
public void setMode(ClockMode value) {
set(MODE, value);
}
public ClockMode getMode() {
return (ClockMode) get(MODE);
}
}
Listing 3: Class ClockUBean
The rest was as easy as pie: Create the layout consisting of a EditText and a ToggleButton. Copy the reusable classes from the Observable DataBinding Sample into the Android project. Transfer and adapt the code lines which interconnect model, view and controller (which reside in the main method in the SWT and Swing variants), into the Android Activity class - done.
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent"> <EditText android:id="@+id/time" android:gravity="right" android:layout_alignParentLeft="true" android:layout_toLeftOf="@+id/mode" android:layout_height="wrap_content" android:layout_width="fill_parent" /> <ToggleButton android:id="@+id/mode" android:layout_width="120px" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignBottom="@+id/time" android:layout_alignParentTop="true" /> </RelativeLayout>
Listing 4: Definition of the Android widgets and their layouts
Reusable classes from the Observable DataBinding sample
import org.eclipse.core.databinding.observable.value.IObservableValue;
import org.eclipse.core.databinding.observable.value.WritableValue;
import android.app.Activity;
import android.app.KeyguardManager;
import android.app.KeyguardManager.KeyguardLock;
import android.os.Bundle;
import android.widget.EditText;
import android.widget.ToggleButton;
import de.partmaster.databinding.android.AndroidEventConstants;
import de.partmaster.databinding.android.AndroidObservables;
import de.partmaster.databinding.observable.sample.android.R;
import de.partmaster.databinding.observable.ui.sample.ClockUBean;
import de.partmaster.databinding.observable.ui.sample.ClockMode;
import de.partmaster.databinding.observable.ui.sample.ClockService;
import de.partmaster.databinding.observable.ui.sample.ObservableClockController;
import de.partmaster.databinding.observable.ui.sample.ObservableClockView;
public class ClockViewActivity extends Activity implements ObservableClockView {
private IObservableValue timeTextObservable;
private IObservableValue timeEnabledObservable;
private IObservableValue modeSelectionObservable;
private IObservableValue modeTextObservable;
private ClockService clockService;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ToggleButton modeWidget = (ToggleButton) findViewById(R.id.mode);
modeWidget.setTextOn(ClockMode.SET.name());
modeWidget.setTextOff(ClockMode.RUN.name());
EditText timeWidget = (EditText) findViewById(R.id.time);
timeTextObservable = AndroidObservables.observeText(timeWidget,
AndroidEventConstants.Modify);
timeEnabledObservable = AndroidObservables.observeEnabled(timeWidget);
modeTextObservable = new WritableValue(AndroidObservables.getRealm());
modeSelectionObservable = AndroidObservables.observeSelection(modeWidget);
ClockUBean bean = new ClockUBean();
new ObservableClockController().bind(this, bean);
clockService = new ClockService(AndroidObservables.getRealm(), bean);
}
@Override
protected void onStart() {
super.onStart();
clockService.start();
}
@Override
protected void onStop() {
clockService.stop();
super.onStop();
}
@Override
public IObservableValue getModeSelection() {
return modeSelectionObservable;
}
@Override
public IObservableValue getTimeText() {
return timeTextObservable;
}
@Override
public IObservableValue getModeText() {
return modeTextObservable;
}
@Override
public IObservableValue getTimeEnabled() {
return timeEnabledObservable;
}
}
Listing 5: Class ClockViewActivity
It was an enjoyment, as the Observable DataBinding sample runs in the Android emulator finally!

Figure 1: Screenshot Android-Emulator with Clock-App
There was just one little blemish: The long waiting time, until the App after ckicking the run button in the Eclipse IDE starts in the Android emulator. The reason was mainly the overall size of the required Eclipse DataBinding JARs. But there was possibilities: First prune out all unneeded classes from the bundle org.eclipse.equinox.common and then above all the bundle com.ibm.icu ganz removed and replaced by the standard Java classes. As i noticed later, as an alternative to the replacement by the standard Java classes one can use in place of com.ibm.icu the bundle com.ibm.icu.base, which is a minimal implementation of the same packages and classes.
Size comparison Eclipse DataBinding JARs and Android APKs with DataBinding
After that the emulator load and start time is in the green area and the deployment and start an real Android hardware (my Samsung Galaxy) was really fast. As I detect that Android APKs can be shrinked by means of Proguard, (for this purpose the property proguard.config in file default.properties has to be set) I could shrink the APK to ca. 50 KB in the end and demonstrate that the usage of Eclipse DataBinding on Android don't have to lead bloated App sizes. In the main my first Android project brought pleasant experiences - and the verification, that Eclipse DataBinding is suitable for Android. In parallel I tried, to automate build and tests with the Maven-Android-Plugin. That succeded in the end but was combined with some headaches and nightly sessions, but that story I will preserve for another blog.