Guide to getting Location in Android(Address) — Part 2

Jimmy Jose
5 min readSep 26, 2018
pic courtesy: https://www.geospatialworld.net/

In the last part of this tutorial, we have seen how to get the current location(latitude and longitude) with google’s fusedlocationapi. Please check it out if you haven’t already because this is a continuation of that tutorial.

So from the last part, we have got a location object and we need to get the address of the location. The process of converting coordinates to address is called reverse geocoding, and we can use this to look up the address from our coordinates.

We will be using getFromLocation() of the Geocoder class to get the location. This method is synchronous, so we should not use the main thread to invoke this method. We can use IntentService to call the method, which will run long running tasks like this in the background without affecting the UI’s responsiveness.

To do this, create a new class called FetchAddressIntentService that extends IntentService

public class FetchAddressIntentService extends IntentService {/*** Creates an IntentService.  Invoked by your subclass's constructor.*/public FetchAddressIntentService() {super("Location Service");}@Overrideprotected void onHandleIntent(@Nullable Intent intent) {}}

This service will handle our address lookup task on a worker thread and will stop itself when it runs out of work.

Now we need to add an entry to apps manifest inside the application section to tell Android that the app uses an intentservice as shown below:

<serviceandroid:name=".FetchAddressIntentService"android:exported="false"/>

We will need to define some unique constant keys to be used by our service and receiver(will be mentioned later). So let’s create a public final class of constants that can be accessed from different classes.

public final class Constants {public static final String LOCATION_DATA_EXTRA = "CURRENT_LOCATION_LOCATION_DATA_EXTRA";public static final String RECEIVER = "CURRENT_LOCATION_RECEIVER";public static final int FAILURE_RESULT = 0;public static final int SUCCESS_RESULT = 1;}

We need to create a Geocoder object inside the onHandleIntent method to convert the Location to address. We also need to pass a Locale object to ensure the resulting address is localized to users geographic location. The getFromLocation() will return an array of Addresses that are known to describe the area immediately surrounding the given latitude and longitude. The location object and resultreceiver(discussed later) will be passed to the service from where the intentservice is invoked, here it will be MainActivity.

private ResultReceiver mReceiver;//..@Overrideprotected void onHandleIntent(@Nullable Intent intent) {Geocoder geocoder = new Geocoder(this,Locale.getDefault());Location location = intent.getParcelableExtra(Constants.LOCATION_DATA_EXTRA);mReceiver = intent.getParcelableExtra(Constants.RECEIVER);List<Address> addresses = null;String errorMessage = "";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 = "service not available";Log.e(TAG, errorMessage, ioException);} catch (IllegalArgumentException illegalArgumentException) {// Catch invalid latitude or longitude values.errorMessage = "invalid lat long used";Log.e(TAG, errorMessage + ". " +"Latitude = " + location.getLatitude() +", Longitude = " +location.getLongitude(), illegalArgumentException);}//…}

Now we need to deliver our address from the intent service to the activity so that we can update the UI. We can use ResultReceiver for receiving the address from the IntentService. The ResultReceiver class allows you to send a numeric result code as well as a message containing the result data. The numeric code is useful for reporting the success or failure of the geocoding request. We need to define a method that will transfer the address to the Activity:

private void deliverResultToReceiver(int resultCode, String message) {Bundle bundle = new Bundle();bundle.putString(Constants.RESULT_DATA_KEY, message);mReceiver.send(resultCode, bundle);}

mReceiver is the object of ResultReceiver send from the MainActivity which is received along with the location object. Now from the onHandleIntent method, we must call this deliverResultToReceiver() so as to send the address to the MainActivity

@Overrideprotected void onHandleIntent(@Nullable Intent intent) {//….// Handle case where no address was found.if (addresses == null || addresses.size()  == 0) {if (errorMessage.isEmpty()) {errorMessage = "no address found"   ;Log.e(TAG, errorMessage);}deliverResultToReceiver(Constants.FAILURE_RESULT, errorMessage);} else {Address address = addresses.get(0);ArrayList<String> addressFragments = new ArrayList<>();// Fetch the address lines using getAddressLine,// join them, and send them to the thread.for(int i = 0; i <= address.getMaxAddressLineIndex(); i++) {addressFragments.add(address.getAddressLine(i));}Log.i(TAG, "address found");deliverResultToReceiver(Constants.SUCCESS_RESULT,TextUtils.join(System.getProperty("line.separator"),addressFragments));}}

This is all that we have to do to the IntentService. Now we need to start this intentservice from the MainActivity. We can use the following method to start the IntentReceiver.

private void startIntentService() {Intent intent = new Intent(this, FetchAddressIntentService.class);intent.putExtra(Constants.RECEIVER, mResultReceiver);intent.putExtra(Constants.LOCATION_DATA_EXTRA, mLastLocation);startService(intent);}

Where mLastLocation is the object of Location and mResultReceiver is an object of the class ResultReceiver class that we are going to create.

For setting up a ResultReceiver we are going to make an inner class inside the MainActivity which will receive the result that we send form the FetchAddressIntentService. Then we will create a method inside the MainActivity to update our UI.

public class MainActivity extends AppCompatActivity {//..private AddressResultReceiver mResultReceiver;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);//..mResultReceiver = new AddressResultReceiver(null);}class AddressResultReceiver extends ResultReceiver {public AddressResultReceiver(Handler handler) {super(handler);}@Overrideprotected void onReceiveResult(int resultCode, Bundle resultData) {if (resultData == null) {return;}// Display the address string// or an error message sent from the intent service.String mAddressOutput = resultData.getString(Constants.RESULT_DATA_KEY);if (mAddressOutput == null) {mAddressOutput = "";}displayAddressOutput(mAddressOutput);}}}

In the overridden method onReceiveResult we will get the result that we send from the intentservice with mReceiver.send(..)

Now we can make displayAddressOutput() in the MainActivity to update our UI. In android we can only update the UI from the main thread so we have to use runOnUiThread as shown below:

private void displayAddressOutput(final String addressText){runOnUiThread(new Runnable() {@Overridepublic void run() {//address is a textviewaddress.setText(addressText);}});}

That’s it. now we can see the address that we receive from the intent receiver in the textview.

You can find the code for the app here. Happy coding :)

--

--