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.

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

Android DownloadManager Example [Complete Tutorial]

We have a separate tutorial section on this website for HTTP operations. Over there we have already covered all the different network operations like sending a GET/POST request, uploading images, logging request, adding headers etc. We have used two of the most popular Libraries- Volley and Retrofit which provide complete support for all these network operations. But when it comes to downloading large files you cant use Retrofit or Volley, both of these recommend using DownloadManager. And that is what today’s tutorial is all about – DownloadManager Example

The DownloadManager is a system service that handles long-running HTTP downloads. Clients may request that a URI be downloaded to a particular destination file.

Here are some advantages of using DownloadManager

  • It performs all the HTTP interaction on background thread by default. You dont need to create any thread.
  • It gracefully handles connectivity issues if the network changes or device reboots by automatically retrying download after failure due to any reason.
  • It provides features like pause/ resume download thereby improving user experience.
  • Has a in built mechanism  to inform the user of download progress using notification.

Downloading a File

In this section of DownloadManager Example we will prepare our download request and initiate the File Download  

Add Permissions

First step in this DownloadManager Example is to add INTERNET permission to the AndroidManifest.xml

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

Create Activity

We need to create only one Activity in this DownloadManager Example which will have a button to initiate the download of file. Following is the layout file for this activity

<?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="com.example.irshadkumail.downloadexample.MainActivity">

    <Button
        android:layout_width="wrap_content"
        android:id="@+id/download"
        android:text="DOWNLOAD"
        android:layout_centerInParent="true"
        android:layout_height="wrap_content" />


</RelativeLayout>

Now in our Java file of the Activity we initialize the button and set up a click listener as shown below

public class MainActivity extends AppCompatActivity {

    private Button button;

    private long downloadID;

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

        button=findViewById(R.id.download);

        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
            }
        });
    }
}
  • Note that we have also created a global long variable which will be used to store download identifier id. More about this in next section

Prepare the DownloadManager.Request

Before starting the download we need to create a  DownloadManager.Request object with all the details of the download and conditions to start it. DownloadManager.Request has provided us with multiple APIs to set information necessary to begin the download. The only mandatory information to start a download is network URI. Even if you have not specified any destination, Android stores the downloaded file in a shared storage . Below code snippet shows how we prepare our DownloadManager.Request with all the details.

       File file=new File(getExternalFilesDir(null),"Dummy");
       /*
       Create a DownloadManager.Request with all the information necessary to start the download
        */

       DownloadManager.Request request=new DownloadManager.Request(Uri.parse("http://speedtest.ftp.otenet.gr/files/test10Mb.db"))
               .setTitle("Dummy File")// Title of the Download Notification
               .setDescription("Downloading")// Description of the Download Notification
               .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)// Visibility of the download Notification
               .setDestinationUri(Uri.fromFile(file))// Uri of the destination file
               .setRequiresCharging(false)// Set if charging is required to begin the download
               .setAllowedOverMetered(true)// Set if download is allowed on Mobile network
               .setAllowedOverRoaming(true);// Set if download is allowed on roaming network

     
   

Following are some important APIs used to set information of the download

  • setNotificationVisibility(int)
    This API is to control whether a system notification is shown while this download is running or when it is completed.
    It can takes any of the following predefined values: 
    #VISIBILITY_HIDDEN
    #VISIBILITY_VISIBLE 
    #VISIBILITY_VISIBLE_NOTIFY_COMPLETED
    If set to VISIBILITY_HIDDEN, this requires the permission android.permission.DOWNLOAD_WITHOUT_NOTIFICATION.
  •  setDescription(String), setTitle(String)
    These APIs are used to set the description and title of the download notification (if displayed).
  • setDestinationInExternalFilesDir(Context ,String,String) , setDestinationUri(Uri), setDestinationInExternalPublicDir(String,String)
    These APIs are used to set the destination file path of the downloaded file. You can start a download even without specifying the destination, in which case file is temporarily stored in shared storage. Also if you are storing the file in external storage you need to add STORAGE permissions in the Manifest.
  • addRequestHeader(String,String)
    This API is used to add a request header to the HTTP request used to download the file

Apart from these they are a lot of other APIs like setAllowedOverRoaming(), setRequiresCharging(), setAllowedOverMetered()  etc which define the conditions for the download to start. We have not used all the APIs in this DownloadManager Example but you can read more about them here.

Initiate the download

Once the DownloadManager.Request is ready with all the information you can start the download as shown in snippet below

DownloadManager downloadManager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
downloadID = downloadManager.enqueue(request);// enqueue puts the download request in the queue.
  • enqueue(request) returns  a unique long ID which acts as an identifier for the download. Note that calling enqueue() doesnt necessarily start the download immediately. It rather schedules the download request in a queue. Once the DownloadManager is free is starts the download.
  • Important thing to note is how we initialize global variable downloadID . enqueue() returns an ID for the download, unique across the system. Global variable downloadID is used to store this ID. It is used to reference this download in future.

Listen to Download Complete

So far in this DownloadManager Example we saw to initiate downloading a file in Android. This section covers how to be notified when your download is completed.

One thing to note is that DownloadManager  is a separate system service which downloads files requested by client. Here your app is the client and once you call enqueue() from your app it is now the responsibility of DownloadManager to schedule that download and save the file at destination. Therefore it is important that the client is informed when the download is completed. Android DownloadManager sends a ACTION_DOWNLOAD_COMPLETE broadcast intent when any download is completed. You can listen for this broadcast using a BroadcastReceiver and identify if your download is completed using the unique long ID returned by enqueue().

Not sure what Broadcast is and How does BroadcastReceiver work?  Read this simple tutorial

You can be notified when your download is complete by following three steps

  • Create a BroadcastReceiver as shown in snippet below.Inside the receiver we just check if the received broadcast is for our download by matching the received download id with our enqueued download.
    private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
           @Override
           public void onReceive(Context context, Intent intent) {
    
               //Fetching the download id received with the broadcast
               long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);
    
               //Checking if the received broadcast is for our enqueued download by matching download id
               if (downloadID == id) {
                   Toast.makeText(MainActivity.this, "Download Completed", Toast.LENGTH_SHORT).show();
               }
    
           }
       };
  • Once the BroadcastReceiver is created you can register for ACTION_DOWNLOAD_COMPLETE in the onCreate method of your activity. This ACTION_DOWNLOAD_COMPLETE broadcast is fired everytime any file download is completed by DownloadManager.
    @Override
       protected void onCreate(Bundle savedInstanceState) {
           super.onCreate(savedInstanceState);
           setContentView(R.layout.activity_main);
    
           button=findViewById(R.id.download);
           registerReceiver(onDownloadComplete,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
           button.setOnClickListener(new View.OnClickListener() {
               @Override
               public void onClick(View view) {
                   beginDownload();
               }
           });
       }
  • It is also important that you unregister the BroadcastReceiver in onDestroy. This ensures you only listen for this broadcast as long as the activity is active
    @Override
      public void onDestroy() {
          super.onDestroy();
          unregisterReceiver(onDownloadComplete);
      }
    

     

DownloadManager Example: Complete Code

This completes DownloadManager Example. Below is the complete code of your Activity

public class MainActivity extends AppCompatActivity {

    private Button button;

    private long downloadID;

    private BroadcastReceiver onDownloadComplete = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {

            //Fetching the download id received with the broadcast
            long id = intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1);

            //Checking if the received broadcast is for our enqueued download by matching download id
            if (downloadID == id) {
                Toast.makeText(MainActivity.this, "Download Completed", Toast.LENGTH_SHORT).show();
            }

        }
    };

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

        button=findViewById(R.id.download);
        registerReceiver(onDownloadComplete,new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE));
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                beginDownload();
            }
        });
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        unregisterReceiver(onDownloadComplete);
    }

    private void beginDownload(){

        File file=new File(getExternalFilesDir(null),"Dummy");
        /*
        Create a DownloadManager.Request with all the information necessary to start the download
         */

        DownloadManager.Request request=new DownloadManager.Request(Uri.parse("http://speedtest.ftp.otenet.gr/files/test10Mb.db"))
                .setTitle("Dummy File")// Title of the Download Notification
                .setDescription("Downloading")// Description of the Download Notification
                .setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)// Visibility of the download Notification
                .setDestinationUri(Uri.fromFile(file))// Uri of the destination file
                .setRequiresCharging(false)// Set if charging is required to begin the download
                .setAllowedOverMetered(true)// Set if download is allowed on Mobile network
                .setAllowedOverRoaming(true);// Set if download is allowed on roaming network

        DownloadManager downloadManager= (DownloadManager) getSystemService(DOWNLOAD_SERVICE);
        downloadID = downloadManager.enqueue(request);// enqueue puts the download request in the queue.
    }


}

 

We hope you are now clear on how to download files on Android. You can even learn how to upload files on Android by reading this simple tutorial

FusedLocationProviderClient | Get Current Location Android Example

So far we had used FusedLocationProviderApi to fetch user’s location. But as of Google Play Services version 11.6.0, it has been depreciated and now Google recommends us to use FusedLocationProviderClient. In this tutorial we will be explaining why FusedLocationProviderClient is better and how should we implement it.

FusedLocationProviderClient is the main entry point to Google’s LocationServices. With this your app no longer needs to manually manage connections to Google Play Services through GoogleApiClient.

 

If you want to check how current location was fetched with now depreciated FusedLocationProviderAPI read this

Why FusedLocationProviderClient ?

Google’s LocationServices API is the one which is actually used to access device location. To access these services your app needs to connect to Google Play Services. With FusedLocationProviderApi it was our responsibility to initiate and manage the connection. Below code snippet might remind you of how complicated it was to connect to Google Play Services

public class MapsActivity extends FragmentActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {

        private GoogleApiClient googleApiClient;

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

            //Instantiating the GoogleApiClient
            googleApiClient = new GoogleApiClient.Builder(this)
                .addApi(LocationServices.API)
                .addConnectionCallbacks(this)
                .addOnConnectionFailedListener(this)
                .build();

        }

        public void onStart() {
            super.onStart();
            // Initiating the connection
            googleApiClient.connect();
        }

        public void onStop() {
            super.onStop();
            // Disconnecting the connection
            googleApiClient.disconnect();

        }

        //Callback invoked once the GoogleApiClient is connected successfully
        @Override
        public void onConnected(Bundle bundle) {
            //Fetching the last known location using the FusedLocationProviderApi
        }

        @Override
        public void onConnectionSuspended(int i) {

        }

        //Callback invoked if the GoogleApiClient connection fails
        @Override
        public void onConnectionFailed(ConnectionResult connectionResult) {

        }
}

 

As you can see, before we could fetch location we first had to initiate the connection to GooglePlayServices and only after the connection was successful we could fetch location. And if we tried to fetch the location before the connection is complete it would lead to a FATAL IllegalStateException. This approach has a lot of issues like

  • The scenario in which connection to GooglePlayServices fails or is cancelled is not handled.
  •  It would be difficult to share the location between multiple activities without repeating the connection logic again
  • For a app which just wants to fetch location understanding and managing this connection logic every time would be an unnecessary and extra effort

Advantages with FusedLocationProviderClient

  • It takes the complete connection logic under the hood. User no longer needs to initialize GoogleApiClient nor does he need to manage the connection logic.
  • It returns the result as a Task object which is easy to a manage and share.
  • User need not wait until the connection is established to request for a Location. When requesting for the current location the API call automatically waits until the connection is estabilished thereby minimising the chances of an IllegalStateException

It’s recommended to use Google Play services version 11.6.0 or higher, which includes bug fixes for this class.

Example: Fetching Current Location

In this example we have a button in an Activity which when clicked would fetch the current location of the user using FusedLocationProviderClient.

  • App permissions need to be defined in the Manifest file. Thereby the first step would be to add the Location permissions in the AndroidManifest.xml
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
  • Fused Location provider is a part of Google Play Services therefore we need to include the google play services as dependency in build.gradle.
    implementation 'com.google.android.gms:play-services:11.6.0'
  • Add a button to the layout file of the Launcher Activity
    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
        
        <Button
            android:layout_width="wrap_content"
            android:id="@+id/button"
            android:text="FETCH Location"
            android:layout_centerInParent="true"
            android:layout_height="wrap_content" />
    
    </RelativeLayout>
    
  • Now initialize that button and implement the OnClickListener in your MainActivity.java file.
    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
        Button button;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            button = findViewById(R.id.button);
            button.setOnClickListener(this);
    
        }
    
        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                case R.id.button:
                    break;
            }
        }
    }

     

  • As of Android Marshmallow Location permissions need to be explicitly approved by the user before the app begins to collect device location. To learn more about this read this.
  • We had already mentioned that FusedLocationProviderClient takes care of all the connection logic on its own. Therefore we don’t need to initialize GoogleApiClient nor do we need to implement connection callbacks. We only need to initialize the FusedLocationProviderClient as shown below
    FusedLocationProviderClient fusedLocationProviderClient=LocationServices.getFusedLocationProviderClient(this);
  • Once we have FusedLocationProviderClient we can fetch the current location using the getLastLocation() API. It returns Task object which represents a asynchronous operation. Read more about Task API here.
    Task<Location> task = fusedLocationProviderClient.getLastLocation();
  • We will add the success callback listener to the Task object which will be invoked once the connection is established and the location is fetched.
    task.addOnSuccessListener(new OnSuccessListener<Location>() {
        @Override
        public void onSuccess(Location location) {
           if(location!=null) {
                //Write your implemenation here
                Log.d("AndroidClarified",location.getLatitude()+" "+location.getLongitude());      }
           }
    });

    You must have noticed we add a null check for location in OnSuccessListener. This is to avoid crashes in some scenarios in which the location can be null. Below are some of these scenarios

  • GPS is turned off in the device settings.
  • Location was never recorded on the devices.  This could be the case of a new device or a device that has been restored to factory settings.

Complete Activity

  • Below code snippet shows how your activity will look after the complete implementation. Current Location is received  in the onSuccessListener added on the task object as shown below
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    Button button;

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

        button = findViewById(R.id.button);
        button.setOnClickListener(this);

    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.button:
                if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                   
                    return;
                }
                FusedLocationProviderClient fusedLocationProviderClient = LocationServices.getFusedLocationProviderClient(this);
                Task task = fusedLocationProviderClient.getLastLocation();
                task.addOnSuccessListener(new OnSuccessListener() {
                    @Override
                    public void onSuccess(Location location) {

                    }
                });
                break;
        }
    }
}
  • This is how current location is fetched with FusedLocationProviderClient. If you want to learn how to display this location map you can read this.
  • We also have a amazing collection of Android Examples which will help you become a better developer. Make sure you read them