Working with Location(GPS) in Android


Android Location APIs are used in a wide variety of apps for different purposes such as finding user location, notifying when a user has left a general area (Geofencing), and help interpret user activity (walking, running, driving, etc).

However, Android Location APIs are not the only means of acquiring user location. The following will give examples of how to use Android's LocationManager and other common location libraries.

Fused location API

Example Using Activity with LocationRequest

/*
 * This example is useful if you only want to receive updates in this
 * activity only, and have no use for location anywhere else.
 */
public class LocationActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks, 
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
  
  private GoogleApiClient mGoogleApiClient;
  private LocationRequest mLocationRequest;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); setContentView(R.layout.activity_main);
    mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this).addOnConnectionFailedListener(this).addApi(LocationServices.API).build();
    mLocationRequest = new LocationRequest() 
      .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)//GPS quality location points 
      .setInterval(2000) //At least once every 2 seconds
      .setFastestInterval(1000); //At most once a second
  }
  
  @Override
  protected void onStart(){
    super.onStart();
    mGoogleApiClient.connect();
  }
  
  @Override
  protected void onResume(){
    super.onResume();
    //Permission check for Android 6.0+
    if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ==
PackageManager.PERMISSION_GRANTED) { 
      if(mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
      } 
    }
  }
  
  @Override
  protected void onPause(){ 
    super.onPause();
    //Permission check for Android 6.0+
    if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
      if(mGoogleApiClient.isConnected()) {
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
      }
    }
  }
  
  @Override
  protected void onStop(){
    super.onStop();
    mGoogleApiClient.disconnect();
  }
  
  @Override
  public void onConnected(@Nullable Bundle bundle) {
    if(ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
      LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
    } 
  }
  
  @Override
  public void onConnectionSuspended(int i) {
      mGoogleApiClient.connect();
  }
  
  @Override
  public void onConnectionFailed(@NonNull ConnectionResult connectionResult) { }
  
  
  @Override
  public void onLocationChanged(Location location) {
        //Handle your location update code here
  } 
}

Example Using Service with PendingIntent and BroadcastReceiver.

ExampleActivity

Recommended reading: LocalBroadcastManager

/*
 * This example is useful if you have many different classes that should be
 * receiving location updates, but want more granular control over which ones
 * listen to the updates.
 *
 * For example, this activity will stop getting updates when it is not visible, but a database
 * class with a registered local receiver will continue to receive updates, until "stopUpdates()" is
called here.
 *
*/
public class ExampleActivity extends AppCompatActivity { 
  private InternalLocationReceiver mInternalLocationReceiver;
  
  @Override
  protected void onCreate(Bundle savedInstanceState){ 
    super.onCreate(savedInstanceState);
    //Create internal receiver object in this method only.
    mInternalLocationReceiver = new InternalLocationReceiver(this); 
  }
  
  @Override
  protected void onResume(){
    super.onResume();
    //Register to receive updates in activity only when activity is visible
    LocalBroadcastManager.getInstance(this).registerReceiver(mInternalLocationReceiver, new IntentFilter("googleLocation"));
  }
  
  @Override
  protected void onPause(){
    super.onPause();
    //Unregister to stop receiving updates in activity when it is not visible.
    //NOTE: You will still receive updates even if this activity is killed. 
    LocalBroadcastManager.getInstance(this).unregisterReceiver(mInternalLocationReceiver);
  }
    
  //Helper method to get updates
  private void requestUpdates(){
    startService(new Intent(this, LocationService.class).putExtra("request", true));
  }
  
  //Helper method to stop updates
  private void stopUpdates(){
    startService(new Intent(this, LocationService.class).putExtra("remove", true));
  }
  
  /*
   * Internal receiver used to get location updates for this activity.
   *
   * This receiver and any receiver registered with LocalBroadcastManager does
   * not need to be registered in the Manifest.
   *
   */
  
    private static class InternalLocationReceiver extends BroadcastReceiver{
      private ExampleActivity mActivity;
        InternalLocationReceiver(ExampleActivity activity){
            mActivity = activity;
      }
      
      @Override
      public void onReceive(Context context, Intent intent) {
          final ExampleActivity activity = mActivity; if(activity != null) {
                LocationResult result = intent.getParcelableExtra("result");
                //Handle location update here
          } 
      }
    } 
}

LocationService

NOTE: Don't forget to register this service in the Manifest!

public class LocationService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener {
  private GoogleApiClient mGoogleApiClient; 
  private LocationRequest mLocationRequest;
  
  @Override
  public void onCreate(){
    super.onCreate();
    mGoogleApiClient = new GoogleApiClient.Builder(this).addConnectionCallbacks(this)
      .addOnConnectionFailedListener(this).addApi(LocationServices.API).build();
    
    mLocationRequest = new LocationRequest() 
    .setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY)     //GPS quality location points 
    .setInterval(2000) //At least once every 2 seconds
    .setFastestInterval(1000); //At most once a second
  }
  
  @Override
  public int onStartCommand(Intent intent, int flags, int startId){
    super.onStartCommand(intent, flags, startId);
    //Permission check for Android 6.0+
    if (ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) ==
PackageManager.PERMISSION_GRANTED) {
      if (intent.getBooleanExtra("request", false)) {
        if (mGoogleApiClient.isConnected()) {
          LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, getPendingIntent()); 
        } else {
          mGoogleApiClient.connect();
        }
      } else if(intent.getBooleanExtra("remove", false)) {
            stopSelf();
      }
    }
    return START_STICKY; 
  }
  
  @Override
  public void onDestroy(){
    super.onDestroy(); 
    if(mGoogleApiClient.isConnected()){
        LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient,
getPendingIntent());
        mGoogleApiClient.disconnect();
    }
  }
  
  private PendingIntent getPendingIntent(){
      //Example for IntentService
      //return PendingIntent.getService(this, 0, new Intent(this, **YOUR_INTENT_SERVICE_CLASS_HERE**), PendingIntent.FLAG_UPDATE_CURRENT);
        //Example for BroadcastReceiver
    return PendingIntent.getBroadcast(this, 0, new Intent(this, LocationReceiver.class), PendingIntent.FLAG_UPDATE_CURRENT);
  }
  
  @Override
  public void onConnected(@Nullable Bundle bundle) {
        //Permission check for Android 6.0+
    if(ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED) {
            LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
mLocationRequest, getPendingIntent());
    } 
  }
  
  @Override
  public void onConnectionSuspended(int i) {
      mGoogleApiClient.connect();
  }
  
  @Override
  public void onConnectionFailed(@NonNull ConnectionResult connectionResult) {}
  
  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    return null; 
  }
}

LocationReceiver

NOTE: Don't forget to register this receiver in the Manifest!

public class LocationReceiver extends BroadcastReceiver {
  
  @Override
  public void onReceive(Context context, Intent intent) {
    if(LocationResult.hasResult(intent)){
      LocationResult locationResult = LocationResult.extractResult(intent);
      LocalBroadcastManager.getInstance(context).sendBroadcast(new
Intent("googleLocation").putExtra("result", locationResult));
    }
  } 
}

Get Address From Location using Geocoder

After you got the Location object from FusedAPI, you can easily acquire Address information from that object.

private Address getCountryInfo(Location location) {
      Address address = null;
      Geocoder geocoder = new Geocoder(getActivity(), Locale.getDefault()); String errorMessage;
      List<Address> addresses = null;
  
    try {
        addresses = geocoder.getFromLocation(location.getLatitude(), 
                                       location.getLongitude(),
                                        // In this sample, get just a single address.
                                       1);

    } catch (IOException ioException) {
      // Catch network or other I/O problems.
      errorMessage = "IOException>>" + ioException.getMessage();
    } catch (IllegalArgumentException illegalArgumentException) {
      // Catch invalid latitude or longitude values.
      errorMessage = "IllegalArgumentException>>" + illegalArgumentException.getMessage();
    }

    if (addresses != null && !addresses.isEmpty()) {
          address = addresses.get(0);
    }
  return country; 
}

Requesting location updates using LocationManager

As always, you need to make sure you have the required permissions.

public class MainActivity extends AppCompatActivity implements LocationListener{ 
  private LocationManager mLocationManager = null;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2);
    mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
  }
  
  @Override
  protected void onResume() {
    super.onResume();
    
    try {
      mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this);
    } catch(SecurityException e) {
            // The app doesn't have the correct permissions
    } 
  }
  
  @Override
  protected void onPause() {
    try{ 
      mLocationManager.removeUpdates(this);
    } catch (SecurityException e){
      // The app doesn't have the correct permissions
    }
    super.onPause(); 
  }
  
  @Override
  public void onLocationChanged(Location location) { 
    // We received a location update! 
    Log.i("onLocationChanged", location.toString());
  }
  
  @Override
  public void onStatusChanged(String provider, int status, Bundle extras) {}
  
  @Override
  public void onProviderEnabled(String provider) {}
  
  @Override
  public void onProviderDisabled(String provider) {} 
}

Requesting location updates on a separate thread using LocationManager

As always, you need to make sure you have the required permissions.

public class MainActivity extends AppCompatActivity implements LocationListener{
  private LocationManager mLocationManager = null; 
  HandlerThread mLocationHandlerThread = null; 
  Looper mLocationHandlerLooper = null;
  
  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState); setContentView(R.layout.activity_main2);
    mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
    mLocationHandlerThread = new HandlerThread("locationHandlerThread"); 
  }
  
  @Override
  protected void onResume() {
    super.onResume(); 
    mLocationHandlerThread.start();
    mLocationHandlerLooper = mLocationHandlerThread.getLooper();
    
    try {
        mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 0, 0, this,
mLocationHandlerLooper);
    } catch(SecurityException e){
      // The app doesn't have the correct permissions
    } 
  }
  
  @Override
  protected void onPause() { 
    try{
      mLocationManager.removeUpdates(this); 
      
    } catch (SecurityException e){
      // The app doesn't have the correct permissions
    }
    mLocationHandlerLooper = null;
    
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2)
      mLocationHandlerThread.quitSafely();
    else
      mLocationHandlerThread.quit(); mLocationHandlerThread = null;
    super.onPause(); 
  }
  
  @Override
  public void onLocationChanged(Location location) {
    // We received a location update on a separate thread!
    Log.i("onLocationChanged", location.toString());
    // You can verify which thread you're on by something like this:
    // Log.d("Which thread?", Thread.currentThread() == Looper.getMainLooper().getThread() ? "UIThread" : "New thread");
  }
  
  @Override
  public void onStatusChanged(String provider, int status, Bundle extras) {}
  
  @Override
  public void onProviderEnabled(String provider) {}
  
  @Override
  public void onProviderDisabled(String provider) {} 
}

Getting location updates in a BroadcastReceiver

First, create a BroadcastReceiver class to handle the incoming Location updates:

public class LocationReceiver extends BroadcastReceiver implements Constants {
  
  @Override
  public void onReceive(Context context, Intent intent) { 
    if (LocationResult.hasResult(intent)) {
      LocationResult locationResult = LocationResult.extractResult(intent);
      Location location = locationResult.getLastLocation();
      
      if (location != null) {
                // Do something with your location
      } else {
        Log.d(LocationReceiver.class.getSimpleName(), "*** location object is null ***");
      } 
    }
  } 
}

Then when you connect to the GoogleApiClient in the onConnected callback:

@Override
public void onConnected(Bundle connectionHint) {
    Intent backgroundIntent = new Intent(this, LocationReceiver.class);
    mBackgroundPendingIntent = backgroundPendingIntent.getBroadcast(getApplicationContext(),
LOCATION_REUEST_CODE, backgroundIntent, PendingIntent.FLAG_CANCEL_CURRENT);
  
    mFusedLocationProviderApi.requestLocationUpdates(mLocationClient, mLocationRequest,
backgroundPendingIntent);
}

Don't forget to remove the location update intent in the appropriate lifecycle callback:

@Override
public void onDestroy() {
  if (servicesAvailable && mLocationClient != null) { 
    if (mLocationClient.isConnected()) {
        fusedLocationProviderApi.removeLocationUpdates(mLocationClient,
backgroundPendingIntent);
        // Destroy the current location client
        mLocationClient = null; 
    } else {
        mLocationClient.unregisterConnectionCallbacks(this);
        mLocationClient = null; 
    }
  }
  super.onDestroy(); 
}

Register geofence

I have created GeoFenceObserversationService singleton class.

GeoFenceObserversationService.java:

public class GeoFenceObserversationService extends Service implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, 
ResultCallback<Status> {
  
  protected static final String TAG = "GeoFenceObserversationService"; 
  protected GoogleApiClient mGoogleApiClient;
  protected ArrayList<Geofence> mGeofenceList;
  private boolean mGeofencesAdded;
  private SharedPreferences mSharedPreferences;
  private static GeoFenceObserversationService mInstant; 
  
  public static GeoFenceObserversationService getInstant(){
    return mInstant; 
  }
  
  @Override
  public void onCreate() {
      super.onCreate();
      mInstant = this;
      mGeofenceList = new ArrayList<Geofence>();
      mSharedPreferences = getSharedPreferences(AppConstants.SHARED_PREFERENCES_NAME,
MODE_PRIVATE);
      mGeofencesAdded = mSharedPreferences.getBoolean(AppConstants.GEOFENCES_ADDED_KEY, false);
        buildGoogleApiClient();
  }
  
  @Override
  public void onDestroy() {
    mGoogleApiClient.disconnect();
    super.onDestroy(); 
  }
  
  @Nullable
  @Override
  public IBinder onBind(Intent intent) {
    return null; 
  }
  
  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
    return START_STICKY; 
  }
  
  protected void buildGoogleApiClient() {
    mGoogleApiClient = new GoogleApiClient.Builder(this)
      .addConnectionCallbacks(this) 
      .addOnConnectionFailedListener(this) 
      .addApi(LocationServices.API) 
      .build();
    
      mGoogleApiClient.connect();
  }
  
  @Override
  public void onConnected(Bundle connectionHint) { }
  
  @Override
  public void onConnectionFailed(ConnectionResult result) { }
  
  @Override
  public void onConnectionSuspended(int cause) {}
  
  private GeofencingRequest getGeofencingRequest() {
    GeofencingRequest.Builder builder = new GeofencingRequest.Builder();
    builder.setInitialTrigger(GeofencingRequest.INITIAL_TRIGGER_ENTER);
    builder.addGeofences(mGeofenceList);
    
    return builder.build();
  }
  
  public void addGeofences() {
    if (!mGoogleApiClient.isConnected()) {
      Toast.makeText(this, getString(R.string.not_connected), Toast.LENGTH_SHORT).show(); 
      return;
    }
    populateGeofenceList(); 
    
    if(!mGeofenceList.isEmpty()){
      try { 
        LocationServices.GeofencingApi.addGeofences(mGoogleApiClient,
getGeofencingRequest(), getGeofencePendingIntent()).setResultCallback(this); 
      } catch (SecurityException securityException) {
        securityException.printStackTrace();
      }
    }
  }
  
  public void removeGeofences() {
    if (!mGoogleApiClient.isConnected()) {
      Toast.makeText(this, getString(R.string.not_connected), Toast.LENGTH_SHORT).show();
      return; 
    }
    try {
               LocationServices.GeofencingApi.removeGeofences(mGoogleApiClient, getGeofencePendingIntent()).setResu ltCallback(this);
    } catch (SecurityException securityException) { 
      securityException.printStackTrace();
    } 
  }
  
  public void onResult(Status status) {
    if (status.isSuccess()) {
      mGeofencesAdded = !mGeofencesAdded;
      SharedPreferences.Editor editor = mSharedPreferences.edit();
      editor.putBoolean(AppConstants.GEOFENCES_ADDED_KEY, mGeofencesAdded);
      editor.apply();
    } else {
      String errorMessage = AppConstants.getErrorString(this,status.getStatusCode());
      Log.i("Geofence", errorMessage);
    } 
  }

  private PendingIntent getGeofencePendingIntent() {
    Intent intent = new Intent(this, GeofenceTransitionsIntentService.class);
    return PendingIntent.getService(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT);
  }
  
  private void populateGeofenceList() {
    mGeofenceList.clear();
    List<GeoFencingResponce> geoFenceList = getGeofencesList;
    
    if(geoFenceList!=null&&!geoFenceList.isEmpty()){
      for (GeoFencingResponce obj : geoFenceList){
        mGeofenceList.add(obj.getGeofence());
        Log.i(TAG,"Registered Geofences : " + obj.Id+"-"+obj.Name+"-"+obj.Lattitude+"-
"+obj.Longitude);
      }
    } 
  }
}

AppConstant:

public static final String SHARED_PREFERENCES_NAME = PACKAGE_NAME + ".SHARED_PREFERENCES_NAME"; public static final String GEOFENCES_ADDED_KEY = PACKAGE_NAME + ".GEOFENCES_ADDED_KEY";
public static final String DETECTED_GEOFENCES = "detected_geofences";
public static final String DETECTED_BEACONS = "detected_beacons";

public static String getErrorString(Context context, int errorCode) { 
  Resources mResources = context.getResources();
  
  switch (errorCode) {
    case GeofenceStatusCodes.GEOFENCE_NOT_AVAILABLE:
      return mResources.getString(R.string.geofence_not_available);
      
    case GeofenceStatusCodes.GEOFENCE_TOO_MANY_GEOFENCES:
      return mResources.getString(R.string.geofence_too_many_geofences);
      
    case GeofenceStatusCodes.GEOFENCE_TOO_MANY_PENDING_INTENTS:
      return mResources.getString(R.string.geofence_too_many_pending_intents);
      
    default:
    return mResources.getString(R.string.unknown_geofence_error);
  } 
}

Where I started Service? From Application class

startService(new Intent(getApplicationContext(),GeoFenceObserversationService.class));

How I registered Geofences?

GeoFenceObserversationService.getInstant().addGeofences();

Shibaji Debnath

Software Engineer, Developer and Trainer

+91-8981009499