Android Dependency Injection [Dagger2 Example]

Dependency injection might sound like a very complex programming concept but is indeed very simple. In fact most programmers regularly use dependency injection without even realizing it. Dependency injection is basically providing the dependencies that an object needs instead of forcing it to construct them on its own. To simplify it let us suppose you have a House class which internally uses Wall and Pillar objects as shown below. Here Wall and Pillar are the dependencies for the House class.

public class House {

    Wall wall;

    Pillar pillar;

    public House() {
        wall = new Wall();
        pillar = new Pillar();
    }
    
}

Now as seen we instantiate both Wall and Pillar in the constructor. This will work fine but is against standard coding practice of separation of responsibility and reduces reusabilty and testabilty of your code. So instead you can pass instances of Wall and Pillar objects over the constructor as shown below. This is simple Dependency Injection. Here you are just passing the instances from outside the House class rather than creating them inside. You can also pass it using a setter method.

Now many of you must already be using dependency injection using constructor or setter methods in your Android applications. Then why should we use Dagger2 ? Most importantly it removes a lot of Boilerplate code from your application. It generates this code at compile time using annotation processing improving your code’s readibility and testability.

In order to demonstrate Dagger2  we will develop an Android application step by step. For ease of understanding we will use the same House example shown above

Please note that in this example we are not using Android specific dagger library because the aim of this tutorial is to understand dependency injection and Dagger2 in general.

Gradle dependencies

  • As with any Android library before beginning we need to add the following dependencies in our build.gradle.
    implementation 'com.google.dagger:dagger:2.22.1'
    annotationProcessor 'com.google.dagger:dagger-compiler:2.22.1'
  • As already mentioned we will use the same House example but with Dagger2 dependency injections. Following is our House class with its two dependencies Pillar and Wall
    public class House {
    
        Pillar pillar;
    
        Wall wall;
    
        public House(){
            Log.d(TAG,"House initialized");
        }
    
        public void startBuilding(){
            Log.d(TAG, "started building...");
        }
    
    }

    Following is Pillar class

    public class Pillar {
    
        public Pillar(){
            Log.d(TAG,"Pillar Initialized...");
        }
    
    }

    Following is the Wall class

    public class Wall {
    
        public Wall(){
            Log.d(TAG,"Wall initialized...");
        }
    }

@Inject

javax.inject.Inject annotation is the most widely used Dagger annotation. It can be used along with member fields, constructors and even methods. It is used to identify both objects requesting dependency injection and also constructors which are used to fulfill that dependency.

Constructor injection
@Inject annotation on constructor of a class enables Dagger to use that constructor for creating an instance of that class if there is dependency injection requested by that particular class object. Basically adding @Inject on to a constructor helps Dagger2 to satisfy dependencies of that object. Also if constructors annotated with @Inject has any parameters then it implies that injection is requested for those parameters and Dagger needs to fulfill it.

In the context of this example we add @Inject annotation to constructors of all three of our classes to ensure Dagger2 knows how to create an instance if there is dependency injection requested for any of them. Code snippets below is after adding annotation on the constructors.

public class House {

    Pillar pillar;

    Wall wall;

    @Inject
    public House(){
        Log.d(TAG,"House initialized");
    }

    public void startBuilding(){
        Log.d(TAG, "started building...");
    }

}

Constructor annotated in Pillar.java

public class Pillar {

    @Inject
    public Pillar(){
        Log.d(TAG,"Pillar Initialized...");
    }
}

Similarly Wall.java

public class Wall {

    @Inject
    public Wall(){
        Log.d(TAG,"Wall initialized...");
    }
}


Field Injection

@Inject annotation on class member fields denotes that dependency injection is requested for those fields. It also means we need to provide Dagger2 with a way to fulfill the requested injection. This could be done either by constructor injection or using Modules(which will be discussed later).

For this example we add @Inject annotation to both the member fields Wall and Pillar of House.java. This informs Dagger2 that injection is requested for both objects. As constructors of both the objects are already annotated, Dagger can instantiate them via that constructor and satisfy the dependency

public class House {

    @Inject
    Pillar pillar;

    @Inject
    Wall wall;

    @Inject
    public House(){
        Log.d(TAG,"House initialized");
    }

    public void startBuilding(){
        Log.d(TAG, "started building...");
    }

}

Method Injection
Apart from fields and constructors @Inject annotation can also be used on a method. It denotes that dependency injection is requested for the parameters of that method and similar to Field injection we need to provide Dagger2 with a way to satisfy the requested injection. Important thing to note here is that method annotated with @Inject is invoked directly by Dagger2.

So far we are not using @Inject annotation on method in this example.

Note that dependency injection in a class happens in a sequence i.e. First the constructor annotated with @Inject is invoked and its parameters are injected then field dependencies of the class are injected followed of dependency injection of parameters of methods annotated with Inject. But for this sequence to start constructor of the class needs to be annotated.

@Component

Component is used to initiate dependency injection. They start the dependency graph by informing Dagger2 which class is first requesting its dependencies to be injected via Dagger2. Apart from this they also help Dagger in addressing the dependencies by bundling all the dependency providers available for this instance. Essentially Component is required to connect dependency providers with the classes requesting dependency injection. Suprisingly, considering all the work they do they have a very simple implementation. You just need to define a interface with @Component annotation. Dagger2 generates a compile time implementation for this interface thereby removing all the boiler-plate code that you could have written.

If all of this sounds very complicated to you don’t panic. Just continue with the tutorial. Dagger2 is very simple once you get a hang of it

In this example we define a component MainComponent.java with a single method inject(). This kind of methods are known as member-injection method which take in a single parameter representing the class which needs its dependencies to be injected. In this case that class is MainActivity.java.

@Component
public interface MainComponent {

    /*
    * Member injection method to inject dependencies in MainActivity
    * */
    void inject(MainActivity mainActivity);

}

Now rebuild your Android application, this will generate the implementation for your interface by appending “Dagger” to your interface name. In this example generated file name will be DaggerMainComponent.java

  • Now in your MainActivity.java you can get instance of your interface MainComponent using the auto-generated DaggerMainComponent as shown below. Then you use this instance and call inject method passing in your current MainActivity instance as parameter. This, as already explained, will initiate dependency injection in your activity.
    public class MainActivity extends AppCompatActivity {
    
        /*
        * Instance variable with @Inject annotation denoting this dependency needs 
        * to be injected via Dagger
        * */
        @Inject
        House house;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            /*
            * Creating instance of interface MainComponent using auto-generated  
            * implementation DaggerMainComponent
            * */
            MainComponent mainComponent = DaggerMainComponent.builder().build();
            
            /*
            * This call initiates dependency injection in your class MainActivity*/
            mainComponent.inject(this);
    
        }
    }
    
  • Dagger2 now searches if any member fields or methods in your activity are requesting dependency injection. As noted House object is the only instance variable requesting for dependency injection and its constructor is already annotated with @Inject. Therefore Dagger instantiates House object immediately.
  • But House.java itself has two dependencies Wall and Pillar requesting injection and both theses objects also have constructors annotated with @Inject. Therefore soon after instantiating House object Dagger instantiates Wall and Pillar objects.
  • This creates a Dependency graph wherein your MainActivity had dependency on House object and internally your House object has dependency on Wall and Pillar objects. As of now Wall and Pillar objects don’t have any dependency but if that would have been the case our graph would have grown. Following is the visual representation of dependency graph
    Wall           Pillar
      |              |
      |______________|
              |
            House
  • If you would have noted we have added logs in all our constructors therefore lets quickly run our application and check if things are working as intended. We have pasted the screenshot of the logs below.dependency_inject
  • As seen in the screenshot first you House class is instantiated followed Pillar and Wall objects. All three dependencies we injected via Dagger2 with the help of @Inject annotation. I hope now you have a better understanding of how Dagger2 helps us in injecting dependencies without manually instantiating a single object.

@Module

So far we learnt that to inject a object dependency via Dagger2 we need to annotate the object’s constructor with @Inject. But that is not always possible like in the cases listed below

  1.  Interface dependency
  2.  Dependency from Third-party libraries
  3.  Object with constructors parameters which need to be configured at runtime .

This is where Modules come to our rescue. Modules are classes that help Dagger fulfill the dependencies requested, which cannot be fulfilled by constructor injections. A Module class is defined with a @Module annotation and has methods which help Dagger in addressing object dependencies matching their return type.

  • To further understand modules, let us add two more objects to our application- SteelRod.java and Cement.java. As you will note we don’t add @Inject annotation on the constructors for these classes. Module will help us in addressing dependencies of these objects
    public class SteelRod {
    
        public SteelRod(){
             Log.d(TAG, "SteelRod initliazed...");
        }
    }
    
    public class Cement {
    
        public Cement(){
            Log.d(TAG, "Cement initialized");
        }
    }
    
  • These two objects will be dependencies(fields) of our Pillar object. We add @Inject annotation on them to request for dependency injection.

    public class Pillar {
    
        @Inject
        Cement cement;
    
        @Inject
        SteelRod steelRod;
    
        public Pillar(){
            Log.d(TAG,"Pillar Initialized...");
        }
    }
    
  • Instead of adding annotation to the constructor of these two classes we define a new Module class as shown below which will provide these two dependencies. Note the @Provides annotation on both the methods . It informs Dagger that the respective methods can be used to address dependencies of objects matching its return type
    /*
    * Adding @Module annotation is mandatory while
    * defining your module
    * */
    @Module
    public class MainModule {
    
        /*
        * This method enables Dagger to fulfill dependency
        * of the Cement object
        * */
    
        @Provides
        public static Cement provideCement(){
            return new Cement();
        }
    
        /*
        * This method enables Dagger to fulfill dependency
        * of the SteelRod object
         * */
        @Provides
        public static SteelRod provideSteelRod(){
            return new SteelRod();
        }
    
    }
    
  • While defining Modules you need to note that Dagger matches return type of your method with requested dependency not the name of the method. Which means provideSteelRod() and provideCement() would satisfy the dependency of their respective objects even if they had different name as long as the return type matches.
  • Now this module needs to be attached to the Component. Remember how we mentioned that component helps Dagger in addressing dependencies. This is done with the help of Module. Once attached to a Component all the objects provided by the particular module will be available for Component’s instance.
  • Modules are attached to components by simply adding the module class name along with the @Component annotation as shown below. You can add multiple Modules to a single component.
    /*
    * Modules are added to the component by listing them
    * with the @Component annotation
    * */
    @Component (modules = MainModule.class)
    public interface MainComponent {
    
        /*
        * Member injection method to inject dependencies in MainActivity
        * */
        void inject(MainActivity mainActivity);
    
    }
  • On a side note, our dependency graph has now grown as Pillar object has two more dependencies SteelRod and Cement.
             SteelRod     Cement
                |           |
                |___________|
                      |
Wall               Pillar
  |                   |
  |___________________|
            |
          House


  • Now run the application and as seen in the screenshot below constructors of Cement and SteelRod classes are also invoked. Also note the sequence in which the classes are instantiated. As Cement and SteelRod are field dependencies of Pillar.java they are instantiated soon after Pillar.java and before Wall.java

Passing Runtime parameters with Modules

  • Many constructors require parameters at runtime for instantiation. With Dagger you can even pass these values with help of Modules.
  • In this example let us suppose that our SteelRod object needs integer parameter at runtime in constructor . With the help of Module you have to pass this value from Activity. Following is how the new constructor of SteelRod looks like
    public class SteelRod {
    
        public SteelRod(int rodDia) {
            Log.d(TAG, "SteelRod initliazed with " + rodDia);
        }
    }
  • First you need to add Constructor to your Module and convert its methods from static to non static. Previously our methods were not dependent on a instance of Module hence we had declared them static.
  • Declare a instance variable rodDia in the Module and initialize it with the value passed in constructor as shown below. We pass the same instance variable while calling the constructor of SteelRod object
    /*
    * Adding @Module annotation is mandatory while
    * defining your module
    * */
    @Module
    public class MainModule {
    
        private int rodDia;
    
        /*
        * Constructor for the module which takes in a integer parameter to
        * configure the local instance variable
        * */
    
        public MainModule(int rodDia) {
            this.rodDia = rodDia;
        }
    
        /*
        * This method enables Dagger to provide dependency
        * of the Cement object
        * */
    
        @Provides
        public Cement provideCement(){
            return new Cement();
        }
    
        /*
        * This method enables Dagger to provide dependency
        * of the SteelRod object
         * */
        @Provides
        public SteelRod provideSteelRod(){
            return new SteelRod(rodDia);
        }
    
    }
    
  • Now the question arises how to pass value to the Module constructor because till now Module instantiation was taken care by Dagger itself. But Dagger also provides a way to pass the module instance while building the component object as shown in the code snippet below.
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        int rodDia = 10;
        /*
        * Creating instance of interface MainComponent using auto-generated
        * implementation DaggerMainComponent
        * */
        MainComponent mainComponent = DaggerMainComponent.builder()
                .mainModule(new MainModule(rodDia))
                .build();
    
        /*
        * This call initiates dependency injection in your class MainActivity*/
        mainComponent.inject(this);
    
    }
  • Now let us run the application and check the logs. As seen in the screenshot below we are a able to pass runtime parameters to the constructor using module.

Lets wrap up for now

This finishes us with the basics of Dagger2. There is still a lot more we can do with Dagger but we tried to cover all the essentials in this tutorial. If you have any questions feel free to ask them in the comments below. Also checkout more amazing Android tutorials here. We will soon be back with more amazing Dagger tutorials

via GIPHY

Android Data binding Example [Simple Tutorial]

As Android developers we all were sick of  the multiple findViewById() in our Activity/Fragments. This call was mandatory for every view which needs to be accessed in your class. This added a lot of boiler-plate code in your application making it complex and difficult to maintain. For example we have a User class and wanted a TextView in layout to display the name field of User object. Below snippet shows how we used to achieve. Problem was this had to repeated for every field which needs to be binded to UI

 TextView textView = findViewById(R.id.text_view);
textView.setText(user.name);

Finally Google decided to get rid of it using the new Android Data Binding library as a part of Android Jetpack. This library allows developers to bind UI elements directly with the data source in the XML file itself. In this article we will be discussing how this library works and how to implement it in your Android application.

It is always to easy to understand any library when we develop an application using it. Therefore let us develop a simple Android application to display user details in the launch activity using Data binding. 

Enable Android Data Binding

  • You don’t need to add any gradle dependency in your application to enable Android Data Binding. It is a part of Support Repository which you can update in SDK Tools of SDK manager. Just make sure to add the following in your build.gradle to enable Data Binding
android {
    ...
    dataBinding {
        enabled = true
    }
}

Define the Data Source

  • As already mentioned Android Data Binding enables binding data directly with the UI elements. So in order to continue first we need to define a simple Data Model. Below is a sample UserModel class with member fields like nameage, imageUri etc.
    package com.example.databindingex;
    
    import java.util.Arrays;
    import java.util.List;
    
    public class UserModel {
    
        private String name;
        private String imageUrl;
        private int age;
        private List<String> hobbies;
    
        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 getImageUrl() {
            return imageUrl;
        }
    
        public void setImageUrl(String imageUrl) {
            this.imageUrl = imageUrl;
        }
    
        public List<String> getHobbies() {
            return hobbies;
        }
    
        public void setHobbies(List<String> hobbies) {
            this.hobbies = hobbies;
        }
    
    }
    
  • In a real world application, data for such model is fetched from server using a network request. But to simplify things in this example we are using dummy static data.
    package com.example.databindingex;
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    
    public class UserModel {
    
       ...
        
        /*
        * Returns a valid UserModel object with dummy data 
        * */
    
        public static UserModel getValidStub() {
            UserModel userModel = new UserModel();
            userModel.setName("Bob Vance");
            userModel.setAge(35);
            userModel.setImageUrl("https://randomuser.me/api/portraits/men/28.jpg");
            userModel.setHobbies(Arrays.asList(new String[]{"Reading books","Travelling","Listening music"}));
            
            return userModel;
        }
        
    }
    

Adding Data source to Layout

  • In usual scenario our layout file would look something like below. It defines all the views required to display the data with RelativeLayout as its root view.  It has three TextViews to display name age, hobbies. ImageView is used for displaying image and a Button on click of which we will emulate submit action.
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="16dp"
        tools:context=".MainActivity">
    
        <ImageView
            android:id="@+id/image_view"
            android:layout_width="120dp"
            android:layout_height="150dp"
            android:layout_marginRight="16dp"
            android:scaleType="fitXY"
            tools:src="@mipmap/ic_launcher" />
    
        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="150dp"
            android:layout_toRightOf="@id/image_view"
            android:orientation="vertical">
    
            <TextView
                android:id="@+id/name_text"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@android:color/black"
                android:textSize="18sp"
                android:textStyle="bold"
                tools:text="Rohit Kumar" />
    
            <TextView
                android:id="@+id/age_text"
                android:layout_marginTop="8dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textSize="14sp"
                android:textStyle="bold"
                tools:text="28" />
    
            <TextView
                android:id="@+id/hobbies_text"
                android:layout_marginTop="8dp"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:textColor="@android:color/darker_gray"
                android:textSize="14sp"
                android:textStyle="bold"
                tools:text="Playing Cricket" />
        </LinearLayout>
    
        <Button
            android:id="@+id/submit_btn"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/image_view"
            android:layout_marginTop="16dp"
            android:text="SUBMIT" />
    </RelativeLayout>
  • To enable Android Data Binding in a XML file the root element should be changed to <layout>. This new <layout> root will contain the normal view hierarchy and a <data> element which defines what data this layout will be binded to.
  • Therefore for this example too we will make the necessary changes in the layout file as shown below.
    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools">
    
        <data>
            <!-- Enable us to reference View class directly  -->
            <import type="android.view.View" />
            <!-- Defines a variable user of type UserModel -->
            <variable
                name="user"
                type="com.example.databindingex.UserModel" />
        </data>
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="16dp"
            tools:context=".MainActivity">
              
              
            ...
    
         
        </RelativeLayout>
    
    </layout>
  • As seen in the code snippet we add a variable of type UserModel with the name user. This name can now be used in expression syntax to reference the variable. I consider adding variable in layout file same as declaring a instance variable in a class. In both cases we define the variable name and type and then name is used to access the variable in the entire file.
  • You can also access any Class from your layout file using the <import> tag under the data element. This is similar to how we import classes in our Java files so that we can reference them easily. Usually <import> is used to access static fields and methods of that class.
  • In this example we import android.widget.View so that we are able to access static fields View.GONE and View.VISIBLE.

Binding data variable to layout elements

Expression syntax is used to bind value to any layout element. @{} inside a layout file denotes expression syntax. You can also use imported static methods and variables in the expression syntax.

Binding Strings and Integers

  • In this example we bind the first TextView in the layout to name field of the UserModel. Since name field of UserModel is of type String we are able to directly bind it with the TextView.
    <TextView
          android:id="@+id/name_text"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:textColor="@android:color/black"
          android:text="@{user.name}"
          android:textSize="18sp"
          android:textStyle="bold"
          tools:text="Rohit Kumar" />
  •  But age field of UserModel is of type int and when we bind any Integer type to TextView it automatically assumes that the integer is string resource id.
  • Therefore we need to find a way to convert Integer to String. Simplest way to do it is using the String.valueOf() static method. We don’t event need to import String as java.lang.* variables are available by default using databinding.
    <TextView
        android:id="@+id/age_text"
        android:layout_marginTop="8dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="14sp"
        android:text="@{String.valueOf(user.age)}"
        android:textStyle="bold"
        tools:text="28" />
    
  • Interesting thing to note about the above code snippet is that it seems we are able to access private fields (name and age) of the user variable in expression syntax. This is not the case. Expression syntax is always expected to be simpler and therefore we should avoid writing accessor method like @{user.getName()} . The expression parser automatically tries to find the Java Bean accessor name (getXxx() or isXxx()) for your property x.
  • Expression syntax also checks for null values by default. This means that if a TextView is set to @{user.name} and user is null a NullPointerException is not encountered but instead the TextView is set to default value of the field ‘name’ i.e. null.

Binding Collections

  • You must have already noticed that we have still not set text to the hobbies TextView (hobbies_text). This is because we intend to do so using the hobbies list of UserModel.
  • Databinding allows use of collections like Map, List and Array in the expression syntax. As you would do in any Java class first you need to import Collection type and then add data variable for it. You can then access the collection items in the expression syntax using the [] operation. This operator works for arrays,lists and even Maps.
  • In this example we wont need to import List as we can access it directly using the user.hobbies field. Code snippet below shows we set the text to first element of hobbies list using [] operator.
    <TextView
        android:id="@+id/hobbies_text"
        android:layout_marginTop="8dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/darker_gray"
        android:textSize="14sp"
        android:text="@{user.hobbies[0]}"
        android:textStyle="bold"
        tools:text="Playing Cricket" />

Operators in Expression syntax

  • Almost all the operators both mathematical and conditional are valid in expression syntax. In this example we use some of them while binding data.
  • In this example to avoid IndexOutOfBoundsException we use conditional operator to check the size of our hobbies list before binding the TextView to the first element in the list.
    <TextView
        android:id="@+id/hobbies_text"
        android:layout_marginTop="8dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/darker_gray"
        android:textSize="14sp"
        android:text="@{user.hobbies.size() > 0 ? user.hobbies[0]:@string/no_hobbies}"
        android:textStyle="bold"
        tools:text="Playing Cricket" />
  • Operators are used to determine whether to display Button or not based on user’s age. We use both comparison and ternary operator for this. Importing View class allowed us to access static field View.GONE and View.VISIBLE.

    <Button
        android:id="@+id/submit_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/image_view"
        android:layout_marginTop="16dp"
        android:visibility="@{user.age > 10 ? View.VISIBLE : View.GONE}"
        android:text="SUBMIT" />
  • Concatenation operator can also be easily used in expression syntax. In this example we just append two hobbies by adding a space between them using concatenation operator.
    <TextView
        android:id="@+id/hobbies_text"
        android:layout_marginTop="8dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/darker_gray"
        android:textSize="14sp"
        android:text="@{user.hobbies.size() > 1 ? user.hobbies[0]+' '+user.hobbies[1]:@string/no_hobbies}"
        android:textStyle="bold"
        tools:text="Playing Cricket" />

Inflating the layout file

  • Once you add all the variables and expression syntax in a layout file rebuild your project. Android Data Binding library now generates a Binding class for the layout file. This class is responsible for binding your layout file with data. Name of this Binding class is also as per the name of your layout file converted to Pascal case and with “Binding” appended at the end. Like in this example our activity_main.xml will have a binding class with the name ActivityMainBinding.java.
  • To instantiate this class and inflate layout the you can take any of the two approaches. If you know the Binding class name you can simply inflate the layout using the static method inflate() as shown below. This method inflates the layout and binds the object to it returning reference to the object.
    ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
    

    If you are not aware of the binding class type you can use DataBindingUtil method for inflating the layout and bind the object. This also returns you the reference to the binding class object

    ActivityMainBinding activityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main);
  • This binding class will generate all the logic behind the expression syntax, variables and import statements you added in the layout file. For example when you set text attribute to @{user.name} it is the binding class which interprets the expression syntax, checks for NullPointerException and then gets name field of UserModel object.
  • For every data variable in layout file a instance variable is created in the Binding this. You can therefore bind the data with using the setter method for that variable. The variable defined in layout is just a variable without any value so its important to set value to the variable for Andoid Data Binding to work. For this example we can bind value to variable using setUser() as shown below. Calling this setter method is what actually binds data to your layout.
    public class MainActivity extends AppCompatActivity {
        
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
            setContentView(binding.getRoot());
            UserModel userModel = UserModel.getValidStub();
            //Sets value to the data variable
            binding.setUser(userModel);
            
        }
    }

Handling Events with Expression Syntax

  • Expression syntax can be used to handle events dispatched from a view. This can be achieved in two ways- Method References and Listener Bindings. Lets suppose in this example we want to handle onClick event of the button. First we will a define a inner class and add it as variable to our layout file.
  • For this example we define a inner class ClickHandler with two methods with different signatures
    public class MainActivity extends AppCompatActivity {
    
        ...
               
        public class ClickHandler {
    
            public void handleClick(View view) {
                Toast.makeText(MainActivity.this, "Button clicked " + view.getId(), Toast.LENGTH_SHORT).show();
            }
    
            public void handleClick() {
                Toast.makeText(MainActivity.this, "Button clicked ", Toast.LENGTH_SHORT).show();
            }
    
        }
    }
    
    
  • Then we add a variable of type ClickHandler to the layout.
    <?xml version="1.0" encoding="utf-8"?>
    <layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools">
    
        <data>
            <!-- Enable us to reference View class directly  -->
            <import type="android.view.View" />
            <!-- Defines a variable user of type UserModel -->
            <variable
                name="user"
                type="com.example.databindingex.UserModel" />
            <!-- Defines a variable to handle click events -->
            <variable
                name="clickHandler"
                type="com.example.databindingex.MainActivity.ClickHandler" />
        </data>
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="16dp"
            tools:context=".MainActivity">
           
            ...
     
    
    </layout>
  • This clickHandler data variable will be handling various events from the layout so before beginning its also important to set the value to our variable.
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ActivityMainBinding binding = ActivityMainBinding.inflate(getLayoutInflater());
        setContentView(binding.getRoot());
        UserModel userModel = UserModel.getValidStub();
        //Sets value to the data variable
        binding.setUser(userModel);
        binding.setClickHandler(new ClickHandler());
    
    }
  • With Method References you can directly invoke handler method from your layout. The only condition is the handler method should have the same signature as the listener method. Android Data Binding library internally sets a listener to target view and invokes the handler method when the event is fired. Your method references expression is processed at compile time so if the method doesn’t exists or the signature doesn’t match you will receive a compile time error.
  • In this example we have defined a method handleClick(View) with the same signature as View’s onClick(View). This method will be invoked on button click using expression syntax
    <Button
        android:id="@+id/submit_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/image_view"
        android:layout_marginTop="16dp"
        android:onClick="@{clickHandler::handleClick}"
        android:visibility="@{user.age > 10 ? View.VISIBLE : View.GONE}"
        android:text="SUBMIT" />
  • Listener Binding allows you to invoke handler methods whose signature doesn’t match the listener method. In ClickHandler class we also declared a method with view parameter. We can invoke this method from expression syntax as shown in the code snippet below.
    <Button
        android:id="@+id/submit_btn"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/image_view"
        android:layout_marginTop="16dp"
        android:onClick="@{(view)->clickHandler.handleClick()}"
        android:visibility="@{user.age > 10 ? View.VISIBLE : View.GONE}"
        android:text="SUBMIT" />

Run the application

  • With all the layout elements binded to data lets run the application and see how this works out. Below is the screenshot of the application running Android Data binding example. As you can see it works perfectly fine and your code is much cleaner than before.Now try clicking the button and you will notice a Toast message as shown in the screenshot below. This confirms that our implementation for button click is working as expected.

 

Note : You must have already noticed that we have not handled displaying image in ImageView using data binding and therefore the ImageView is empty. This will be covered in our next tutorial. Stay Tuned!

Let’s Wrap up for now

This finishes us with the basics of Android Data binding example. I hope you are now much clear how data binding works and how it helps us in cleaning up boiler plate code. If you have any questions feel free to comment down below. But remember there is still a lot more to understand and do with Android Data binding which will be covered in next tutorials. Till then good bye!

If you liked this tutorial and wanted to read more Android Tutorials please visit this

Firebase Remote Config Android Example

Suppose its Christmas and you want to display your app in a new Christmas theme for all the users. You might simply think of sending an app update with the new theme but its not certain that all your users will download the update. Also sending an update for a theme change would be an unnecessary effort, considering that you have to do it for all the festivals. It is for scenarios like these that Firebase Remote Config was built.

Firebase Remote Config is a cloud service that lets you change the behavior and appearance of your app without requiring users to download an app update. Remote config basically allows you to maintain parameters on cloud which can be fetched from your application. These parameters are then used to define the changes you want to see in your application. Like in the above Christmas theme scenario, we can define a parameters with the text, color, images for the new theme which can again be fetched using Remote Config library from your app.

How Firebase Remote Config works ?

Remote Config library helps your app to fetch parameters from the firebase remote config server. It will be responsible for fetching the values and caching them in your app. To handle situations in which the server is not accessible or the parameters are not found we have to define default parameter values in the app. Once fetched from server these values are stored in cache for the next 12 hours.Which means any request in this period will return the cached values.

Remote Config implementation will just take very few lines of code because it handles all the complicated logic of fetching and caching on its own.This is mainly done by have three different configs as explained below

  • Default Config – This config contains default values defined in your app. If no matching keys are found on remote config server these default parameters are copied to the active config and returned to the client
  • Fetched Config – This contains most recently fetched  parameters from the remote config server. But this config cant be directly accessed from your app. You need to activate these config parameters which will copy parameter values from this config to the active config from where they can be directly accessed.
  • Active Config – This is the only config directly accessible from your app and it contains values from either Default or Fetched config.

Caching in Firebase Remote Config

Remote Config library implements parameter caching on its own. After the first fetch the parameters are saved into local cache. By default the cache expires in 12 hours but you can set a custom expiry for the cache using the fetch(time) method. For every fetch request it checks if the cached values are older than the specified expiry time. If yes it fetches for the new parameters. If not the cached values are only returned. Though you can change the cache expiry time for parameter values you are not allowed to call fetch API more than 5 times in an hour.

Don’t panic if you are still not clear how Remote config library works. Its implementation is very simple and the  following example will clear all your confusion

In this Firebase Remote Config example we will display a welcome message in our app using three parameters from the remote config- Welcome string, text size , text color.

Adding Firebase to your App

To start this integration first you need to register your app with Firebase. Go to Firebase Console  and follow the steps mentioned. Once the project is registered download the google-service.json file and add it to your Android project as shown in the screenshot below

Adding Gradle Dependencies

After registering your app with firebase you are provided with a set of dependencies which you need to add in your Android application. Apart from these you also need to add dependency for Remote Config library given below

implementation 'com.google.firebase:firebase-config:16.1.0'

In the end your app’s build.gradle should have the following

dependencies {
    implementation 'com.google.firebase:firebase-config:16.1.0' // Remote Config gradle dependency
    implementation 'com.google.firebase:firebase-core:16.0.4'   // Firebase gradle dependency
}
apply plugin: 'com.google.gms.google-services'   // Google Services plugin

Similarly your project’s build.gradle should have the following

buildscript {
    
    repositories {
        google()
        jcenter()
    }
    dependencies {
        classpath 'com.android.tools.build:gradle:3.2.1'
        classpath 'com.google.gms:google-services:4.0.1'  // Project Level Google Services dependency
       
    }
}

Note that you should always use the latest version of the gradle dependencies. Versions used in this example are latest at the time of publishing this post but it might not be the case always. You can find all the latest versions here.

Designing the Layout

  • In order to show the welcome message we need to define a layout and initialize it in an activity. For this we will have a simple layout with TextView– to display the welcome message and Button to fetch the latest parameters.
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <TextView
            android:layout_width="wrap_content"
            tools:text="Welcome to the app"
            android:id="@+id/text"
            android:layout_marginTop="32dp"
            android:layout_centerHorizontal="true"
            android:layout_height="wrap_content" />
    
        <Button
            android:layout_width="wrap_content"
            android:text="FETCH"
            android:id="@+id/fetch"
            android:layout_centerInParent="true"
            android:layout_height="wrap_content" />
    
    </RelativeLayout>

Initializing FirebaseRemoteConfig

  • We just need one object to work with Remote Config library i.e. FirebaseRemoteConfig. The same object is used to set defaults parameters, fetch new parameters or activate the fetched parameters. It is a singleton object and can be obtained using FirebaseRemoteConfig.getInstance() API as shown below
    public class MainActivity extends AppCompatActivity {
    
        private FirebaseRemoteConfig firebaseRemoteConfig;
    
        private Button button;
    
        private TextView textView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            button = findViewById(R.id.fetch);
            textView= findViewById(R.id.text);
         
            // Fetch singleton FirebaseRemoteConfig object
            firebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
  • Once initialized you can use the setConfigSettings() API to enable developer mode for remote config. Developer mode allows us to refresh the parameter cache any number of times. By default Firebase doesn’t allow us to fetch new parameters for more than 5 times in an hour as explained in the last section. For this example we enable developer mode as shown below
    firebaseRemoteConfig.setConfigSettings(new FirebaseRemoteConfigSettings.Builder()
                   .setDeveloperModeEnabled(true)
                   .build());

Defining Default Values

  • As already mentioned, in this example we will have a welcome message in our app based on three parameters from remote config. Each parameter needs to have unique key and value and it is mandatory for us to define default values for all the parameters locally in our application. This can be achieved  by having a resource XML file or defining a map as shown below
    <?xml version="1.0" encoding="utf-8"?>
    <defaultsMap>
        <entry> 
            <key>text_str</key>
            <value>Welcome to the app</value>
        </entry>
        <entry>
            <key>text_str</key>
            <value>14</value>
        </entry>
        <entry>
            <key>text_color</key>
            <value>#FF0000</value>
        </entry>
    </defaultsMap>

    Instead of a XML resource file you can instead define default values in a map as shown below. Either of them will work fine

    Map<String,Object> map =new HashMap<>();
    map.put("text_str","Welcome to the app");
    map.put("text_size",14);
    
    map.put("text_color","#FF0000");

    Keep note of the parameter keys used in your default map. You will have to use the same when defining parameters on Firebase Console

  • Now we set the default parameters for remote config using the setDefaults() API. We set the map from our XML file as default. This will ensure that if any parameter key is not found on cloud its value will be fetched from this map
    firebaseRemoteConfig.setDefaults(R.xml.default_map)
    

    Getting Parameter Values

  • firebaseRemoteConfig.get<>() is used to get parameter values from the config. As already explained this can be either be from your default config or can be from the remote server. This call doesn’t actually send request for the parameter values from server but instead gets the values stored in the active config. If we have not fetched the parameters from server it returns the default in-app parameter values instead
  • In the example we try to get three parameter values using the corresponding get APIs and the parameter keys. We use the three parameter values to define the string, text size and text color of our TextView.
public class MainActivity extends AppCompatActivity {

    private FirebaseRemoteConfig firebaseRemoteConfig;

    private Button button;

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        button = findViewById(R.id.fetch);
        textView= findViewById(R.id.text);
        firebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
        firebaseRemoteConfig.setConfigSettings(new FirebaseRemoteConfigSettings.Builder()
                .setDeveloperModeEnabled(true)
                .build());
        firebaseRemoteConfig.setDefaults(R.xml.default_map);
        firebaseRemoteConfig.setDefaults(map);
    
        textView.setTextColor(Color.parseColor(firebaseRemoteConfig.getString("text_color")));
        textView.setTextSize((float) firebaseRemoteConfig.getValue("text_size").asDouble());
        textView.setText(firebaseRemoteConfig.getString("text_str"));
    }
  • As seen in the above code snippet as soon as the activity is created (onCreate) we try to get the parameter values using the corresponding get<>() APIs and display the welcome text.

Fetching Latest Parameters

  • firebaseRemoteConfig.fetch() call initiates request to remote config server for the latest parameters and once fetched these are stored in local cache. By default these parameters expire in 12 hours but we can set custom expiry time using the fetch(long) method (the long argument denoting time in seconds). Within the expiry time, 12 hour by default, if we try calling fetch() again no request to server is sent instead the values stored in cache are returned. When requesting using fetch(long) the remote config client checks if the cached values are older than the seconds specified in method argument. If yes new parameters are requested else the cached value is returned.
  • In this example we initiate the fetch request on button click using the fetch(long) method. We set the custom expiry time as 0 so that every time our app gets the latest values from the config.
    button.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   /*
                    This will initiate fetching of parameters. We have set the expiry time as 0
                    which will ensure we get fresh parameters every time
                    */
                   firebaseRemoteConfig.fetch(0).addOnCompleteListener(new OnCompleteListener<Void>() {
                       @Override
                       public void onComplete(@NonNull Task<Void> task) {
                           if (task.isSuccessful()){
                               Toast.makeText(MainActivity.this, "Activated", Toast.LENGTH_SHORT).show();
                           }else {
                               Toast.makeText(MainActivity.this, "Not Activated", Toast.LENGTH_SHORT).show();
                           }
                       }
                   });
               }
           });

Activating Fetched Parameters

  • Even after a successful fetch request when you try to get the values using get<>() APIs old values are returned. Remember how they are three configs (Active, Fetched and Default) and only active config is accessible from your app. Therefore in order for the fetched config parameters to be available you need to activate them using firebaseRemoteConfig.activateFetched(). This will move all the parameters from the fetched config to active config thereby making them accessible from the app.
  • This concept of parameter activation is to improve user experience and avoid any abrupt changes in behavior or design of the app when you are still navigating the app.
  • In the following example we activate the fetched parameters in the OnClick listener as shown below. After activation if you try to retrieve values using get() APIs the new parameter values will be returned. In this example we just activate the new values but don’t “get” them immediately in OnClick listener
    button.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View v) {
                   /*
                    This will initiate fetching of parameters. We have set the expiry time as 0
                    which will ensure we get fresh parameters every time
                    */
                   firebaseRemoteConfig.fetch(60*5).addOnCompleteListener(new OnCompleteListener<Void>() {
                       @Override
                       public void onComplete(@NonNull Task<Void> task) {
                           if (task.isSuccessful()){
                               Toast.makeText(MainActivity.this, "Activated", Toast.LENGTH_SHORT).show();
                               /*
                               Activiting fetched parameters. The new parameters will now be available to your app
                                */
                               firebaseRemoteConfig.activateFetched();
                           }else {
                               Toast.makeText(MainActivity.this, "Not Activated", Toast.LENGTH_SHORT).show();
                           }
                       }
                   });
               }
           });

Just be reminded that so far we have not created any parameters on the Firebase Console. Since we only have default in app parameters defined these will only be returned even if we try to calling fetch() or get()

Complete Activity

This finishes our client side implementation for Firebase remote config. Our complete activity will now look as the code snippet below. 

public class MainActivity extends AppCompatActivity {

    private FirebaseRemoteConfig firebaseRemoteConfig;

    private Button button;

    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
       
        button = findViewById(R.id.fetch);
        textView= findViewById(R.id.text);
        firebaseRemoteConfig = FirebaseRemoteConfig.getInstance();
        firebaseRemoteConfig.setConfigSettings(new FirebaseRemoteConfigSettings.Builder()
                .setDeveloperModeEnabled(true)
                .build());
        firebaseRemoteConfig.setDefaults(R.xml.default_map);

        /*
        Setting color, size and string for TextView using parameters returned from
        remote config server
         */
        textView.setTextColor(Color.parseColor(firebaseRemoteConfig.getString("text_color")));
        textView.setTextSize((float) firebaseRemoteConfig.getValue("text_size").asDouble());
        textView.setText(firebaseRemoteConfig.getString("text_str"));

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                /*
                 This will initiate fetching of parameters. We have set the expiry time as 0
                 which will ensure we get fresh parameters every time
                 */
                firebaseRemoteConfig.fetch(60*5).addOnCompleteListener(new OnCompleteListener<Void>() {
                    @Override
                    public void onComplete(@NonNull Task<Void> task) {
                        if (task.isSuccessful()){
                            Toast.makeText(MainActivity.this, "Activated", Toast.LENGTH_SHORT).show();
                            /*
                            Activiting fetched parameters. The new parameters will now be available to your app
                             */
                            firebaseRemoteConfig.activateFetched();
                        }else {
                            Toast.makeText(MainActivity.this, "Not Activated", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
            }
        });

    }
}
  • Note that we initiate fetching latest params from server only on click of button. Getting parameters before that will return values from the default map. Therefore in first launch of app we see the default in app values displayed (Screenshot below)
  • Also note that we have not defined any parameters for this app on remote config server yet. So even clicking the button and requesting using fetch() would return the default in-app values only.

Adding Parameters to Console

In this section we will add the parameters to the remote config server. So far we had only defined the parameters in app

  • Go to Firebase Console and select the project your created in step 1.
  • Once the project is selected click Grow > Remote Config in the left pane. Look at screenshot for reference.
  • Now click the “Add your first parameter” button and then add a string as shown in the screenshot below. Keep the parameter key same as the default one defined in your app map. I created a parameter with string value “Hello Android Clarified! Its working”.
  • Similarly add parameters to define your text size and and text colour. Again the parameter key should be same as the one defined in your app
  • After adding you need to publish this parameters to make them accessible from your app. Just click the Publish Changes button on the upper right corner of your console. Look at the screenshot above reference

Fetching from Server

  • Now clicking “Fetch” button in your app will request the parameters values from server. But notice that even after clicking fetch the text immediately doesn’t change. This is because after the request is successful we just activate the latest parameters but don’t retrieve them to update the text. So in order for the changes to reflect you need to restart the app. This will ensure on onCreate() is called again and latest parameters are retrieved from active config using get<>() APIs.

Conclusion

This finishes our Firebase Remote Config example. If properly implemented Firebase remote config can give developers complete control over app behaviour across all the versions. I hope this tutorials helps you in understanding Firebase. Feel free to comment any questions.

If you liked this tutorial you can find more at Android Examples 

EventBus Android Example [Full Tutorial]

Imagine a scenario in which you have an Activity containing with 2 fragments and you want your activity to be notified of any event in your fragments, also the vice versa. Simple solution is to attach listeners to both your fragments and activity. Now as the number of events increase the code becomes complex also both the fragments and your activity need to be tightly coupled with each other. All this becomes very difficult to manage. To solve problems like these we have Android EventBus library. EventBus is open source lightweight android library which uses publisher-subscriber pattern to communicate between Android components.  Following are some of the advantages of using EventBus library

  • Loose coupling between event senders and receivers
  • Very easy to implement as it uses annotation processing
  • Enables sending events across threads.
  • Tiny jar yet powerful and highly optimized

In this tutorial we will demonstrate a complete EventBus Android Example explaining all its features and uses in an Android App

eventbus
Flow Diagram for EventBus

Add the dependency

First step in this EventBus android example is to add the following dependency in build.gradle of our project

implementation 'org.greenrobot:eventbus:3.0.0'

Posting Events

In this section we will learn how to send any event with EventBus library. This event will be received by all the registered event handlers across the application. Before sending an Event let us first define our custom event class. This is a simple POJO class which can be customized as per our use case. Since this is just an example we have a simple Java class MyCustomEvent. Look at the code snippet below for reference

public class MyCustomEvent{
    String data;

    public MyCustomEvent(String data){
        this.data=data;
    }
}

Now you can post this event by this simple one line code

EventBus.getDefault().post(new MyCustomEvent("Custom event data"));
  • EventBus.getDefault() returns an EventBus object . Calling the post method on this EventBus object will fire the Event. Simple, isn’t it?

Subscribing to Events

So far we saw how to send events using EventBus library. In this section we will learn how to listen to those events

In order to listen for events we need to register our subscriber with EventBus. Here subscriber refers to the android class/component where you intend to handle the event. Like in activity/fragment we should ideally register in onStart() method. Similarly our subscriber should be unregistered in onStop(). Look at the code snippet below for reference

public class MainActivity extends AppCompatActivity {

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

    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    public void onStop(){
        super.onStop();
        EventBus.getDefault().unregister(this);

    }
}
  • In the above snippet we register the subscriber(MainActivity.java) by calling EventBust.getDefault().register(this) in onStart() method. Similarly we unregister it using EventBust.getDefault().unregister(this) in onStop() method. This ensures that our activity will listen for events as long as its active.
  • With our activity now listening for events we now need to create a event handler to respond to these events. This can be done by adding a @Subscribe annotation to a method and specify the event (which you intend to handle) as method parameter. This method will then act as the event handler and will be invoked everytime the event is fired. Look at the code snippet below for reference.
/*
   Method will act as the event handler for MyCustomEvent
   */
   @Subscribe
   public void customEventReceived(MyCustomEvent event){
       textView.setText(event.data);
   }
  • In the above snippet customEventReceived(MyCustomEvent) method will be invoked everytime  MyCustomEvent is fired as long as the activity is active. Similarly you can add multiple event handlers for different events in a single class.
  • This finishes the basic implementation of EventBus. As you must have seen its very loosely coupled as you neither need to attach any listener nor need to manually send event to multiple receivers. This simplifies communication between multiple Android components.

As of EventBus 3 you can choose any name for your event handler method. Till EventBus 2.0 you had to follow a naming convention

Working with Threads

One of the biggest advantages with EventBus is its ability to post events across threads with minimal effort. This means you can post events to any thread (background or UI). This becomes very useful as in Android all UI changes need to be done on main thread whereas network operations or any long running task should be done on a separate background thread. With EventBus you just need to add a annotation specifying thread mode on your event handler method and this will ensure that this method is invoked on the specified thread. Following are the different thread modes

  • ThreadMode.Posting
    This is the default ThreadMode for a event handler if no ThreadMode is specified. This mode implies that the event handler method will be called on the same thread as the one posting the event. As there is no thread switching involved this is very efficient. All the event handlers are called synchronously once the event is posted. We should avoid any long running tasks in the event handler as that might block the posting thread, which can also be the main thread. Look at the snippet below for reference.

    @Subscribe(threadMode = ThreadMode.POSTING)
    public void customEventReceived(MyCustomEvent event){
       Toast.makeText(this,"Custom Event Received",Toast.LENGTH_SHORT).show();
    }
  • ThreadMode.MAIN
    This mode implies that the event handler will be called on the main thread. Therefore we should avoid running any long tasks in the event handler which could block the main thread. If the thread posting this event is also the main thread then all the event handlers are called synchronously.

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void customEventReceived(MyCustomEvent event){
       Toast.makeText(this,"Custom Event Received",Toast.LENGTH_SHORT).show();
    }
  • ThreadMode.Background
    This mode implies that the event handler will be called on the background thread. If the event is also posted from a background thread then the event handler is called on the same thread. Whereas if the event is posted from main thread then the event handler is invoked on a separate background thread. For this mode a single background thread is used to post all the events sequentially. As with other modes, we should running long tasks in this mode as that will block the single background thread.

    @Subscribe(threadMode = ThreadMode.BACKGROUND)
    public void customEventReceived(MyCustomEvent event){
       Toast.makeText(this,"Custom Event Received",Toast.LENGTH_SHORT).show();
    }
  • ThreadMode.Async
    This mode implies that the event handler method will always be called on a separate thread. This thread is different from posting thread or the main thread. In this mode the EventBus doesn’t wait for the event handler to complete its execution, thus this is useful when we want to run a long task in our event handler. For every event handler EventBus creates a new background thread which also means developers should avoid running a lot of event handler methods with this mode at the same time as that might lead to multiple concurrent threads.

    @Subscribe(threadMode = ThreadMode.ASYNC)
    public void customEventReceived(MyCustomEvent event){
       Toast.makeText(this,"Custom Event Received",Toast.LENGTH_SHORT).show();
    }

Event Handler Priority

With EventBus you can even assign a priority to a event handler method which will determine the sequence in which the event handlers will be executed. The default priority is 0. This priority is only valid among event handler methods of the same mode which means that it wont effect the order of delivery for event handlers with different ThreadModes.

@Subscribe(priority = 1)
public void customEventReceived(MyCustomEvent event){
    
}

Canceling Event delivery

One very interesting feature of EventBus library is the ability to cancel further event delivery from a Event Handler method. This could come very handy for a lot of use cases where you would like to stop event delivery midway. Calling  EventBus.getDefault().cancelEventDelivery(event)  from a event handler method would cancel all the further delivery of events. Look at the code snippet below for reference.

@Subscribe(threadMode = ThreadMode.BACKGROUND)
public void customEventReceived(MyCustomEvent event){
    EventBus.getDefault().cancelEventDelivery(event);
}

Conclusion

This brings us to the end of EventBus Android example. If you liked this tutorial do checkout some of our other tutorials at Android Examples and if you have any questions feel free to comment below.

Butterknife Android Example [Complete Tutorial]

butterknife_android_example
ButterKnife Library

As developers we always aim to remove all kinds of boilerplate code from our applications. But with Android application sometimes  we are forced to do similar view initialization in almost every view components (Activities, Fragments etc). Here I am talking about initialization with findViewById() , setOnClickListener(), setOnTouchListener which is mandatory to be repeated for all the views we are working with. Fortunately, Jake Wharton from Square has come with ButterKnife Library which uses annotation processing to removes all the unnecessary, repetetive code from your application. Before beginning with our Butterknife android example let us first understand how this library works.

What is Android Butterknife ?

Butterknife is a lightweight view binding library to simplify finding views and attaching listeners by binding them with annotations in Android components like Activity, Fragment or Dialog. Here annotation processing is used at compile time which automatically generates new simple Java classes containing logic to handle view initialization. Basically  at compile time annotation processor scans through all your files looking for annotations and processes them to creating new Java files. Following are some of the advantages of use Butterknife library in your application

  • Removes repetitive findViewById calls from your code by using @BindView directly  on fields.
  • Removes all the anonymous inner classes created to handle events like clicks with setOnClickListener(). Instead we append  @OnClick annotation with methods which handle the action. 
  • Removes the resource lookups by using resource annotations on fields

In the following Butterknife Android example we will explore the uses of Butterknife Library by implementing various annotations

Adding Gradle Dependencies

Before beginning with the implementation of Butterknife android example we need to add the following dependencies in build.gradle

dependencies {
  implementation 'com.jakewharton:butterknife:9.0.0-rc1'
  annotationProcessor 'com.jakewharton:butterknife-compiler:9.0.0-rc1'
}

Initializing  Butterknife

View binding with Butterknife can be performed in an activity, view or even dialog. But before adding annotations we need to initialize Butterknife library. We initialize it by specifying a target component to search for annotations and root view to search views.

Below are some of the APIs used to specify the target component and rootview

  • bind(android.app.Activity target)
    Binds annotated fields and methods in the specified Activity.
  • bind(android.app.Dialog target)
    Binds annotated fields and methods in the specified Dialog.
  • bind(java.lang.Object target, android.view.View source)
    Binds annotated fields and methods in the specified target using the source View as the view root.
  • bind(android.view.View target)
    Binds annotated fields and methods in the specified View.

Below code snippet shows how to initialize ButterKnife in your Activity.

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /*
        Below call initializes Butterknife library to find and process annotated fields and
        methods in the current activity. Current activity layout is used as the view root.
         */
        ButterKnife.bind(this);
    }
}

Similarly we can initialize Butterknife in a Fragment. We just need to pass the extra method paramater “view”  representing the rootview.

public class ExampleFragment extends Fragment {

    public View onCreateView(LayoutInflater layoutInflater, ViewGroup parent, Bundle bundle) {
        View view = layoutInflater.inflate(R.layout.fragment_example,parent,false);
        /*
        Below call initializes ButterKnife library to find the annotated field and methods in the 
        current class and set the variable view as the root view
         */
        ButterKnife.bind(this,view);
        return view;
    }

}

Apart from Activity and Fragment, you can also use Butterknife in other components  like ViewHolder, Adapters etc. Below example shows how to initialize butter knife in an Adapter’s getView()

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    View v = LayoutInflater.from(context).inflate(R.layout.item_spinner, null);
    ButterKnife.bind(this,v);

    return v;
}

Similarly we can initialize Butterknife for custom views, dialogs etc

Initializing Views

Initializing views is the first thing we do in our Android component. Whether its a Activity or Fragment first several lines of our code is always occupied by multiple findViewById() calls to initialize various views inside our layout. Consider the example below, here we initialize and attach listeners to all the four views as soon as the activity is created.

public class MainActivity extends AppCompatActivity {

    Button buttonOne;

    Button buttonTwo;

    TextView textOne;

    TextView textTwo;

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

        buttonOne = findViewById(R.id.button_one);
        buttonTwo = findViewById(R.id.button_two);
        textOne = findViewById(R.id.text_view_one);
        textTwo = findViewById(R.id.text_view_two);

        buttonOne.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("AndroidClarified","Button One Clicked");
            }
        });

        buttonTwo.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.d("AndroidClarified","Button Two Clicked");
            }
        });
    }
}

@BindView, @OnClick

Now as the number of views increase in our layout so does the above code. For every new view we need to initialize using findViewById() and then add a anonymous class if we need to listen for any events. This approach is tiring and adds a lot of boilerplate code in our application.  Now for the same views we use Butterknife library to initialize and attach click listeners. Look at the code snippet below. The same is achieved with lesser code which is also easy to understand.

public class MainActivity extends AppCompatActivity {

    /*
    @BindView annotation is used to initialize views
     */
    @BindView(R.id.button_one)
    Button buttonOne;

    @BindView(R.id.button_one)
    Button buttonTwo;

    @BindView(R.id.text_view_one)
    TextView textOne;

    @BindView(R.id.text_view_two)
    TextView textTwo;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        /*
        Below call initializes Butterknife library to find and process annotated fields and
        methods in the current activity. Current activity layout is used as the view root.
         */
        ButterKnife.bind(this);

    }

    /*
       @OnClick annotation denoted that the respective method will be invoked on click of 
       the view
     */
    @OnClick(R.id.button_one)
    public void buttonOneClicked() {
        Log.d("AndroidClarified","Button One Clicked");
    }
    
    @OnClick(R.id.button_two)
    public void buttonTwoClicked(){
        Log.d("AndroidClarified","Button Two Clicked");
    }
}
  • As you must have noticed, we use @BindView annotation to initialize the view. Adding that annotation casts the view with the specified id from the root view to the field.
  • Similarly @OnClick annotation automatically attaches a listener and will invoke the respective method on click of the  view i.e. Clicking of button one (with id R.id.button_one) will invoke method buttonOneClicked()
  • Any number of parameters from OnClickListener#onClick(android.view.View) may be used with the
    method. This means you can either have no arguments, as in this example, or specify View as the argument.
  • If multiple buttons have the same action you can specify multiple ids with @OnClick annotation as shown below.
     
     @OnClick({R.id.button_one,R.id.button_two})
    public void buttonOneClicked() {
        Log.d("AndroidClarified","Buttons Clicked");
    }
    
  • Note that all the views binded with annotation should be present in the target layout. If not an exception is encountered. To avoid an exception you can add @Nullable annotation to the binding. This prevents from the exception being thrown if the view is not found. Look at the code snippet below for reference
    @Nullable
    @BindView(R.id.text_view_two)
    TextView textTwo;

@OnTouch, @OnLongClick

  • Similar to @OnClick we have have annotations like @OnTouch, @OnLongClick etc which help us to handle various events on the view. As the name suggests, @OnTouch is to handle touch events and @OnLongClick to handle long click events. One important thing to note is the number of arguments you are adding on the method with annotation.
  • @OnTouch annotation will bind the method to OnTouchListener#onTouch(View, MotionEvent) listener of the view for each id specified. Both method arguments of the listener, View and MotionEvent , are optional but it has to always return boolean
    @OnTouch(R.id.text_view_one)
    boolean onTouch() {
        Toast.makeText(this, "Touched!", Toast.LENGTH_SHORT).show();
        return false;
    }
  • @OnLongClick annotation will bind the method to  OnLongClickListener#onLongClick(View) listener of the view for each id specified. No method argument is mandatory but it has to always return boolean.
    @OnLongClick(R.id.text_view_two)
    boolean onLongClick() {
        Toast.makeText(this, "On Long Clicked!", Toast.LENGTH_SHORT).show();
        return false;
    
    }

@OnCheckedChange, @OnItemSelected , @OnItemClick

  • Butterknife even provides us with annotations to handle events on custom views like CompoundButton and AdapterView(Spinner, ListView) . Similar to other annotations this also needs to be a binded to a method along with the id of the view.
  • @OnCheckedChange annotation will bind the method to the OnCheckedChangeListener(CompoundButton,boolean) of the view for each id specified. The only mandatory argument for the binded method is boolean. You can add a CompoundButton  argument as per your use case.
    @OnCheckedChanged(R.id.checker)
       void onChecked(boolean checked) {
           Toast.makeText(this, checked ? "Checked!" : "Unchecked!", Toast.LENGTH_SHORT).show();
       }
  • @OnItemSelected annotation will bind the method to OnItemSelectedListener#onItemSelected(AdapterView, View, int,long) of the view of each of the specified. This works very well with ListViews, Spinner etc. The only mandatory method argument for the binded method is integer position.
    @OnItemSelected(R.id.example_list) 
    void onItemSelected(int position) {
        Toast.makeText(this, "Selected position " + position + "!", Toast.LENGTH_SHORT).show();
     }

Important thing to note is with Butterknife library we dont we need to initialize view with @BindView before attaching listener. You can directly bind a method to any listener using @OnClick, @OnTouch etc and the it will be invoked

Binding resources

In the above section we discussed how to bind fields to views and methods to view event listeners using Butterknife annotations. Similarly we can bind fields to android resources using different Butterknife annotations. Here resources can be anything like drawables, string , dimension ,color code, arrays etc. This will remove all the boilerplate code and save a lot of developer effort. Also its super easy to implement, you just need to bind the resources using the respective annotation and providing the resource id. The code snippet below is self explanatory

@BindColor(R.color.colorPrimary)
int primaryColor;

@BindDimen(R.dimen.dimen_9dp)
int myDimen;

@BindString(R.string.app_name)
String appName;

@BindDrawable(R.drawable.ic_launcher_background)
Drawable bgDrawable;

/*
@BindBitmap directly binds a drawable image into a Bitmap field.
 */
@BindBitmap(R.drawable.statue)
Bitmap bitmap;
  • Same as it is for @BindView , @OnClick etc it is important to initialize ButterKnife for the resource binding to work

Conclusion

This finishes our Butterknife Android example. I hope you are now clear with its implementation and advantages. If not feel free ask any question in the comments below. Similar to Butterknife, there is one more very important library which helps you upload images from your application. Read more about it here .

[Android Example] Pick Image from Gallery or Camera

Nowadays, images are a important part of almost every mobile app. Some of these apps have their own camera and gallery to capture or pick image. This is mostly common for apps which are used for image editing(Instagram). But not every app might have the same use case. Not every app needs to design a camera/gallery to allow user to pick image. Android provides us a way to use the system Camera and Gallery app to select image. This is possible with the help of Intent.

Intent in itself is very useful concept in Android. If you are not sure of how they work read this.

In this example we will develop an application to pick an image from Gallery or capture an Image using the system Camera app. 

Pick Image from Gallery Example

Before beginning you need to make sure your app has READ_EXTERNAL_STORAGE permission. To declare this you need to add the following in your AndroidManifest.xml .

<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

For devices on or above Marshamallow version, make sure you request the permission at runtime as shown here

Step 1: Create an Intent

Intent is how android components contact each other. Therefore to open the Gallery app you need to create an Intent describing the task and launch it. For launching Gallery app action used will be ACTION_PICK.

private void pickFromGallery(){
       //Create an Intent with action as ACTION_PICK
       Intent intent=new Intent(Intent.ACTION_PICK);
       // Sets the type as image/*. This ensures only components of type image are selected
       intent.setType("image/*");
       //We pass an extra array with the accepted mime types. This will ensure only components with these MIME types as targeted.
       String[] mimeTypes = {"image/jpeg", "image/png"};
       intent.putExtra(Intent.EXTRA_MIME_TYPES,mimeTypes);
       // Launching the Intent 
       startActivityForResult(intent,GALLERY_REQUEST_CODE);

   }
  • Set the action to ACTION_PICK and type to “image/*” this ensures only components which can handle picking of images are targeted
  • You must have noticed that we pass an extra array of MIME types even after setting the type as image/*. This ensures that only components with jpeg and png images should be activated with this Intent.
  • Also we launch the Intent with startActivityForResult. This will ensure that your activity receives a callback once the image is selected. Integer request code needs to be provided when launching the Intent. This acts as an identifier when the target(Gallery app) sends back the image.
  • Apart from the system Gallery app, user might have some other third party apps installed which meet the Intent description. In this case Android launches a chooser. If no such apps are installed directly Gallery app is launched.

    pick_image
    Chooser when multiple components satisfy the Intent

Step 2: Receiving the selected Image

To receive the image selected from Gallery you need to override the method onActivityResult() as shown below

public void onActivityResult(int requestCode,int resultCode,Intent data){

      // Result code is RESULT_OK only if the user selects an Image
      if (resultCode == Activity.RESULT_OK)
      switch (requestCode){
          case GALLERY_REQUEST_CODE:
              //data.getData returns the content URI for the selected Image
              Uri selectedImage = data.getData();
              imageView.setImageURI(selectedImage);
              break;
      }

  }
  • You receive the information about the selected image wrapped in an Intent in the onActivityResult() callback.
  • In the above code snippet we retrieve the content URI of the image by calling getData() on the returned Intent object. We use the same URI to display the image in your ImageView.
  • But content URI is not same as absolute file path. You can retrieve the absolute file path from the content URI as shown in code snippet below
public void onActivityResult(int requestCode,int resultCode,Intent data){

    // Result code is RESULT_OK only if the user selects an Image
    if (resultCode == Activity.RESULT_OK)
    switch (requestCode){
        case GALLERY_REQUEST_CODE:
            //data.getData return the content URI for the selected Image
            Uri selectedImage = data.getData();

            String[] filePathColumn = { MediaStore.Images.Media.DATA };
            // Get the cursor
            Cursor cursor = getContentResolver().query(selectedImage, filePathColumn, null, null, null);
            // Move to first row
            cursor.moveToFirst();
            //Get the column index of MediaStore.Images.Media.DATA
            int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
            //Gets the String value in the column
            String imgDecodableString = cursor.getString(columnIndex);

            cursor.close();
            // Set the Image in ImageView after decoding the String
            imageView.setImageBitmap(BitmapFactory.decodeFile(imgDecodableString));

            break;
      
    }

}

Capturing an Image with Camera

In this section instead of selecting photo from gallery app we will allow users to click a photo using camera app and display it in your application.

Since we are clicking a new photo and saving it we will need to add WRITE_EXTERNAL_STORAGE permission in the AndroidManifest.xml

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

For devices running on and above Android Marshmallow version, we need to ask this permission at runtime as shown here

Nougot

Before Nougot was released we were creating and passing a file path to the camera app to save the captured image. Since Nougot version Android has restricted apps to share file URIs with scheme “file://”. Basically you cannot pass a file path with “file://” along with the Intent. You are only allowed to share URIs with content:// scheme. This is where FileProvider comes into picture

FileProvider is a special subclass of ContentProvider that facilitates secure sharing of files associated with an app by creating a content:// Uri for a file instead of a file:/// Uri.

In this example we will be implementing the same

Step 1: Create file_paths.xml

First we should create a XML file to specify the location of the files for which you will be requesting content URIs. Your XML file should have a  <paths> element with some child elements. Each child elements needs to have two attributes “name” and “path”. The path attribute is to mention the sub-directory location  and name attribute will replace the sub-directory name in the content URI shared. Some child element examples are below

  • <external-path name=namepath=path/>
     external-path child element represents that you intend to share files from the device public external storage.
  • <external-files-path name=namepath=path/>
     external-files-path child element represents that you intend to share files from the app’s private external storage. You can read more about this here

For this example we create a file by the name file_paths.xml in xml directory and add the following code in it.

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

The above code snippet tells FileProvider that you intend to request content URIs for the root folder (path=”.”) of your public external storage with the name  external_files. The <paths> element can have multiple child elements

Step 2:

To implement FileProvider in your app you need to add the provider tag in your AndroidManifest.xml as shown below. We provide the path for the file created in step one for attribute resource.

<provider
         android:name="android.support.v4.content.FileProvider"
         android:authorities="${applicationId}.provider"
         android:exported="false"
         android:grantUriPermissions="true">
         <meta-data
             android:name="android.support.FILE_PROVIDER_PATHS"
             android:resource="@xml/file_paths">
         </meta-data>
     </provider>

Set the android:nameattribute to android.support.v4.content.FileProvider. Set the android:authorities attribute to a URI authority based on a domain you control; for example, if you control the domain mydomain.com you should use the authority com.mydomain.fileprovider. Set the android:exported attribute to false; the FileProvider does not need to be public. Set the android:grantUriPermissions attribute to true, to allow you to grant temporary access to files

Step 3: Create file path to store Image

Since camera will be capturing a new image we need to provide it with a file path to save that image. In this step we have a method createImageFile() which returns a file  created in DCIM/Camera folder of external storage.

private String cameraFilePath;

private File createImageFile() throws IOException {
        // Create an image file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        String imageFileName = "JPEG_" + timeStamp + "_";
        //This is the directory in which the file will be created. This is the default location of Camera photos
        File storageDir = new File(Environment.getExternalStoragePublicDirectory(
                Environment.DIRECTORY_DCIM), "Camera");
        File image = File.createTempFile(
                imageFileName,  /* prefix */
                ".jpg",         /* suffix */
                storageDir      /* directory */
        );
        // Save a file: path for using again
        cameraFilePath = "file://" + image.getAbsolutePath();
        return image;
    }
  • We declare a global string variable cameraFilePath. This is to store the path  of the file created in this method. This variable will be used in next step.
  • File name is generated at random using the current date
  • The parent  folder for this file is DCIM/Camera. This is the default location for all the Images clicked via Camera

Step 3: Launch an Intent

To start the  camera app we need to create an Intent describing the task and launch it. Action used for opening Camera app is MediaStore.ACTION_IMAGE_CAPTURE.

private void captureFromCamera() {
       try {
           Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
           intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(this, BuildConfig.APPLICATION_ID + ".provider", createImageFile()));
           startActivityForResult(intent, CAMERA_REQUEST_CODE);
       } catch (IOException ex) {
           ex.printStackTrace();
       }
   }
  • Note that we share the file path using FileProvider API getUriForFile(). It takes three parameter – Context, FileProvider authority and the new file created.  The getUriForFile() method returns a content URI which can be shared with other apps.
  •  We launch the Intent using startActivityForResult() and pass a unique integer identifier to identify this Intent.
  • The camera app then saves the new image into content URI  shared. Note that content URI actually points to the file created in createImageFile() method. We already have path for this file in our global variable cameraFilePath.

Step 4: Receiving the captured Image

Once the camera captures an image we receive a callback onActivityResult() in our activity. We can use the integer request code to match if the callback is invoked for camera Intent only.

public void onActivityResult(int requestCode,int resultCode,Intent data){

      // Result code is RESULT_OK only if the user captures an Image
      if (resultCode == Activity.RESULT_OK)
      switch (requestCode){
          case CAMERA_REQUEST_CODE:
              imageView.setImageURI(Uri.parse(cameraFilePath));
              break;
      }

  }
  • As already mentioned  cameraFilePath variable has the path of file where camera has saved the image. We can use the same file path to display that image.

Conclusion

I hope this tutorial was useful and you are now clear on how to pick image in Android. If yes do check some more android examples here. We regularly post new android examples every week.

OkHttp Interceptor for Retrofit2 | With Example

This tutorial only focuses on OkHttp Interceptor. If you want to learn how to send a network request with Retrofit from basics read this

In our last post we have learnt how to send a network request with Retrofit on Android. While Retrofit makes sending HTTP requests quite simple it also allows a unique mechanism to monitor and rewrite these requests. This is implemented with the help of OkHttp Interceptor. Intercept – the word means “to obstruct something from reaching its destinations”, similarly Interceptors obstruct a request, rewrite it and then send it to the destinations(server). For example suppose you need all the HTTP requests sent from your app to have authorization token as header. Instead of dynamically adding the same header to all the endpoints as shown here you can simply add a interceptor which will be invoked every time you send a request.

The official explanation for Interceptors is

Interceptors are to observe, modify and potentially short-circuits requests going out and the corresponding responses coming back in. Typically interceptors add, remove, or transform headers on the request  or response.

Interceptors are basically of two types

  • Application Interceptors
    These are high level interceptors which are used to intercept request and response. They are usually used to rewrite headers/query of both request and response. These are definitly invoked once even if the response is fetched from cache.
  • Network Interceptors
    These are low level interceptors used to monitor requested just as it is transmitted over the network. Are very useful to follow redirects and retries and give access to various low level details of the request. These are not invoked if the response is cached.

The diagram below explains the difference between Network and Application Interceptors

interceptors.png

Creating Interceptors

Creating or defining a Interceptor is very simple. You just need to implement the Interceptor interface and override the intercept() method as shown below.  The same interface implementation works for both  NetworkInterceptor and ApplicationInterceptor

private static class CustomInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        /*
        chain.request() returns original request that you can work with(modify, rewrite)
        */
        Request request = chain.request();

        // Here you can rewrite the request

        /*
        chain.proceed(request) is the call which will initiate the HTTP work. This call invokes the
        request and returns the response as per the request.
        */
        Response response = chain.proceed(request);

        //Here you can rewrite/modify the response

        return response;
    }
}

 

A call to chain.proceed(request) is a critical part of each interceptor’s implementation. This simple-looking method is where all the HTTP work happens, this is where the request is initiated and a response is fetched to satisfy the request.

Once you have defined your interface you can register it with the OkHttp client as shown below. Now you should register this client with Retrofit.Builder thereby for all your requests OkHttp client will be used and your interceptor will be invoked

OkHttpClient okHttpClient = new OkHttpClient.Builder()
    .addInterceptor(new CustomInterceptor()) // This is used to add ApplicationInterceptor.
    .addNetworkInterceptor(new CustomInterceptor()) //This is used to add NetworkInterceptor.
    .build();

//Defining the Retrofit using Builder
Retrofit retrofit = new Retrofit.Builder()
    .baseUrl(BASE_URL) //This is the onlt mandatory call on Builder object.
    .client(okHttpClient) //The Htttp client to be used for requests
    .addConverterFactory(GsonConverterFactory.create()) // Convertor library used to convert response into POJO
    .build();

 

 

Logging Request and Response

As developers it is very important that we log the requests/responses which are sent and received  from the app. These logs give us details about the headers, response body, request body and various other details which are crucial for debugging any error. Thanks to Interceptors logging all the HTTP operations on Android is very simple.

Retrofit provides us with a Custom Interceptor – HttpLoggingInterceptor which can be registered with OkHttpClient. With this you will be able to print all the logs for the HTTP operations through this client.

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://backend.example.com")
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

 

Rewriting Requests- Adding/Removing Headers

We have already seen how to define a Custom Interceptor. Now in this section we will define a Custom Interceptor to modify the headers sent with a network request.

private static class RequestInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        /*
        chain.request() returns original request that you can work with(modify, rewrite)
        */
        Request originalRequest = chain.request();

        Headers headers = new Headers.Builder()
            .add("Authorization", "auth-value")
            .add("User-Agent", "you-app-name")
            .build();

        Request newRequest = originalRequest.newBuilder()
            .addHeader("Authorization", "auth-value") //Adds a header with name and value.
            .addHeader("User-Agent", "you-app-name")
            .cacheControl(CacheControl.FORCE_CACHE) // Sets this request's Cache-Control header, replacing any cache control headers already present.
            .headers(headers) //Removes all headers on this builder and adds headers.
            .method(originalRequest.method(), null) // Adds request method and request body
            .removeHeader("Authorization") // Removes all the headers with this name
            .build();

        /*
        chain.proceed(request) is the call which will initiate the HTTP work. This call invokes the
        request and returns the response as per the request.
        */
        Response response = chain.proceed(newRequest);

        return response;
    }

}

 

Rewriting Response with OKHttp Interceptor

Similarly, OkHttp Interceptor can be used to rewrite/ modify response from the server. With this you can not only rewrite response headers but also can make changes to the response body.

In the example below we read the response code and build a new response body based on the response code.

private static class ResponseInterceptor implements Interceptor {

    @Override
    public Response intercept(Chain chain) throws IOException {
        // We obtain the response using chain.proceed() API. This invokes the request and return the response
        Response response = chain.proceed(chain.request());
        try {

            JSONObject jsonObject = new JSONObject();

            if (response.code() == 200) {
                jsonObject.put("code", 200);
                jsonObject.put("status", "OK");
                jsonObject.put("message", new JSONObject(response.body().string()));
            } else {
                jsonObject.put("code", 404);
                jsonObject.put("status", "ERROR");
                jsonObject.put("message", new JSONObject(response.body().string()));
            }
            MediaType contentType = response.body().contentType();
            ResponseBody body = ResponseBody.create(contentType, jsonObject.toString());
            return response.newBuilder().body(body).build();
        } catch (JSONException e) {
            e.printStackTrace();
        }

        return response;
    }
}

Android Image Upload Example | Multipart Retrofit2

This tutorial only discusses Android image upload using multipart with Retrofit2. If you want to learn about sending HTTP requests using Retrofit2 from basics read this.

Why use Multipart to Upload Image ?

Before diving deep into Android Image upload, its important to understand some basics of HTTP requests. Most importantly how are different types of data sent to server. POST requests are used to send data to server, this data can either be simple readable text fields or sometimes even non alphanumeric binary data(like image files).  In our last tutorial6 we saw how to send a simple POST request with retrofit. To facilitate sending different types of data we have different methods given below

With application/x-www-form-urlencoded, the body of the HTTP message sent to the server is essentially one giant query string — name/value pairs are separated by the ampersand (&), and names are separated from values by the equals symbol (=). But for each non-alphanumeric byte that exists in one of our values, it’s going to take three bytes to represent it. This is very inefficient for sending large files hence this method cannot be used for sending image files

That’s where  multipart/form-data  comes in. Multipart sends a single object(file) in various parts, each part is separated by a boundary and has some portion of object’s data. Each part also has its own headers like Content-Type, Content-Deposition. Below is a sample representation of multipart request to upload two file a.txt and a.html

POST / HTTP/1.1
[[ Less interesting headers ... ]]
Content-Type: multipart/form-data; boundary=---------------------------735323031399963166993862150
Content-Length: 834

-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="text1"

text default
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="text2"

aωb
-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file1"; filename="a.txt"
Content-Type: text/plain

Content of a.txt.

-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file2"; filename="a.html"
Content-Type: text/html

<!DOCTYPE html><title>Content of a.html.</title>

-----------------------------735323031399963166993862150
Content-Disposition: form-data; name="file3"; filename="binary"
Content-Type: application/octet-stream

aωb
-----------------------------735323031399963166993862150--

 

This efficiency of sending single file in multiple parts is the reason most of the browsers use Multipart to upload files. Retrofit is one of the few network libraries which has built in support for Multipart. This enables you  to upload any files from your app without worrying of any of the internal details of Multipart.

Android Image Upload

In the following example we will be uploading a Image file to Server using Multipart with Retrofit2. As per the scope of this example we will only cover how to upload a image/file on server but if you want to first learn how to pick image from gallery/camera please read this example

Step 1 : Add Gradle dependencies

implementation 'com.squareup.retrofit2:retrofit:2.3.0'
implementation 'com.squareup.retrofit2:converter-gson:2.3.0'

Step 2 : Create Retrofit Instance

Create a class NetworkClient.java  to retrieve the Retrofit object anywhere in your application. This instance will be used to send the HTTP request from your app.

public class NetworkClient {

    private static final String BASE_URL = "";
    private static Retrofit retrofit;

    public static Retrofit getRetrofitClient(Context context) {

        if (retrofit == null) {
            OkHttpClient okHttpClient = new OkHttpClient.Builder()
                    .build();
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .client(okHttpClient)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build();

        }

        return retrofit;
    }
    
}

Step 3 : Define the Upload API

We know that with Retrofit2, interface methods are used to  define the APIs with which the app will be interacting. Therefore for uploading an image we define the following API. Note the use of @Multipart and @Part

ublic interface UploadAPIs {
    @Multipart
    @POST("/upload")
    Call<ResponseBody> uploadImage(@Part MultipartBody.Part file, @Part("name") RequestBody requestBody);
}

 

@Multipart 
Denotes that the request body is multi-part. Parts should be declared as parameters and annotated with @Part.

@Part
This denotes a single part of Multipart request. The parameter on which this type exists will be processed in three ways

  • If the parameter type is MultipartBody.Part the contents will be used directly. Part name is not required with the annotation (i.e., @Part MultipartBody.Part part).
  • If the parameter  type is RequestBody the value will be used directly with its content type. Supply the part name in the annotation (e.g., @Part("foo") RequestBody foo).
  • Other object types will be converted to an appropriate representation by using a converter. Supply the part name in the annotation (e.g., @Part("foo") Image photo).

 

Step 4 : Uploading an Image

Now comes most important part- Uploading a File. Input parameter to this method is a simple image file path. As already mentioned if you want to learn how to get file path of the images from gallery/camera read this tutorial

  • Using the file path we create a file object and then create a request body with that image file. Now with the request body we create a MultipartBody.Part by passing file name and part name as shown in the snippet below. This MultipartBody.Part is sent to Retrofit to initiate image upload
  • We can even send some text fields along with the image. Make sure server is able to handle all tha parts that you are sending. Since this is a demo example I am just sending a sample description text request body along with the image.
  • Both  the text and image are sent as parts along with the mutipart requests

 

private void uploadToServer(String filePath) {
     Retrofit retrofit = NetworkClient.getRetrofitClient(this);

     UploadAPIs uploadAPIs = retrofit.create(UploadAPIs.class);

     //Create a file object using file path
     File file = new File(filePath);

     // Create a request body with file and image media type
     RequestBody fileReqBody = RequestBody.create(MediaType.parse("image/*"), file);

     // Create MultipartBody.Part using file request-body,file name and part name 
     MultipartBody.Part part = MultipartBody.Part.createFormData("upload", file.getName(), fileReqBody);

     //Create request body with text description and text media type
     RequestBody description = RequestBody.create(MediaType.parse("text/plain"), "image-type");

     // 
     Call call = uploadAPIs.uploadImage(part, description);

     call.enqueue(new Callback() {
         @Override
         public void onResponse(Call call, Response response) {

         }

         @Override
         public void onFailure(Call call, Throwable t) {

         }
     });

 }

Conclusion

This finishes our fully functional Android Image Upload Example, but in software development just getting something working is not enough. One of the major characteristic of a good programmer is that he writes clean code. And the best tips and guidelines for clean code are mentioned in this book by noted software expert Robert C. Martin. It is a must read for every software engineer.


If you are new to Android you should definitely go through the list of Android examples here. It is a must read for every Android Developer
sexy Indian girls