In my android app, I need to gt the user location when he clicks a button. I do not need to receive continuous updates on his location however.
I searched through a few questions on stackoverflow, but the answers are 2-3 years old, so I was wondering, as on the Android SDK now, what is the best way to do it.
Also, I would like not to get null in the location if possible.
Thanks in advance.
UPDATE September 23, 2020
Change log of version 17.1.0 mentions a new way to get current location:
FusedLocationProviderClient.getCurrentLocation()
A single fresh location will be returned if the device location can be determined within reasonable time (tens of seconds), otherwise null will be returned.
This method may return locations that are a few seconds old, but never returns much older locations. This is suitable for foreground applications that need a single fresh current location.
Documentation: https://developers.google.com/android/reference/com/google/android/gms/location/FusedLocationProviderClient#getCurrentLocation(int,%20com.google.android.gms.tasks.CancellationToken)
Example of usage:
val cancellationTokenSource = CancellationTokenSource()
fusedLocationProviderClient.getCurrentLocation(LocationRequest.PRIORITY_HIGH_ACCURACY, cancellationTokenSource.token)
// onStop or whenever you want to cancel the request
cancellationTokenSource.cancel()
Old Answer
You can use setNumUpdates method and pass the value 1.
example:
mLocationRequest = new LocationRequest();
mLocationRequest.setNumUpdates(1);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
By default locations are continuously updated until the request is explicitly removed, however you can optionally request a set number of updates. For example, if your application only needs a single fresh location, then call this method with a value of 1 before passing the request to the location client.
https://developers.google.com/android/reference/com/google/android/gms/location/LocationRequest.html#setNumUpdates(int)
Android introduce Fused Location in last I/O Summit, Fused location provide you more reliable and accurate location with the best available provider.
import android.location.Location;
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 com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
public class MainActivity extends AppCompatActivity implements GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener, LocationListener {
TextView txtOutputLat, txtOutputLon;
Location mLastLocation;
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
String lat, lon;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GoogleApiClient();
}
#Override
public void onConnected(Bundle bundle) {
mLocationRequest = LocationRequest.create();
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setInterval(100); // Update location every second
//use if you want location update
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
// here you get current location
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
if (mLastLocation != null) {
lat = String.valueOf(mLastLocation.getLatitude());
lon = String.valueOf(mLastLocation.getLongitude());
}
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onLocationChanged(Location location) {
lat = String.valueOf(location.getLatitude());
lon = String.valueOf(location.getLongitude());
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
GoogleApiClient();
}
synchronized void GoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
#Override
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
#Override
protected void onDestroy() {
super.onDestroy();
mGoogleApiClient.disconnect();
}
}
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
protected void onStart() {
mGoogleApiClient.connect();
super.onStart();
}
protected void onStop() {
mGoogleApiClient.disconnect();
super.onStop();
}
public class MainActivity extends ActionBarActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
#Override
public void onConnected(Bundle connectionHint) {
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
if (mLastLocation != null) {
mLatitudeText.setText(String.valueOf(mLastLocation.getLatitude()));
mLongitudeText.setText(String.valueOf(mLastLocation.getLongitude()));
}
}
}
for further reference see here
Related
I am trying to make an app that can track the GPS location of my bike. To start off, I've been trying to read the GPS location of my own phone. My app keeps crashing whenever I try to launch the fragment below. I have put in a Google API key for the App and placed the line " " in my manifest file. What are some good ways to debug this issue? I am new to Android Studio so I would love to hear any sort of feedback.
//FindBikeFragment.java
import android.Manifest;
import android.content.pm.PackageManager;
import android.location.Location;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.location.FusedLocationProviderClient;
import com.google.android.gms.location.LocationServices;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
/**
* A simple {#link Fragment} subclass.
*/
public class FindBikeFragment extends Fragment implements OnMapReadyCallback {
/**
* GoogleMaps object
*/
private GoogleMap mMap;
/**
* Provides the entry point to the Fused Location Provider API.
*/
private FusedLocationProviderClient mFusedLocationClient;
/**
* Represents a geographical location.
*/
protected Location mLastLocation;
public FindBikeFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_find_bike, container, false);
return v;
}
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SupportMapFragment mapFragment = (SupportMapFragment) getChildFragmentManager().findFragmentById(R.id.map1);
mapFragment.getMapAsync(this);
}
#Override
public void onMapReady(GoogleMap googleMap) {
mMap = googleMap;
mFusedLocationClient = LocationServices.getFusedLocationProviderClient(getActivity());
if (ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
mLastLocation = mFusedLocationClient.getLastLocation().getResult();
LatLng pp = new LatLng(mLastLocation.getLatitude(), mLastLocation.getLongitude());
MarkerOptions option = new MarkerOptions();
option.position(pp).title("Some City");
mMap.addMarker(option);
mMap.moveCamera(CameraUpdateFactory.newLatLng(pp));
return;
}
}
}
You need to use GoogleApiClient for FusedLocationApi.Here is a working code
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();}
return;
}
Call buildGoogleApiClient method on onCreate().And implement required interfaces. GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener,
LocationListener
#Override
public void onConnected(#Nullable Bundle bundle) {
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) {
// TODO: Consider calling
// ActivityCompat#requestPermissions
// here to request the missing permissions, and then overriding
// public void onRequestPermissionsResult(int requestCode, String[] permissions,
// int[] grantResults)
// to handle the case where the user grants the permission. See the documentation
// for ActivityCompat#requestPermissions for more details.
return;
}
Location mLastLocation = LocationServices.FusedLocationApi.getLastLocation(
mGoogleApiClient);
if (mLastLocation != null) {
//place marker at current position
// Log.e("x", mLastLocation.getLongitude() + "");
//Log.e("y", mLastLocation.getLatitude() + "");
currentLocation = mLastLocation;
}
LocationRequest mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(5000); //5 seconds
mLocationRequest.setFastestInterval(5000); //3 seconds
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
mLocationRequest.setSmallestDisplacement(50F); //1/10 meter
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
#Override
public void onLocationChanged(Location location) {
currentLocation = location;
}
And define this parameters
private GoogleApiClient mGoogleApiClient;
private Location currentLocation;
Btw if you are using Android 6.x or 7.x version. You need to grant permission at runtime.Check here
#FnR you're talking about the old version of the fusedlocationapi, a new one came out with the version 11 of the playservices, it's supposed to be much simpler, look here : https://android-developers.googleblog.com/2017/06/reduce-friction-with-new-location-apis.html
I got the same issue there seems to be a conflict between the new fusedlocationapi and the map activities.
I'm able to get my current location with this new technique in an empty activity with no map but the exact same code used in a map activity returns a null result.
Google didn't even provide any example of this with a map, which is crazy since getting the location is 99% of the time coupled with a map
I have a route tracking app that uses GoogleMaps and Location Services. And everything worked fine until yesterday that my app crash every time with a NullPointerException and i don't know why, i haven't changed anything in the code.
Here's the code:
package com.example.rocketron.mapa.Activities;
import android.location.Location;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;
import com.example.rocketron.mapa.R;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
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.maps.CameraUpdate;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.BitmapDescriptorFactory;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.Marker;
import com.google.android.gms.maps.model.MarkerOptions;
public class MainActivity extends AppCompatActivity implements ConnectionCallbacks,
OnConnectionFailedListener, LocationListener, OnMapReadyCallback {
protected static final String TAG = "location-updates-sample";
// Keys for storing activity state in the Bundle.
protected final static String REQUESTING_LOCATION_UPDATES_KEY = "requesting-location-updates-key";
protected final static String LOCATION_KEY = "location-key";
//UI Elements
protected Toolbar toolbar;
protected FloatingActionButton fab;
protected GoogleMap googleMap;
protected MapView mapView;
//Provides the entry point to Google Play services.
protected GoogleApiClient mGoogleApiClient;
//Stores parameters for requests to the FusedLocationProviderApi.
protected LocationRequest mLocationRequest;
//Represents a geographical location.
protected Location mCurrentLocation;
//Tracks the status of the location updates request. Value changes when the user presses the
//Start Updates and Stop Updates button.
protected boolean mRequestingLocationUpdates;
protected double latitude;
protected double longitude;
protected LatLng mLatLng;
protected CameraUpdate cameraUpdate;
Marker currentMarker;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
toolbar = (Toolbar) findViewById(R.id.appbar);
setSupportActionBar(toolbar);
fab = (FloatingActionButton) findViewById(R.id.fab);
mapView = (MapView) findViewById(R.id.mi_mapa);
mapView.getMapAsync(this);
mapView.onCreate(savedInstanceState);
mRequestingLocationUpdates = false;
updateValuesFromBundle(savedInstanceState);
buildGoogleApiClient();
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
togglePeriodicLocationUpdates();
}
});
}
#Override
public void onMapReady(GoogleMap map) {
googleMap = map;
googleMap.getUiSettings().setMapToolbarEnabled(false); //Hide Map Toolbar when marker is clicked
}
#Override
protected void onStart() {
mGoogleApiClient.connect();
super.onStart();
}
#Override
protected void onResume() {
super.onResume();
mapView.onResume();
if (mGoogleApiClient.isConnected() && mRequestingLocationUpdates) {
startLocationUpdates();
}
}
#Override
protected void onPause() {
super.onPause();
mapView.onPause();
if (mGoogleApiClient.isConnected()) {
stopLocationUpdates();
}
}
#Override
protected void onStop() {
mGoogleApiClient.disconnect();
super.onStop();
}
#Override
protected void onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
/**
* Updates fields based on data stored in the bundle.
*
* #param savedInstanceState The activity state saved in the Bundle.
*/
private void updateValuesFromBundle(Bundle savedInstanceState) {
Log.i(TAG, "Updating values from bundle");
if (savedInstanceState != null) {
// Update the value of mRequestingLocationUpdates from the Bundle, and make sure that
// the Start Updates and Stop Updates buttons are correctly enabled or disabled.
if (savedInstanceState.keySet().contains(REQUESTING_LOCATION_UPDATES_KEY)) {
mRequestingLocationUpdates = savedInstanceState.getBoolean(
REQUESTING_LOCATION_UPDATES_KEY);
setFabEnabledState();
}
// Update the value of mCurrentLocation from the Bundle and update the UI to show the
// correct latitude and longitude.
if (savedInstanceState.keySet().contains(LOCATION_KEY)) {
// Since LOCATION_KEY was found in the Bundle, we can be sure that mCurrentLocation
// is not null.
mCurrentLocation = savedInstanceState.getParcelable(LOCATION_KEY);
}
updateUI();
}
}
/*------------- Build Google API Client ----------------*/
protected synchronized void buildGoogleApiClient() {
Log.i(TAG, "Building GoogleApiClient");
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
createLocationRequest();
}
/*------------- Set Up a Location Request -----------------*/
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(5000);
mLocationRequest.setFastestInterval(1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
protected void updateUI() {
if (currentMarker != null)
currentMarker.remove();
latitude = mCurrentLocation.getLatitude();
longitude = mCurrentLocation.getLongitude();
mLatLng = new LatLng(latitude, longitude);
cameraUpdate = CameraUpdateFactory.newLatLng(mLatLng);
googleMap.moveCamera(cameraUpdate);
currentMarker = googleMap.addMarker(new MarkerOptions()
.position(mLatLng)
.title(String.format("%f, %f", latitude, longitude))
.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_ORANGE)));
}
private void togglePeriodicLocationUpdates() {
if (!mRequestingLocationUpdates) {
// Changing the fab button
mRequestingLocationUpdates = true;
setFabEnabledState();
getSupportActionBar().hide();
// Starting the location updates
startLocationUpdates();
} else {
// Changing the fab button
mRequestingLocationUpdates = false;
setFabEnabledState();
getSupportActionBar().show();
// Stopping the location updates
stopLocationUpdates();
}
}
private void setFabEnabledState() {
if (mRequestingLocationUpdates) {
fab.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_stop_white_24dp));
} else {
fab.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_play_arrow_white_24dp));
}
}
protected void startLocationUpdates() {
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(
mGoogleApiClient, this);
}
/*-------------Override Methods from ConnectionCallbacks, OnConnectionFailedListener-------------*/
#Override
public void onConnected(Bundle bundle) {
Log.i(TAG, "Connected to GoogleApiClient");
if (mCurrentLocation == null) {
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
updateUI();
}
if (mRequestingLocationUpdates) {
startLocationUpdates();
}
}
#Override
public void onConnectionSuspended(int i) {
// The connection to Google Play services was lost for some reason. We call connect() to
// attempt to re-establish the connection.
Log.i(TAG, "Connection suspended");
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
// Refer to the javadoc for ConnectionResult to see what error codes might be returned in
// onConnectionFailed.
Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + connectionResult.getErrorCode());
}
/*----------------Override Methods from LocationListener ----------------------*/
#Override
public void onLocationChanged(Location location) {
mCurrentLocation = location;
updateUI();
Toast.makeText(this, getResources().getString(R.string.location_updated_message), Toast.LENGTH_SHORT).show();
}
/*------------------Stores activity data in the Bundle.--------------------*/
public void onSaveInstanceState(Bundle savedInstanceState) {
savedInstanceState.putBoolean(REQUESTING_LOCATION_UPDATES_KEY, mRequestingLocationUpdates);
savedInstanceState.putParcelable(LOCATION_KEY, mCurrentLocation);
super.onSaveInstanceState(savedInstanceState);
}
/*----------------Toolbar Methods---------------*/
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
And the error:
02-15 07:02:47.675 13898-13898/com.example.rocketron.mapa
E/AndroidRuntime: FATAL EXCEPTION: main
java.lang.NullPointerException
at com.example.rocketron.mapa.Activities.MainActivity.updateUI(MainActivity.java:187)
at com.example.rocketron.mapa.Activities.MainActivity.onConnected(MainActivity.java:244)
at com.google.android.gms.common.internal.zzk.zzk(Unknown Source)
at com.google.android.gms.internal.zzmg.zzi(Unknown Source)
at com.google.android.gms.internal.zzme.zzpi(Unknown Source)
at com.google.android.gms.internal.zzme.onConnected(Unknown Source)
at com.google.android.gms.internal.zzmi.onConnected(Unknown Source)
at com.google.android.gms.internal.zzlz.onConnected(Unknown Source)
at com.google.android.gms.common.internal.zzj$zzg.zzqv(Unknown Source)
at com.google.android.gms.common.internal.zzj$zza.zzc(Unknown Source)
at com.google.android.gms.common.internal.zzj$zza.zzv(Unknown Source)
at com.google.android.gms.common.internal.zzj$zzc.zzqx(Unknown Source)
at com.google.android.gms.common.internal.zzj$zzb.handleMessage(Unknown Source)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:137)
at android.app.ActivityThread.main(ActivityThread.java:4867)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:511)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1007)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:774)
at dalvik.system.NativeStart.main(Native Method)
It looks like your mCurrentLocation is null. This line is the problem mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);. The getLastLocation does not get the last known location. You may want to look at this thread for a possible solution.
The fused location provider will only maintain background location if at least one client is connected to it. Now just turning on the location service will not gurranty to store the last known location.
Once the first client connects, it will immediately try to get a location. If your activity is the first client to connect and getLastLocation() is invoked right away in onConnected(), that might not be enough time for the first location to arrive..
Then you are setting mLocationRequest.setFastestInterval(30000); which basically means 30 seconds. so at least 30 seconds after and generally according to your setting preferred time is 120 secs, isn't it a very long time if there is no stored last known location at all? plus you are setting battery balanced priority, which will make you waiting longer time.
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
I suggest you to launch Maps app first, so that there is at least some confirmed location and then test your app.
Also, double check if you have your GPS enabled. You may put a checker whether your GPS is on/off. See this thread.
I wrote some code to get a continuously track location it works but the update triggers only sometimes after restarting the app after the screen timeout, but not allways...
In the "Google Play Services", i use only com.google.android.gms:play-services-location:8.3.0 (to reduce the size).
The AndroidManifest.xml is set for an ACCESS_FINE_LOCATION" permission.
The GPS and "Wi-Fi & mobile network location" of my device is on.
Thanks in advance for help.
Here is the main part:
import android.location.Location;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;
import android.widget.Toast;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import comm.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
import com.google.android.gms.location.LocationServices;
public class MainActivity extends AppCompatActivity implements
ConnectionCallbacks, OnConnectionFailedListener, LocationListener {
private GoogleApiClient mGoogleApiClient;
private LocationRequest mLocationRequest;
private final static int CONNECTION_FAILURE_RESOLUTION_REQUEST = 9000;
protected TextView txtlat, txtlong;
// ---------------------------------------------------------------------------------------
// Create
// ---------------------------------------------------------------------------------------
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txtlat = (TextView)findViewById(R.id.txtlat);
txtlong = (TextView)findViewById(R.id.txtlong);
if (mGoogleApiClient == null) {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
createLocationRequest();
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(10000); // 10sec
mLocationRequest.setFastestInterval(5000); // 5sec
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
// ----------------------------------------------------------------------
#Override
protected void onResume() {
super.onResume();
mGoogleApiClient.connect();
}
// ---------------------------------------------------------------------
#Override
protected void onPause() {
super.onPause();
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
mGoogleApiClient.disconnect();
}
}
// ---------------------------------------------------------------------
#Override
public void onConnected(Bundle connectionHint) {
Location location=LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (location == null) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
} else {
displayLocation(location);
}
}
// ---------------------------------------------------------------------
public void onLocationChanged (Location location) {
displayLocation(location);
}
// ---------------------------------------------------------------------
#Override
protected void onStop() {
super.onStop();
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
}
My app is currently using Maps by Google Play Services
speficying:
mMap.setMyLocationEnabled(true);
I realize each time I am displaying the map in my app:
the location is indicated on the map by a blue dot
a location icon is displaying in the top bar
if I go into Settings/Location of the phone, my app is reported as "High battery use"
However, I can see there are apps that use Maps and still show the location blue dot, but the location icon doesn't appear in top bar and their battery usage is low.
My app currently grants both permissions:
android.permission.ACCESS_COARSE_LOCATION
android.permission.ACCESS_FINE_LOCATION
My question is:
how can I show the location blue dot with low battery usage?
is it possible to specify the accuracy/battery usage by code?
UPDATE
Actually I realized that the way to do it is to use the GoogleApiClient's FusedLocationApi
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
I have configured the GoogleApiClient inside my Activity, calling:
GoogleApiClient.connect() on the Activity's start
GoogleApiClient.disconnect() on the Activity's stop
on the onConnected callback I set the criteria for the location updates: fastest interval of 1 minute with low power priority:
private static final LocationRequest REQUEST = LocationRequest.create()
.setFastestInterval(60000) // in milliseconds
.setInterval(180000) // in milliseconds
.setPriority(LocationRequest.PRIORITY_LOW_POWER);
#Override
public void onConnected(Bundle bundle) {
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient,
REQUEST,
this); // LocationListener
}
I have tested that the GoogleApiClient connects correctly at start, but for some reasons whenever I visit the fragment with the embedded MapView, I still get the high battery use for my app on the Settings/Location screen!
It seems the MapView is ignoring these low power criterias!
FINALLY FOUND THE SOLUTION!!!
thanks to Tristan for his answer!
By default, GoogleMap uses its on location provider, which is not the Fused Location Provider. In order to use the Fused Location Provider (which allows you to control the location accuracy and power consumption) you need to explicitely set the map location source with GoogleMap.setLocationSource() (documentation)
I am reporting here a sample activity to do that:
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.api.GoogleApiClient;
import com.google.android.gms.common.api.GoogleApiClient.ConnectionCallbacks;
import com.google.android.gms.common.api.GoogleApiClient.OnConnectionFailedListener;
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.maps.GoogleMap;
import com.google.android.gms.maps.GoogleMap.OnMyLocationButtonClickListener;
import com.google.android.gms.maps.LocationSource;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.SupportMapFragment;
import android.location.Location;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends FragmentActivity
implements
ConnectionCallbacks,
OnConnectionFailedListener,
LocationSource,
LocationListener,
OnMyLocationButtonClickListener,
OnMapReadyCallback {
private GoogleApiClient mGoogleApiClient;
private TextView mMessageView;
private OnLocationChangedListener mMapLocationListener = null;
// location accuracy settings
private static final LocationRequest REQUEST = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mMessageView = (TextView) findViewById(R.id.message_text);
SupportMapFragment mapFragment =
(SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map);
mapFragment.getMapAsync(this);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
}
#Override
protected void onResume() {
super.onResume();
mGoogleApiClient.connect();
}
#Override
public void onPause() {
super.onPause();
mGoogleApiClient.disconnect();
}
#Override
public void onMapReady(GoogleMap map) {
map.setLocationSource(this);
map.setMyLocationEnabled(true);
map.setOnMyLocationButtonClickListener(this);
}
public void showMyLocation(View view) {
if (mGoogleApiClient.isConnected()) {
String msg = "Location = "
+ LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
Toast.makeText(getApplicationContext(), msg, Toast.LENGTH_SHORT).show();
}
}
/**
* Implementation of {#link LocationListener}.
*/
#Override
public void onLocationChanged(Location location) {
mMessageView.setText("Location = " + location);
if (mMapLocationListener != null) {
mMapLocationListener.onLocationChanged(location);
}
}
#Override
public void onConnected(Bundle connectionHint) {
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient,
REQUEST,
this); // LocationListener
}
#Override
public void onConnectionSuspended(int cause) {
// Do nothing
}
#Override
public void onConnectionFailed(ConnectionResult result) {
// Do nothing
}
#Override
public boolean onMyLocationButtonClick() {
Toast.makeText(this, "MyLocation button clicked", Toast.LENGTH_SHORT).show();
// Return false so that we don't consume the event and the default behavior still occurs
// (the camera animates to the user's current position).
return false;
}
#Override
public void activate(OnLocationChangedListener onLocationChangedListener) {
mMapLocationListener = onLocationChangedListener;
}
#Override
public void deactivate() {
mMapLocationListener = null;
}
}
You will want to make your activity (or better a separate object for this purpose) implement the LocationSource interface.
It is pretty simple you need to store the listener passed in the activate() method and call it when the location is updated and forget it when deactivate() is called. See this answer for an example, you will probably want to update it to use the FusedLocationProvider.
Once you have this set up you can pass your activity as the LocationSource for the map like so mMap.setLocationSource(this) (documentation).
This will stop the map from using its default LocationSource which uses the high battery use location services.
It's stated here that
FusedLocationProviderApi provides improved location finding and power usage and is used by the "My Location" blue dot.
So "My Location" dot on map is fed by FusedLocationProviderApi. And as you grant permission android.permission.ACCESS_FINE_LOCATION you allow FusedLocationProviderApi for your app to get data from GPS which may cause high battery use.
So add only android.permission.ACCESS_COARSE_LOCATION permission to manifest and Android should not blame you for battery usage.
You can do so by using Network provider classes
You can use below code
AppLocationService.java // Special for getting current location with low battery usage (same like Battery saver mode in nexus 5 ,5.0)
package coreclass;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
public class AppLocationService extends Service implements LocationListener {
protected LocationManager locationManager;
Location location;
private static final long MIN_DISTANCE_FOR_UPDATE = 10;
private static final long MIN_TIME_FOR_UPDATE = 1000 * 60 * 2;
public AppLocationService(Context context) {
locationManager = (LocationManager) context
.getSystemService(LOCATION_SERVICE);
}
public Location getLocation(String provider) {
if (locationManager.isProviderEnabled(provider)) {
locationManager.requestLocationUpdates(provider,
MIN_TIME_FOR_UPDATE, MIN_DISTANCE_FOR_UPDATE, this);
if (locationManager != null) {
location = locationManager.getLastKnownLocation(provider);
return location;
}
}
return null;
}
#Override
public void onLocationChanged(Location location) {
}
#Override
public void onProviderDisabled(String provider) {
}
#Override
public void onProviderEnabled(String provider) {
}
#Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
#Override
public IBinder onBind(Intent arg0) {
return null;
}
}
Usage of above class
MainActivity.java
AppLocationService appLocationService;
appLocationService = new AppLocationService(getActivity());
Location nwLocation = appLocationService.getLocation(LocationManager.NETWORK_PROVIDER);
if (nwLocation != null) {
Lat = nwLocation.getLatitude();
Longi = nwLocation.getLongitude();
}
In this way you can get the current location with GPS mode in high bettery usage mode, after you can set the blue dot or whatever you want
Hope it helps you and all
I am working on geo-loaction applciation where I need to calculate user speed, to get user speed I used LocationManager for their current location after every 30second, for this I used following code.
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 1000*30, 0, myLocationListener); //from NETWORK_PROVIDER
locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, 1000*30, 0, myLocationListener); //from GPS_PROVIDER
But my problem is I want location based on fix time interval here is 30second. Right now I get location after every 45second also vary. So if any know how I can set 'maxTime' to location manager then please let me know.
Also if any one know the location listener calling business logic based on time / distance also please let me know.
Thanks in advanced.
Use the new introduced Fused Location Provider API
import android.app.IntentService;
import android.content.Intent;
import android.location.Location;
import android.os.Bundle;
import com.google.android.gms.common.ConnectionResult;
import com.google.android.gms.common.GooglePlayServicesClient;
import com.google.android.gms.location.LocationClient;
import com.google.android.gms.location.LocationListener;
import com.google.android.gms.location.LocationRequest;
public class Locations extends IntentService implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {
public Locations() {
super("Locations");
Log.d("Locations", "Location service started... ");
}
private LocationRequest locationRequest;
private LocationClient locationClient;
private Location location;
private static final int INTERVAL = 1800000;
private static final int FASTEST_INTERVAL = 60000;
#Override
protected void onHandleIntent(Intent intent) {
locationRequest = LocationRequest.create();
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setInterval(INTERVAL);
locationRequest.setFastestInterval(FASTEST_INTERVAL);
locationClient = new LocationClient(this, this, this);
locationClient.connect();
}
#Override
public void onLocationChanged(Location l) {
// do something on Location change.
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
}
#Override
public void onConnected(Bundle arg0) {
Log.w("Locations", "Location client connected...");
// get last location
location = locationClient.getLastLocation();
Log.w("Locations", "Latitude : "+location.getLatitude() + "");
Log.w("Locations", "Longitude : "+location.getLongitude() + "");
}
#Override
public void onDestroy() {
if (locationClient.isConnected()) {
locationClient.removeLocationUpdates(this);
}
locationClient.disconnect();
}
#Override
public void onDisconnected() {
}
}
Source https://developer.android.com/training/location/index.html