Google’s Fused location api vs Android’s Location API

When you work with location related functionalities you have two choices. But most of us choose Android location API. Some of us don’t even have an idea what Fused location API is all about.  I have written about both APIs. At the end of the tutorial you can decide which API to use for your application requirements.

 

Android’s Location API

Provided by android platform by default. All the classes are located in the android.location  package. One of the main classes of the location framework is android.location.LocationManager.  It is an android’s main component and it contains most of the APIs we need to work with device sensor.

Here is the simple code snippet how the listeners work with LocationManager.

You can write this code in the onCreate method in your main Activity class.

 

@Override
protected void onCreate(Bundle savedInstanceState) {

    LocationManager locationManager = (LocationManager)this.getSystemService(Context.LOCATION_SERVICE);

    //Location Listener is an interface. It will be called every time when the location manager reacted.
    LocationListener locationListener = new LocationListener() {
        public void onLocationChanged(Location location) {
        // This method is called when a new location is found by the network location provider or Gps provider.
        //do you logics here.
        }

        public void onStatusChanged(String provider, int status, Bundle extras) {}

        public void onProviderEnabled(String provider) {}

        public void onProviderDisabled(String provider) {}
    };

    // Register the listener with Location Manager's network provider
    locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);

    //Or  Register the listener with Location Manager's gps provider
    locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, locationListener);

}

 

Don’t forget to add permissions to the Android manifest file.

 

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

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

I will include a working example later.

 

 

Google’s Fused Location Services API

 

Most recommended API by everyone. The android official document recommends to use this way.

Here I will show you how to get the location update using fused api.

 

Android Documentation Recommend The Fused Api

Add Google Play services

Guys, Don’t Fused Location API is also a part of the Google Play services Location APIs. One of the important requirements to work with fused api is, your project should include Google Play services. 

To add Google Play services Open the build.gradle file and add the following dependency

 

apply plugin: 'com.android.application'

android {
    compileSdkVersion 'Google Inc.:Google APIs:23'
    buildToolsVersion "25.0.1"

    defaultConfig {
        applicationId "fusedapitest.appsgit.com.fusedlocationtest"
        minSdkVersion 15
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.google.android.gms:play-services-location:10.0.1'
    testCompile 'junit:junit:4.12'
}

 

Don’t forget to update the version number whenever the Google Play services is updated.

Now Sync Project with the changes to apply in the project.

 

Permission required

Now add the permissions to the manifest file

 

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="fusedapitest.appsgit.com.fusedlocationtest">

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

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>


</manifest>
 

 

Enable Run Time Permission

Runtime  require Android 6.0 Marshmallow (API level 23) or above versions.  For more information about runtime permission visit this link.

Add Google Client API

 

You need to connect to Google Play services API client to work with fused api.

And You need to implement 3 interfaces and pass their instances to googleApiClient

  • ConnectionCallbacks
  • OnConnectionFailedListener
  • LocationListener  (To Get the location update )

 

This is the MainActivity.java with implemented interfaces.

 

 
package fusedapitest.appsgit.com.fusedlocationtest;

import android.Manifest;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.IntentSender;
import android.content.pm.PackageManager;
import android.location.Location;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.PendingResult;
import com.google.android.gms.common.api.ResultCallback;
import com.google.android.gms.common.api.Status;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.location.LocationSettingsRequest;
import com.google.android.gms.location.LocationSettingsResult;
import com.google.android.gms.location.LocationSettingsStates;
import com.google.android.gms.location.LocationSettingsStatusCodes;

public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener     {

    Button connectButton;
    GoogleApiClient googleApiClient;
    Location lastLocation;
    LocationRequest locationRequest;
    private static final int PERMISSION_REQUEST_CODE_LOCATION = 1;

    int REQUEST_CHECK_SETTINGS  = 1000;

    /*!
    * 'onCreate' Called when the activity is first created. followed by onStart().
    * */
    @Override
    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_home);

        /*
        * Setting up the runtime permission. We should implement when we target  Android Marshmallow (API 23) as a target Sdk.
        * */
        requestPermission(PERMISSION_REQUEST_CODE_LOCATION,getApplicationContext(), this);

        connectButton = (Button) findViewById(R.id.button);

        connectButton.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

                Toast.makeText(MainActivity.this, "Button Pressed.!", Toast.LENGTH_SHORT).show();

                try {

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

                    locationRequest = LocationRequest.create();
                    locationRequest.setInterval(1000);

                    locationRequest.setFastestInterval(1000);
                    locationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);

                    checkForLocationReuqestSetting(locationRequest);
                    googleApiClient.connect();

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

            }
        });


    }

    /*
    * onStart : Called when the activity is becoming visible to the user.
    * */
    @Override
    protected void onStart() {
        super.onStart();

    }

    /*
    * onStop : Called when the activity is no longer visible to the user
    * */
    @Override
    protected void onStop() {
        super.onStop();

        //Disconnect the google client api connection.
        if (googleApiClient != null) {
            googleApiClient.disconnect();
        }
    }

    /*
    * onPause : Called when the system is about to start resuming a previous activity.
    * */
    @Override
    protected void onPause() {
        try {
            super.onPause();

            /*
            * Stop retrieving locations when we go out of the application.
            * */
            if (googleApiClient != null) {
                stopLocationUpdates();
            }

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

    }

    protected void stopLocationUpdates() {
        LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this);
    }

    /*
    * Callback method of GoogleApiClient.ConnectionCallbacks
    * */
    @Override
    public void onConnected(@Nullable Bundle bundle) {
        try {

            lastLocation = LocationServices.FusedLocationApi.getLastLocation(googleApiClient);

            LocationServices.FusedLocationApi.requestLocationUpdates(googleApiClient, locationRequest, this);

            ((TextView)(findViewById(R.id.fieldstatus))).setText("Loading...");

            if (lastLocation != null) {
                ((TextView)(findViewById(R.id.lastlocationfield))).setText("Last location is " + lastLocation);
            }

        } catch (SecurityException ex) {
            ex.printStackTrace();
        }

    }

    /*
    * Callback method of GoogleApiClient.ConnectionCallbacks
    * */
    @Override
    public void onConnectionSuspended(int i) {
        System.out.println("onConnectionSuspended called...");
    }

    /*
    * Callback method of GoogleApiClient.OnConnectionFailedListener
    * */
    @Override
    public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {
        System.out.println("onConnectionFailed called.");
    }


    /*
    * Callback method of Locationlistener.
    * */
    @Override
    public void onLocationChanged(final Location location) {
        System.out.println("location changed "+ location);

        ((TextView)(findViewById(R.id.fieldstatus))).setText("Load...!");

        if (location != null) {
            ((TextView)(findViewById(R.id.fieldstatus))).setText("Lat : " + location.getLatitude() + " lon : " + location.getLongitude());

        } else {
            ((TextView)(findViewById(R.id.fieldstatus))).setText("No location found..!");
        }
        Toast.makeText(MainActivity.this, "called...", Toast.LENGTH_SHORT).show();
    }

    public static void requestPermission(int perCode, Context _c, Activity _a){

        String fineLocationPermissionString = Manifest.permission.ACCESS_FINE_LOCATION;
        String coarseLocationPermissionString = Manifest.permission.ACCESS_COARSE_LOCATION;

        if  (   ContextCompat.checkSelfPermission(_a, fineLocationPermissionString) != PackageManager.PERMISSION_GRANTED &&
                ContextCompat.checkSelfPermission(_a, coarseLocationPermissionString) != PackageManager.PERMISSION_GRANTED
                ) {

            //user has already cancelled the permission prompt. we need to advise him.
            if (ActivityCompat.shouldShowRequestPermissionRationale(_a,fineLocationPermissionString)){
                Toast.makeText(_c,"We must need your permission in order to access your reporting location.",Toast.LENGTH_LONG).show();
            }

            ActivityCompat.requestPermissions(_a,new String[]{fineLocationPermissionString, coarseLocationPermissionString},perCode);

        }

    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults) {

        for (String per : permissions) {
            System.out.println("permissions are  " + per);
        }

        switch (requestCode) {

            case PERMISSION_REQUEST_CODE_LOCATION:
                if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {

                    Toast.makeText(this, "Permission loaded...", Toast.LENGTH_SHORT).show();

                } else {

                    Toast.makeText(getApplicationContext(),"Permission Denied, You cannot access location data.",Toast.LENGTH_LONG).show();

                }
                break;
        }
    }

    /**
     * Google Fused API require Runtime permission. Runtime permission is available for Android Marshmallow 
     * or Greater versions. 
     * @param locationRequest needed to check whether we need to prompt settings alert.
     */
    private void checkForLocationReuqestSetting(LocationRequest locationRequest) {
        try {
            LocationSettingsRequest.Builder builder = new LocationSettingsRequest.Builder()
                    .addLocationRequest(locationRequest);

            PendingResult<LocationSettingsResult> result = LocationServices.SettingsApi.checkLocationSettings(googleApiClient, builder.build());

            result.setResultCallback(new ResultCallback<LocationSettingsResult>() {
                @Override
                public void onResult(@NonNull LocationSettingsResult locationSettingsResult) {
                    final Status status = locationSettingsResult.getStatus();
                    final LocationSettingsStates locationSettingsStates = locationSettingsResult.getLocationSettingsStates();

                    switch (status.getStatusCode()) {
                        case LocationSettingsStatusCodes.SUCCESS:
                            // All location settings are satisfied. The client can
                            // initialize location requests here.
                            Log.d("MainActivity", "onResult: SUCCESS");
                            break;
                        case LocationSettingsStatusCodes.RESOLUTION_REQUIRED:
                            Log.d("MainActivity", "onResult: RESOLUTION_REQUIRED");
                            // Location settings are not satisfied, but this can be fixed
                            // by showing the user a dialog.
                            try {
                                // Show the dialog by calling startResolutionForResult(),
                                // and check the result in onActivityResult().
                                status.startResolutionForResult(
                                        MainActivity.this,
                                        REQUEST_CHECK_SETTINGS);
                            } catch (IntentSender.SendIntentException e) {
                                // Ignore the error.
                            }
                            break;
                        case LocationSettingsStatusCodes.SETTINGS_CHANGE_UNAVAILABLE:
                            Log.d("MainActivity", "onResult: SETTINGS_CHANGE_UNAVAILABLE");
                            // Location settings are not satisfied. However, we have no way
                            // to fix the settings so we won't show the dialog.

                            break;
                    }

                }
            });

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

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == REQUEST_CHECK_SETTINGS){
            Toast.makeText(this, "Setting has changed...", Toast.LENGTH_SHORT).show();
        } else {
            super.onActivityResult(requestCode, resultCode, data);
        }
    }
}
 

 

 

Get Location Update continuously

We have implemented the interfaces in the MainActivity.

To request Location update we need 3 parameters.

  • GoogleApiClient instance
  • LocationRequest instance
  • LocationListener instance

We already have GoogleApiClient object. MainActivity is a subclass of LocationListener.

 

If you want to get Accurate data  LocationRequest.PRIORITY_HIGH_ACCURACY willbe used as a parameter. Keep in mind the above will request location only from gps sensor in your device. If you want to work with network wifi or cellular data use LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY

 

Stop Location Updates

We should stop if we don’t need location anymore,

@Override
protected void onPause() {
super.onPause();
LocationServices.FusedLocationApi.removeLocationUpdates(googleApiClient, this);
}

 

To test the app you might need the view xml.

<?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:id="@+id/activity_home"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="fusedapitest.appsgit.com.fusedlocationtest.MainActivity">

    <TextView
        android:text="Press Connect button to get Location cordinaes."
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/button"
        android:layout_marginTop="46dp"
        android:id="@+id/fieldstatus"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <Button
        android:text="Connect"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_marginTop="151dp"
        android:id="@+id/button"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true" />

    <TextView
        android:text="your Last location"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="21dp"
        android:id="@+id/lastlocationfield"
        android:layout_below="@+id/fieldstatus"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_alignParentRight="true"
        android:layout_alignParentEnd="true" />

</RelativeLayout>

 

That’s all. You are done.

 

 

Try to use fused location API from your next project You will observe very good perfomance.

For more information about Fused location visit android official document.

 

Thanks you guys, Let me know if you need any help on this.

 

 


 

Check my latest post about Google’s location update via Background service using Retrofit.

 

 

 

About Zumry

Zumry Mohamed

Self Taught iOS & Android Mobile Application Developer.

Article written by zumrywahid

Self Taught iOS & Android Mobile Application Developer.

Be the first to comment

Leave a Reply

Your email address will not be published. Required fields are marked *