Getting Started with Data Binding Library in Android: Binding local data

Getting Started with Data Binding Library in Android: Binding local data

    In I/O 2015 Google announced a data binding library for Android. With data binding, you create an ongoing link between an element in the user interface and a value. Data binding is the process that establishes a connection between the application UI and business logic.
    As a developer we always try to do tasks with lesser code, findViewById() and setText() would be the things which will increase line of codes. Data binding eliminates needs of these methods.

Prerequisites

    The Data Binding Library offers both flexibility and broad compatibility - it's a support library, so you can use it with all Android platform versions back to Android 2.1 (API level 7+).
    To use data binding, Android Plugin for Gradle 1.5.0-alpha1 or higher is required. See how to update the Android Plugin for Gradle.
    Now, in this post, I will present some basic features of this library, which can make you developing Android apps faster!
    To get started with Data Binding, configure your app to use data binding, add the dataBinding element to your build.gradle file in the app module:
android {
   ...
   dataBinding{
      enabled=true
   }
}

Declaring in layout (xml) files

    Suppose you have a simple POJO file with it's own variables, setters and getters like this:
package info.devexchanges.simpledatabinding;

public class Cat {
    private String name;
    private int age;
    private String owner;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
    }
}
    In order to use data binding library with a specific layout, we need the layout files to have the root tag of layout followed by a data element and a view root element. Look at this main activity layout which enable  data binding:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <import type="android.view.View"/>
        <variable
            name="cat"
            type="info.devexchanges.simpledatabinding.Cat" />

        <variable
            name="handlers"
            type="info.devexchanges.simpledatabinding.MainActivity.OnClickHandler"/>
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="@dimen/activity_horizontal_margin">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:orientation="vertical">

            <TextView
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:gravity="center"
                android:text="Binding local data"
                android:textAppearance="?android:attr/textAppearanceLarge" />

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:layout_marginTop="30dp"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Cat Name: " />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@{cat.name}"
                    android:textStyle="bold" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Cat Age: " />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@{String.valueOf(cat.age)}"
                    android:textStyle="bold" />
            </LinearLayout>

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_gravity="center_horizontal"
                android:orientation="horizontal">

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Cat Owner: " />

                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@{cat.owner}"
                    android:textStyle="bold" />
            </LinearLayout>

            <Button
                android:id="@+id/btn_update"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:text="Update Cat information" />
        </LinearLayout>
    </RelativeLayout>
</layout>
    As you can see, the Cat class has 3 variables named name, age and owner. In the xml code above, I have written android:text="@{cat.name}" which will bind name value to that TextView (and similar with 2 remaining variables).

Configure in Java code

In the programmatically code of this activity, Establish data binding in onCreat():
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
        cat = new Cat("Tom", 2);
        mainBinding.setCat(cat);
    }
    You have noticed in above code that we have mentioned ActivityMainBinding but we have not declared it anywhere or where is that class? Your answer is it’s auto generated class for DataBinding. Binding class will be generated based on your layout file name. For example, if your layout name is activity_main.xml, you Binding class name will be ActivityMainBinding.
    Running your application, we'll have this output:

Update value with onClick event

    Data Binding allows you to write expressions handling events that are dispatched from the views (e.g. onClick()). Event attribute names are governed by the name of the listener method with a few exceptions. For example, View.OnLongClickListener has a method onLongClick(), so the attribute for this event is android:onLongClick. There are two ways to handle an event:
  • Method References: In your expressions, you can reference methods that conform to the signature of the listener method. When an expression evaluates to a method reference, Data Binding wraps the method reference and owner object in a listener, and sets that listener on the target view. If the expression evaluates to null, Data Binding does not create a listener and sets a null listener instead. 
  • Listener Bindings: These are lambda expressions that are evaluated when the event happens. Data Binding always creates a listener, which it sets on the view. When the event is dispatched, the listener evaluates the lambda expression.
    Here, in this post, I use the first way: Method References.
    Events can be bound to handler methods directly, similar to the way android:onClick can be assigned to a method in an Activity. One major advantage compared to the View#onClick attribute is that the expression is processed at compile time, so if the method does not exist or its signature is not correct, you receive a compile time error.
    The major difference between Method References and Listener Bindings is that the actual listener implementation is created when the data is bound, not when the event is triggered. If you prefer to evaluate the expression when the event happens, you should use listener binding.
    To assign an event to its handler, use a normal binding expression, with the value being the method name to call. So, in the MainActivity, providing a handler class simple like this:
public class OnClickHandler {
        public void onUpdateCat(View view) {
            cat.setName("Tom Tom");
            cat.setOwner("Hong Thai");
        }
    }
    Update activity_main.xml file with declaring this handler class:
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">

    <data>
        <import type="android.view.View"/>
        <variable
            name="cat"
            type="info.devexchanges.simpledatabinding.Cat" />

        <variable
            name="handlers"
            type="info.devexchanges.simpledatabinding.MainActivity.OnClickHandler"/>
    </data>

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:padding="@dimen/activity_horizontal_margin">

            ...

            <Button
                android:id="@+id/btn_update"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:onClick="@{handlers.onUpdateCat}"
                android:text="Update Cat information" />
        </LinearLayout>
    </RelativeLayout>
</layout>
    As you can see, never forget to add android:onClick attribute to your clicked view, and it's value is the method inside your handler class.
The next important work is updating your POJO class:
  • Make it extends from BaseObservable
  • Update getters methods by using @Bindable annotation
  • Notify property for changing its value for that use notifyPropertyChanged() in setters methods
And this is new code for Cat class:
Cat.java
package info.devexchanges.simpledatabinding;

import android.databinding.BaseObservable;
import android.databinding.Bindable;

public class Cat extends BaseObservable {
    private String name;
    private int age;
    private String owner;

    public Cat(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

    public void setName(String name) {
        this.name = name;
        notifyPropertyChanged(info.devexchanges.simpledatabinding.BR.name);
    }

    @Bindable
    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
        notifyPropertyChanged(info.devexchanges.simpledatabinding.BR.age);
    }

    @Bindable
    public String getOwner() {
        return owner;
    }

    public void setOwner(String owner) {
        this.owner = owner;
        notifyPropertyChanged(info.devexchanges.simpledatabinding.BR.owner);
    }
}
    Here BR is auto generated class like R file.
      Running your app, then click at the button, you'll have this result:

Final thoughts

    Through this post, you've learned some basic features of Data Binding Library in Android. I hope we have sparked your interest in this library, you have learned something new and will put this knowledge into practice, I will have more articles about this topic, so keep visiting!
    Read more:

Android Basic Training Course: Dealing with Threads

Android Basic Training Course: Dealing with Threads

    Users prefer vividly applications, do not like the slow application. Making your application flexibility for users is to use threading capability built into Android. This post will guide you through the issues related to the management of threads in Android and some options to keep sharp interface and responsive.

UI thread and background thread

    Every app has its own special thread that runs UI objects such as View objects; this thread is called the UI thread. Only objects running on the UI thread have access to other objects on that thread.
    The fact that there is some tasks work in the background (e.x: downloading data), because they run on a thread from a thread pool aren't running on your UI thread, they don't have access to UI objects. So, we said that they are running in the background threads.

Android threading constructs

    Android supports the usage of the Thread class to perform asynchronous processing.
Android also supplies the java.util.concurrent package to perform something in the background, for example: using the ThreadPools and Executor classes.
    If you need to update the user interface from a new Thread, you need to synchronize with the UI thread. The classes that help you solve this problem are Handler, AsyncTask,...
    In this post, I provide a sample project about using Thread with Handler, Runnable with Hanlder and how to use an AsyncTask. So, let's begin!

Handling background thread result with Handler

     From official Google doc: a Handler allows you to send and process Message and Runnable objects associated with a thread's MessageQueue. Each Handler instance is associated with a single thread and that thread's message queue. When you create a new Handler, it is bound to the thread / message queue of the thread that is creating it -- from that point on, it will deliver messages and runnables to that message queue and execute them as they come out of the message queue.
    There are two main uses for a Handler: (1) to schedule messages and runnables to be executed as some point in the future; and (2) to enqueue an action to be performed on a different thread than your own.
    A new Thread is defined by the code like this:
                new Thread() {
                    public void run() {
                        bitmap = downloadBitmap(url); // invoke downloading image work
                        imageHandler.sendEmptyMessage(0); //attach event handling with a Handler instance
                    }
                }.start(); //starting thread
    Now, creating a subclass of Handler to deliver messages, you must override handlerMessage() method and in order to avoid "Handler should be static or leaks might occur" error, you should initialized it with a WeakReference:
 private static class ImageHandler extends Handler {
        private final WeakReference<HandlerDemoActivity> weakRef;
        private HandlerDemoActivity activity;

        public ImageHandler(HandlerDemoActivity activity) {
            weakRef = new WeakReference<>(activity);
            this.activity = activity;
        }

        @Override
        public void handleMessage(Message msg) {
            activity.imageView.setImageBitmap(activity.bitmap);
            activity.progressDialog.dismiss();
            Toast.makeText(activity, "Download Successful!", Toast.LENGTH_SHORT).show();
        }
    }
    In this Activity, the "download file from URL" work will be invoked after use click a Button, this is full code of it:
package info.devexchanges.communicatingwithuithread;

import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.net.HttpURLConnection;

public class HandlerDemoActivity extends AppCompatActivity {
    private ProgressDialog progressDialog;
    private ImageView imageView;
    private String url = "http://i.imgur.com/h5YqScl.jpg";
    private Bitmap bitmap = null;
    private ImageHandler imageHandler;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_handler_demo);
        imageHandler = new ImageHandler(this);

        imageView = (ImageView) findViewById(R.id.imageView);
        Button btnDownload = (Button) findViewById(R.id.btn_download);
        btnDownload.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View arg0) {
                progressDialog = ProgressDialog.show(HandlerDemoActivity.this, "Loading", "Loading data...");
                new Thread() {
                    public void run() {
                        bitmap = downloadBitmap(url);
                        imageHandler.sendEmptyMessage(0);
                    }
                }.start();
            }
        });
    }

    private static class ImageHandler extends Handler {
        private final WeakReference<HandlerDemoActivity> weakRef;
        private HandlerDemoActivity activity;

        public ImageHandler(HandlerDemoActivity activity) {
            weakRef = new WeakReference<>(activity);
            this.activity = activity;
        }

        @Override
        public void handleMessage(Message msg) {
            activity.imageView.setImageBitmap(activity.bitmap);
            activity.progressDialog.dismiss();
            Toast.makeText(activity, "Download Successful!", Toast.LENGTH_SHORT).show();
        }
    }

    private Bitmap downloadBitmap(String urlString) {
        try {
            java.net.URL url = new java.net.URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoInput(true);
            connection.connect();
            InputStream input = connection.getInputStream();

            return BitmapFactory.decodeStream(input);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}
    And this is layout (xml file):
    After running app, we will have this result:
Downloading data...
Download process completed

Handler with Runnable

    Runnable represents a command that can be executed. It often declared when create a new Thread. With Handler, To process a Runnable you can use the post() method:
                //defining a new Thread with Runnable
                new Thread(new Runnable() {
                    public void run() {
                        bitmap = downloadBitmap("http://i.imgur.com/HR5QMOY.jpg");
                        handler.post(updateUI);
                    }
                }).start();
    And this is 2nd Runnable declaration:
final Runnable updateUI = new Runnable() {
        public void run() {
            if (bitmap != null) {
                imageView.setImageBitmap(bitmap);
                progressDialog.dismiss();
                Toast.makeText(HandlerWithRunnableActivity.this, "Download Successful!", Toast.LENGTH_SHORT).show();
            }
        }
    };
    Quite similar with Activity above, adding some important methods and reuse activity_handler_demo.xml layout, we have full code:
package info.devexchanges.communicatingwithuithread;

import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;

public class HandlerWithRunnableActivity extends AppCompatActivity {

    private Handler handler;
    private ImageView imageView;
    private Button btnDownload;
    private Bitmap bitmap;
    private ProgressDialog progressDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        handler = new Handler();
        setContentView(R.layout.activity_handler_demo);

        imageView = (ImageView) findViewById(R.id.imageView);
        btnDownload = (Button)findViewById(R.id.btn_download);
        btnDownload.setText("Download image by Runnable");

        btnDownload.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                progressDialog = ProgressDialog.show(HandlerWithRunnableActivity.this, "Loading", "Loading data...");

                //defining a new Thread with Runnable
                new Thread(new Runnable() {
                    public void run() {
                        bitmap = downloadBitmap("http://i.imgur.com/HR5QMOY.jpg");
                        handler.post(updateUI);
                    }
                }).start();
            }
        });
    }

    //Update download result (bitmap) to ImageView
    final Runnable updateUI = new Runnable() {
        public void run() {
            if (bitmap != null) {
                imageView.setImageBitmap(bitmap);
                progressDialog.dismiss();
                Toast.makeText(HandlerWithRunnableActivity.this, "Download Successful!", Toast.LENGTH_SHORT).show();
            }
        }
    };

    private Bitmap downloadBitmap(String urlString) {
        try {
            java.net.URL url = new java.net.URL(urlString);
            HttpURLConnection connection = (HttpURLConnection) url.openConnection();
            connection.setDoInput(true);
            connection.connect();
            InputStream input = connection.getInputStream();

            return BitmapFactory.decodeStream(input);
        } catch (IOException e) {
            e.printStackTrace();
            return null;
        }
    }
}
    Running this, the actual result will similar to the above case:

AsyncTask - a better way

    AsyncTask is an abstract Android class which helps the applications to handle the UI thread in efficient way. AsyncTask class allows us to perform long lasting tasks/background operations and show the result on the UI thread without affecting the main thread. This is an AsyncTask object lifecycle:

    Declaring an AsyncTask with it's parameters:
private class DownloadTask extends AsyncTask<Void, String, Bitmap> {}
    The important methods:
  • onPreExcute(): invoked on the UI thread before the task is executed. This step is normally used to setup the task, for instance by showing a progress bar in the user interface.
  • doInBackground():  running for long lasting time should be put in this method. When execute method is called in UI main thread, this method is called with the parameters passed. This is method that you must override.
  • onProgressUpdate(): invoked on the UI thread after a call to publishProgress(Progress...). The timing of the execution is undefined. This method is used to display any form of progress in the user interface while the background computation is still executing. For instance, it can be used to animate a progress bar or show logs in a text field.
  • onPostExecute(): invoked after background computation in doInBackground() method completes processing. Result of the doInBackground() is passed to this method.
    In this example, your AsyncTask instance will be used for downloading image from URL and this result (a Bitmap) will be set to ImageView, the downloaded percentages will be displayed by a ProgressBar:
private class DownloadTask extends AsyncTask<Void, String, Bitmap>  {
        /**
         * Downloading file in background thread
         */
        @Override
        protected Bitmap doInBackground(Void... params) {
            int count;
            Bitmap bitmap = null;
            try {
                URL url = new URL("http://i.imgur.com/3CBxbOj.jpg");
                URLConnection connection = url.openConnection();
                connection.connect();
                InputStream inputStream = url.openStream();

                //this input stream used for caculate download percentages
                InputStream inputStream1 = url.openStream();
                //decode Bitmap object from input stream
                bitmap = BitmapFactory.decodeStream(inputStream);

                byte data[] = new byte[1024];
                long total = 0;
                // getting file length
                int lenghtOfFile = connection.getContentLength();

                while ((count = inputStream1.read(data)) != -1) {
                    total += count;
                    // publishing the progress....
                    // After this onProgressUpdate will be called
                    publishProgress("" + (int) ((total * 100) / lenghtOfFile));
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

            return bitmap;
        }

        /**
         * Updating progress bar
         */
        protected void onProgressUpdate(String... progress) {
            progressBar.setProgress(Integer.parseInt(progress[0]));
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            imageView.setImageBitmap(bitmap);
            Toast.makeText(AsyncTaskActivity.this, "Download successful!", Toast.LENGTH_SHORT).show();
            btnDownload.setEnabled(true);
        }
    }
    Use this in Activity or Fragment by call:
AsyncTask asyncTask = new DownloadTask().execute();
    Full code for the Activity:
package info.devexchanges.communicatingwithuithread;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.Toast;

import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;

public class AsyncTaskActivity extends AppCompatActivity {

    private ProgressBar progressBar;
    private View btnDownload;
    private AsyncTask asyncTask;
    private ImageView imageView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_asynctask);

        imageView = (ImageView) findViewById(R.id.imageView);
        btnDownload = findViewById(R.id.btn_download);
        progressBar = (ProgressBar) findViewById(R.id.loading);

        btnDownload.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                asyncTask = new DownloadTask().execute();
                Toast.makeText(AsyncTaskActivity.this, "Downloading...", Toast.LENGTH_SHORT).show();
                btnDownload.setEnabled(false);
            }
        });
    }

    private class DownloadTask extends AsyncTask<Void, String, Bitmap> {
        /**
         * Downloading file in background thread
         */
        @Override
        protected Bitmap doInBackground(Void... params) {
            int count;
            Bitmap bitmap = null;
            try {
                URL url = new URL("http://i.imgur.com/3CBxbOj.jpg");
                URLConnection connection = url.openConnection();
                connection.connect();
                InputStream inputStream = url.openStream();
                
                //this input stream used for caculate download percentages
                InputStream inputStream1 = url.openStream();
                //decode Bitmap object from input stream
                bitmap = BitmapFactory.decodeStream(inputStream);

                byte data[] = new byte[1024];
                long total = 0;
                // getting file length
                int lenghtOfFile = connection.getContentLength();

                while ((count = inputStream1.read(data)) != -1) {
                    total += count;
                    // publishing the progress....
                    // After this onProgressUpdate will be called
                    publishProgress("" + (int) ((total * 100) / lenghtOfFile));
                }

            } catch (Exception e) {
                e.printStackTrace();
            }

            return bitmap;
        }

        /**
         * Updating progress bar
         */
        protected void onProgressUpdate(String... progress) {
            progressBar.setProgress(Integer.parseInt(progress[0]));
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            imageView.setImageBitmap(bitmap);
            Toast.makeText(AsyncTaskActivity.this, "Download successful!", Toast.LENGTH_SHORT).show();
            btnDownload.setEnabled(true);
        }
    }
}
    Running this Activity, we have this result:
Downloading data...
Download process completed

    Important Note: With downloading data from URL work, you must provide Internet permission in your AndroidManifest.xml:

Conclusions

    By this post, you've learned about creating and managing multi-threads in Android. You can choose the suitable way to handling background threads result with solutions above. Finally, you can get full code from @Github by clicking button below.