How to optimise battery with FUSED LOCATION API - Android - android

Hi I am facing below issue/problem with location API in android
Battery consumption is high as 30% - 40%, which is causing lot of battery drain.
Location icon in status bar is always ON even when app is closed and when app is uninstalled it goes off automatically.
Requirement:
Need user location when app is opened.
I need to have users location even when app is not opened or not in use based on distance - need user location in background.
Approach:
with GPS
API used FUSED LOCATION API with pending intent.
LocationManager - to check state of GPS On/Off.
Code walkthru:
in OnCreate i m getting location manager instance - getting instance of location manager.
checking is GPS enabled or is network state available else show dialog to enable location: CODE: -
// get GPS state.
locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (isGPSLocationEnabled(locationManager)) {
buildGooleLocationApiClient();
} else if (isNetworkLocationEnabled(locationManager)) {
buildGooleLocationApiClient();
} else {
showAlert();
}
Code for goolgeLocationAPiClient: In this method I am checking android version, requesting permission and enabling services
private void buildGooleLocationApiClient() {
if (Build.VERSION.SDK_INT >= 23) {
int isFineLocationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION);
int isCoarseLocationPermission = ContextCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION);
if (isFineLocationPermission == PackageManager.PERMISSION_DENIED || isCoarseLocationPermission == PackageManager.PERMISSION_DENIED) {
requestPermission();
} else {
checkGoogleLocationApiClient();
}
} else {
checkGoogleLocationApiClient();
}
}
Building GoogleAPI Client:
private void checkGoogleLocationApiClient() {
try {
if (mGoogleApiClient != null) {
if (mGoogleApiClient.isConnected()) {
getMyLocationCampaigns();
} else {
mGoogleApiClient.connect();
}
} else {
buildGoogleApiClient();
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void getMyLocationCampaigns() {
if (mCurrentLocation != null) {
getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
} else {
try {
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
} catch (SecurityException ex) {
ex.printStackTrace();
getData("","");
}
}
}
private synchronized void buildGoogleApiClient() {
try {
Log.i(TAG, "activity Building GoogleApiClient===");
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
createLocationRequest();
} catch (Exception e) {
e.printStackTrace();
getData("","");
}
}
private void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(60 * 60 * 1000);
mLocationRequest.setFastestInterval(60 * 1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
mLocationRequest.setSmallestDisplacement(100);
connectGoogleApiClient();
}
private void connectGoogleApiClient() {
if (mGoogleApiClient != null) {
if (!mGoogleApiClient.isConnected())
mGoogleApiClient.connect();
}
}
#Override
public void onLocationChanged(Location location) {
mCurrentLocation = location;
}
#Override
public void onConnected(#Nullable Bundle bundle) {
if (mCurrentLocation == null) {
try {
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mCurrentLocation != null) {
// MyAPICALL getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
} else {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,mLocationRequest, this);
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mCurrentLocation == null) {
if (locationManager != null) {
String provider = Utils.getUserLastLocation(locationManager);
if (provider != null) {
try {
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
getData(location.getLatitude() + "", location.getLongitude() + "");
} else {
getData("", "");
}
} catch (SecurityException e) {
e.printStackTrace();
}
}
}
} else {
getData(mCurrentLocation.getLatitude()+"",mCurrentLocation.getLongitude()+"");
}
}
} catch (SecurityException ex) {
ex.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
getData("","");
}
}
}
Method to getlocation in background with pending intent
private void startLocationUpdates() {
try {
Intent receiverIntentService = new Intent(this, LocationIntentService.class);
PendingIntent pendingIntent = PendingIntent.getService(this, 1, receiverIntentService, 0);
if (mGoogleApiClient != null) {
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, pendingIntent);
}
}
} catch (SecurityException se) {
se.printStackTrace();
}
}
BroadCastReceiver: In case if device is restarted:
public class LocationBroadcastReceiver extends BroadcastReceiver implements
GoogleApiClient.ConnectionCallbacks,GoogleApiClient.OnConnectionFailedListener, LocationListener {
Context context;
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;
protected Location mCurrentLocation;
public static Boolean mRequestingLocationUpdates = false;
SharedPreferences checkUserStatus;
public LocationBroadcastReceiver() {
}
#Override
public void onReceive(Context context, Intent intent) {
// TODO: This method is called when the BroadcastReceiver is receiving
// an Intent broadcast.
try {
this.context = context;
checkUserStatus = context.getSharedPreferences(Params.LOGIN_DETAILS_PREFERENCE, 0);
String isUserLogedIn = checkUserStatus.getString(Params.TOKEN,"");
// if user is still logged in then only trigger background service
if (!isUserLogedIn.equals("")) {
buildGoogleApiClient();
if (mGoogleApiClient != null) {
if (mGoogleApiClient.isConnected() && mRequestingLocationUpdates) {
startLocationUpdates();
} else {
buildGoogleApiClient();
}
} else {
buildGoogleApiClient();
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
#Override
public void onConnected(Bundle bundle) {
startLocationUpdates();
}
#Override
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(ConnectionResult connectionResult) {
Log.i("Broadcast receiver", "Connection failed: ConnectionResult.getErrorCode() = " + connectionResult.getErrorCode());
}
#Override
public void onLocationChanged(Location location) {
mCurrentLocation = location;
}
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(context)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
createLocationRequest();
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(60 * 60 * 1000);
mLocationRequest.setFastestInterval(60 * 1000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
mLocationRequest.setSmallestDisplacement(100);
}
protected void startLocationUpdates() {
try {
Intent receiverIntentService = new Intent(context,LocationIntentService.class);
PendingIntent pendingIntent = PendingIntent.getService(context,1,receiverIntentService,0);
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, pendingIntent);
}catch (SecurityException se) {
se.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
My intent service class: to get user updated location and make an API call
public class LocationIntentService extends IntentService {
Context context;
Bitmap myBitmap;
URL url;
SharedPreferences.Editor mMyLastLocationHolder;
SharedPreferences mMyLastLocation;
SharedPreferences checkUserStatus;
public LocationIntentService() {
super("LocationIntentService");
}
#Override
protected void onHandleIntent(Intent intent) {
if (intent != null) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
Location location = bundle.getParcelable("com.google.android.location.LOCATION");
if (location != null) {
context = getApplicationContext();
// API call to server
updateAPI(location.getLatitude()+"",location.getLongitude()+"");
Log.v("TAG LOCATION ", " ==== " + location.getLatitude() + " - " + location.getLongitude() + " ==== ");
Log.v("TAG LOCATION ", " ==== calling my-campaigns near me ========");
}
}
}
}
/**
* Handle action Foo in the provided background thread with the provided
* parameters.
*/
private void handleActionFoo(String param1, String param2) {
// TODO: Handle action Foo
throw new UnsupportedOperationException("Not yet implemented");
}
/**
* Handle action Baz in the provided background thread with the provided
* parameters.
*/
private void handleActionBaz(String param1, String param2) {
// TODO: Handle action Baz
throw new UnsupportedOperationException("Not yet implemented");
}
}

I hope this could help you finding the best solution/approach.
Personally prefer to use GoogleApiClient and LocationRequest with a certain priority and interval.
Write a service that implements the following interfaces:
GoogleApiClient.ConnectionCallbacks
GoogleApiClient.OnConnectionFailedListener
LocationListener
public class PositionService extends Service implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {}
Use GoogleApiClient and LocationRequest classes.
Into the onCreate() instantiate a GoogleApiClient object, a LocationRequest object and make mGoogleApiClient connect.
public void onCreate() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
mLocationRequest = LocationRequest.create()
.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY)
.setInterval(mInterval).setFastestInterval(mFastInterval);
mGoogleApiClient.connect();
}
Into the onDestroy() method make the mGoogleApiClient disconnect
#Override
public void onDestroy() {
mGoogleApiClient.disconnect();
}
Now implement the interfaces
#Override
public void onLocationChanged(Location location) {
Log.d("NewLocation", location.toString());
}
#Override
public void onConnected(#Nullable Bundle bundle) throws SecurityException {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
#Override
public void onConnectionSuspended(int i) {
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
Now GoogleApiClient, based on the settings of the LocationRequest, will inform you of the position calling the onLocationChanged() callback
Your business logic should be placed into the onLocationChanged() method. Just pick a good interval timings and priority for the LocationRequest. (see documentation)
Please refer to the official documentation about location strategies, my solution is based on that.
I'm used to get the service started in Foreground to prevent unexpected behaviour by the operating system (e.g. service being killed)

This will only explain you the better logic
Instead of long running service or IntentService Just use Firebase JobDispatcher or Any 3rd Party lib Jobscheduler API such that you move all your location update code to Jobscheduler (https://github.com/googlesamples/android-JobScheduler/blob/master/Application/src/main/java/com/example/android/jobscheduler/service/MyJobService.java)
Start the Job as per your location update interval, configure or alter the Job as per your requirement !! it's really a better solution compare to long running service !!!(You can use eventBus or RxBus for location update in Activity or fragment !!)
Tips: every time Job starts with firing location update before Job closes setup up some system delay of 3seconds or more because some times Googleapiclient takes some more time to update the new updated GPS time after the delay you can close Googleapiclient all unwanted call backs with the running JobService. control the Job configuration smartly with Google Awareness Api or Google Fit API by detecting the User Activity!
All in one Job Jobscheduler Lib: https://github.com/evernote/android-job
P.S: code will be updated very soon

The documentation states
Activities should strongly consider removing all location request when
entering the background (for example at onPause()), or at least swap
the request to a larger interval and lower quality.
therefore what I did when I faced a similar issue was:
I created two location requests, the first had a priority of PRIORITY_HIGH_ACCURACY and an interval of 1 min while the second one had a priority of PRIORITY_LOW_POWER with an internal of 1 hour and a smallest displacement of 1km
When the app is launched I use the first location request (high priority) to get more frequent and accurate location updates
When the app enters the background I swap to the second location request (low priority) to eliminate the battery usage while getting less frequent location updates
(Optional) You can also get the battery percentage when the app is launched and choose according to a limit (eg. 15%) which location request you might want to use when the app is in the foreground
These steps helped me reduce the battery usage of my app from >30% to <3%.

Related

Get continuous location using service even after app killed from recent drawer

I started Service from main activity like;-
Intent intent = new Intent(this, MyLocationService.class);
startService(intent);
MyLocationService class looks like:-
public class MyLocationService extends Service implements
LocationListener,
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener {
private static final String TAG = MyLocationService.class.getSimpleName();
public static Location mCurrentLocation;
LocationRequest mLocationRequest;
GoogleApiClient mGoogleApiClient;
#Override
public void onCreate() {
Log.e(TAG, "onCreate: ");
initiateGooglePlayService();
}
public void initiateGooglePlayService() {
Log.e(TAG, "initiateGooglePlayService: ");
if (isGooglePlayServicesAvailable()) {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(5000);
mLocationRequest.setFastestInterval(5000);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
mLocationRequest.setSmallestDisplacement(10.0f);
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addApi(LocationServices.API)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.build();
mGoogleApiClient.connect();
}
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.e(TAG, "onStartCommand: ");
return super.onStartCommand(intent, flags, startId);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
Log.e(TAG, "onBind: ");
return null;
}
private boolean isGooglePlayServicesAvailable() {
Log.e(TAG, "isGooglePlayServicesAvailable: ");
int status = GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(getApplicationContext());
return ConnectionResult.SUCCESS == status;
}
#Override
public void onConnected(Bundle bundle) {
Log.e(TAG, "onConnected: ");
startLocationUpdates();
}
#Override
public void onConnectionSuspended(int i) {
Log.e(TAG, "onConnectionSuspended: ");
}
protected void startLocationUpdates() {
Log.e(TAG, "startLocationUpdates: ");
try {
PendingResult<Status> pendingResult;
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
pendingResult = LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
} catch (IllegalStateException ignored) {
}
}
#Override
public void onLocationChanged(Location location) {
Log.e(TAG, "onLocationChanged: " + location.getLongitude());
if (mGoogleApiClient.isConnected()) {
mCurrentLocation = location;
Intent intent = new Intent("GPSLocationUpdates");
intent.putExtra("location", location);
LocalBroadcastManager.getInstance(this).sendBroadcast(intent);
Toast.makeText(this, "location", Toast.LENGTH_LONG).show();
}
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
Log.e(TAG, "onConnectionFailed: ");
}
}
I never stop the service anywhere.
But still I am unable to get the control of onLocationChange().
My motive is that, I need continuous location to do some background operation. It worked in lollipop sometimes. But it is not working in Marshmallow and nougat and even kitkat also. I searched but could not get the proper idea. Please let me know, where i am going wrong. Any suggestion accepted.
I am using following dependency for location;-
compile 'com.google.android.gms:play-services-location:9.6.1'
You can use the HyperTrack SDK to get the location updates in the background. Get more insight about the reliability of SDK here in different conditions.
First Setup the SDK
After setting up the SDK you just need to set the Callback to receive the location updates.
HyperTrack.setCallback(new HyperTrackEventCallback() {
#Override
public void onEvent ( #NonNull final HyperTrackEvent event){
switch (event.getEventType()) {
case HyperTrackEvent.EventType.LOCATION_CHANGED_EVENT:
Log.d(TAG, "onEvent: Location Changed");
HyperTrackLocation hyperTrackLocation = event.getLocation();
LatLng latLng = hyperTrackLocation.getLatLng();
updateCurrentLocationMarker(event.getLocation());
break;
}
}
}
(Disclaimer: I work at HyperTrack.)
/**Check out my Github post i did the same for Taxi clone**/
Link ---> https://github.com/yash786agg/GPS/
Note: Remember to remove or comment the below mentioned code if you want the latitude
and latitude even when the application is in background.
if(networkUtilObj != null)
{
networkUtilObj.disconnectGoogleApiClient();
}

GPS icon disappear when open location popup or the app is killed

I am developing an app that should send gps data to a server once every 10 minutes. The problem is that when I kill the app when the GPS is ON, the GPS icon disappear from the status bar (while the GPS is still ON) and the data are not sent anymore. If I do the same with the google maps app the icon doesn't disappear.
Can someone explain me why this is happening?
here is the code that I use
AlarmManager
/**
* ALARM_MANAGER_GPS_TIME_INTERVAL = 10 min
*/
PendingIntent mPendingIntent = PendingIntent
.getService(
context,
PENDING_INTENT_REQUEST_CODE_FOR_GPS,
new Intent(context, GpsDataService.class),
PendingIntent.FLAG_IMMUTABLE);
AlarmManager mAlarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
mAlarmManager.setInexactRepeating(
AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
ALARM_MANAGER_GPS_TIME_INTERVAL,
mPendingIntent);
Service
public class GpsDataService extends Service implements
GoogleApiClient.ConnectionCallbacks,
GoogleApiClient.OnConnectionFailedListener,
LocationListener {
/**
* 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;
private String mPayLoad;
private GpsPojoBuilder mGpsPojoBuilder;
#Override
public void onCreate() {
super.onCreate();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(mGoogleApiClient == null)
buildGoogleApiClient();
mGoogleApiClient.connect();
if (mGoogleApiClient.isConnected())
startLocationUpdates();
return Service.START_NOT_STICKY;
}
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
createLocationRequest();
}
/**
* UPDATE_INTERVAL_IN_MILLISECONDS = 4 sec
* FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS = 2 sec
*/
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest
.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS)
.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS)
.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
#Override
public void onConnected(#Nullable Bundle bundle) {
startLocationUpdates();
}
#Override
public void onConnectionSuspended(int i) {
// The connection to Google Play services was lost for some reason. Call connect() to
// attempt to re-establish the connection.
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
}
/**
* Requests location updates from the FusedLocationApi.
*/
protected void startLocationUpdates() {
checkForPermission();
}
private void checkForPermission() {
Location location;
if (Build.VERSION.SDK_INT >= 23) {
if (ContextCompat.checkSelfPermission(getApplicationContext(),
Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//permission denied
} else {
location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (location != null)
handleLocation(location);
else
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
}else {
location = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if(location != null)
handleLocation(location);
else
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, this);
}
}
#Override
public void onLocationChanged(Location location) {
handleLocation(location);
}
private void handleLocation(Location location){
mCurrentLocation = location;
Toast.makeText(this, "location foound: " + location.toString(), Toast.LENGTH_SHORT).show();
Thread thread = new Thread() {
#Override
public void run() {
try {
mGpsPojoBuilder = new GpsPojoBuilder(getApplicationContext());
mPayLoad = mGpsPojoBuilder
.getCheckInJson(mCurrentLocation.getLatitude(), mCurrentLocation.getLongitude());
sendJSON(mPayLoad, CHECKINS_URL);
} catch (GooglePlayServicesRepairableException e) {
e.printStackTrace();
} catch (GooglePlayServicesNotAvailableException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(mGoogleApiClient.isConnected()) {
stopLocationUpdates();
mGoogleApiClient.disconnect();
}
}
}
};
thread.start();
}
public void sendJSON(String payLoadData, String url_suffix) {
HttpConnectionCreator mHttpConncectionClass = new HttpConnectionCreator(payLoadData, url_suffix);
mHttpConncectionClass.connect();
}
/**
* Removes location updates from the FusedLocationApi.
*/
protected void stopLocationUpdates() {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onDestroy() {
if (mGoogleApiClient.isConnected()) {
stopLocationUpdates();
mGoogleApiClient.disconnect();
}
super.onDestroy();
}
}
According to android.developer, if you want to keep your services alive, you need to use START_STICKY.
int START_STICKY
Constant to return from onStartCommand(Intent, int, int): if this service's process is killed while it is started (after returning from onStartCommand(Intent, int, int)), then leave it in the started state but don't retain this delivered intent. Later the system will try to re-create the service.
inside the following function:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(mGoogleApiClient == null)
buildGoogleApiClient();
mGoogleApiClient.connect();
if (mGoogleApiClient.isConnected())
startLocationUpdates();
return Service.START_NOT_STICKY;
}
change to this:
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(mGoogleApiClient == null)
buildGoogleApiClient();
mGoogleApiClient.connect();
if (mGoogleApiClient.isConnected())
startLocationUpdates();
return Service.START_STICKY;
}

Get LastKnownLocation in Android returns null

I am trying to fetch lastknown location but it is giving me null value. I tested whether GPS is enabled or not, it's returning true.
Even I went into Google Maps and pointed my location (thinking that location data might be cleared) but it still didn't work.
Here is my Code
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
boolean isgps=locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
Log.v("is GPs Enabled",isgps+"");
Location location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
updateNewLocation(location);
}
private void updateNewLocation(Location location) {
TextView myLocationText=(TextView)findViewById(R.id.myLocation);
if(location!=null){
double lat=location.getLatitude();
double lng=location.getLongitude();
myLocationText.setText("lat:"+lat+"\nlong:"+lng);
Log.v("Location","lat:"+lat+"\nlong:"+lng);
}
}
hi why trying to use getLastKnownLocation.It return your previous latitude and longitude.Most probably it retrun you null.It takes time to update and it return previous lat lng.It could be very old or new old.Its not accurate and correct. So,Its better you go for new approach.
I'm just referring you google Fused api for location any kind of updation or current.Its very accurate.
How you can fused api in your project.See i'll give you small example.
Step 1. Make this class GoogleLocationService.java
public class GoogleLocationService {
private GoogleServicesCallbacks callbacks = new GoogleServicesCallbacks();
LocationUpdateListener locationUpdateListener;
Context activity;
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;
public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 30000;
public GoogleLocationService(Context activity, LocationUpdateListener locationUpdateListener) {
this.locationUpdateListener = locationUpdateListener;
this.activity = activity;
buildGoogleApiClient();
}
protected synchronized void buildGoogleApiClient() {
//Log.i(TAG, "Building GoogleApiClient");
mGoogleApiClient = new GoogleApiClient.Builder(activity)
.addConnectionCallbacks(callbacks)
.addOnConnectionFailedListener(callbacks)
.addApi(LocationServices.API)
.build();
createLocationRequest();
mGoogleApiClient.connect();
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
private class GoogleServicesCallbacks implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
#Override
public void onConnected(Bundle bundle) {
startLocationUpdates();
}
#Override
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
if (connectionResult.getErrorCode() == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED) {
Toast.makeText(activity, "Google play service not updated", Toast.LENGTH_LONG).show();
}
locationUpdateListener.cannotReceiveLocationUpdates();
}
#Override
public void onLocationChanged(Location location) {
if (location.hasAccuracy()) {
if (location.getAccuracy() < 30) {
locationUpdateListener.updateLocation(location);
}
}
}
}
private static boolean locationEnabled(Context context) {
boolean gps_enabled = false;
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
try {
gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch (Exception ex) {
ex.printStackTrace();
}
return gps_enabled;
}
private boolean servicesConnected(Context context) {
return isPackageInstalled(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, context);
}
private boolean isPackageInstalled(String packagename, Context context) {
PackageManager pm = context.getPackageManager();
try {
pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
return true;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return false;
}
}
public void startUpdates() {
/*
* Connect the client. Don't re-start any requests here; instead, wait
* for onResume()
*/
if (servicesConnected(activity)) {
if (locationEnabled(activity)) {
locationUpdateListener.canReceiveLocationUpdates();
startLocationUpdates();
} else {
locationUpdateListener.cannotReceiveLocationUpdates();
Toast.makeText(activity, "Unable to get your location.Please turn on your device Gps", Toast.LENGTH_LONG).show();
}
} else {
locationUpdateListener.cannotReceiveLocationUpdates();
Toast.makeText(activity, "Google play service not available", Toast.LENGTH_LONG).show();
}
}
//stop location updates
public void stopUpdates() {
stopLocationUpdates();
}
//start location updates
private void startLocationUpdates() {
if (checkSelfPermission(activity, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(activity, ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, callbacks);
}
}
public void stopLocationUpdates() {
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, callbacks);
}
}
public void startGoogleApi() {
mGoogleApiClient.connect();
}
public void closeGoogleApi() {
mGoogleApiClient.disconnect();
}
}
Step2. Make this interface
LocationUpdateListener.java
public interface LocationUpdateListener {
/**
* Called immediately the service starts if the service can obtain location
*/
void canReceiveLocationUpdates();
/**
* Called immediately the service tries to start if it cannot obtain location - eg the user has disabled wireless and
*/
void cannotReceiveLocationUpdates();
/**
* Called whenever the location has changed (at least non-trivially)
* #param location
*/
void updateLocation(Location location);
/**
* Called when GoogleLocationServices detects that the device has moved to a new location.
* #param localityName The name of the locality (somewhere below street but above area).
*/
void updateLocationName(String localityName, Location location);
}
Step 3. Use this piece of code where you want to get location
private GoogleLocationService googleLocationService;
googleLocationService = new GoogleLocationService(context, new LocationUpdateListener() {
#Override
public void canReceiveLocationUpdates() {
}
#Override
public void cannotReceiveLocationUpdates() {
}
//update location to our servers for tracking purpose
#Override
public void updateLocation(Location location) {
if (location != null ) {
Timber.e("updated location %1$s %2$s", location.getLatitude(), location.getLongitude());
}
}
#Override
public void updateLocationName(String localityName, Location location) {
googleLocationService.stopLocationUpdates();
}
});
googleLocationService.startUpdates();
and call this onDestroy
if (googleLocationService != null) {
googleLocationService.stopLocationUpdates();
}
Thanks hope this help you.

Send GPS coordinates periodically

I am a windows application programmer and recently started learning android. I am planning to make an app that will send GPS coordinates periodically say after every 30 seconds after clicking "start" button and stop doing so by clicking "stop" button (I have already developed a web service accepting the data) . In windows I would use a timer and on every "tick" will find the GPS coordinates and send it. Please help in understanding how a similar thing can be done in android.
Please check how i'm using in my app and its working perfect.I'm using fused api for update location please follow few steps.
Step 1. Make this class
GoogleLocationService.java
public class GoogleLocationService {
private GoogleServicesCallbacks callbacks = new GoogleServicesCallbacks();
LocationUpdateListener locationUpdateListener;
Context activity;
protected GoogleApiClient mGoogleApiClient;
protected LocationRequest mLocationRequest;
public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 30000;
public GoogleLocationService(Context activity, LocationUpdateListener locationUpdateListener) {
this.locationUpdateListener = locationUpdateListener;
this.activity = activity;
buildGoogleApiClient();
}
protected synchronized void buildGoogleApiClient() {
//Log.i(TAG, "Building GoogleApiClient");
mGoogleApiClient = new GoogleApiClient.Builder(activity)
.addConnectionCallbacks(callbacks)
.addOnConnectionFailedListener(callbacks)
.addApi(LocationServices.API)
.build();
createLocationRequest();
mGoogleApiClient.connect();
}
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
private class GoogleServicesCallbacks implements GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
#Override
public void onConnected(Bundle bundle) {
startLocationUpdates();
}
#Override
public void onConnectionSuspended(int i) {
mGoogleApiClient.connect();
}
#Override
public void onConnectionFailed(#NonNull ConnectionResult connectionResult) {
if (connectionResult.getErrorCode() == ConnectionResult.SERVICE_VERSION_UPDATE_REQUIRED) {
Toast.makeText(activity, "Google play service not updated", Toast.LENGTH_LONG).show();
}
locationUpdateListener.cannotReceiveLocationUpdates();
}
#Override
public void onLocationChanged(Location location) {
if (location.hasAccuracy()) {
if (location.getAccuracy() < 30) {
locationUpdateListener.updateLocation(location);
}
}
}
}
private static boolean locationEnabled(Context context) {
boolean gps_enabled = false;
LocationManager lm = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
try {
gps_enabled = lm.isProviderEnabled(LocationManager.GPS_PROVIDER);
} catch (Exception ex) {
ex.printStackTrace();
}
return gps_enabled;
}
private boolean servicesConnected(Context context) {
return isPackageInstalled(GooglePlayServicesUtil.GOOGLE_PLAY_STORE_PACKAGE, context);
}
private boolean isPackageInstalled(String packagename, Context context) {
PackageManager pm = context.getPackageManager();
try {
pm.getPackageInfo(packagename, PackageManager.GET_ACTIVITIES);
return true;
} catch (PackageManager.NameNotFoundException e) {
e.printStackTrace();
return false;
}
}
public void startUpdates() {
/*
* Connect the client. Don't re-start any requests here; instead, wait
* for onResume()
*/
if (servicesConnected(activity)) {
if (locationEnabled(activity)) {
locationUpdateListener.canReceiveLocationUpdates();
startLocationUpdates();
} else {
locationUpdateListener.cannotReceiveLocationUpdates();
Toast.makeText(activity, "Unable to get your location.Please turn on your device Gps", Toast.LENGTH_LONG).show();
}
} else {
locationUpdateListener.cannotReceiveLocationUpdates();
Toast.makeText(activity, "Google play service not available", Toast.LENGTH_LONG).show();
}
}
//stop location updates
public void stopUpdates() {
stopLocationUpdates();
}
//start location updates
private void startLocationUpdates() {
if (checkSelfPermission(activity, ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && checkSelfPermission(activity, ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
return;
}
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, callbacks);
}
}
public void stopLocationUpdates() {
if (mGoogleApiClient.isConnected()) {
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, callbacks);
}
}
public void startGoogleApi() {
mGoogleApiClient.connect();
}
public void closeGoogleApi() {
mGoogleApiClient.disconnect();
}
}
Step2. Make this interface
LocationUpdateListener.java
public interface LocationUpdateListener {
/**
* Called immediately the service starts if the service can obtain location
*/
void canReceiveLocationUpdates();
/**
* Called immediately the service tries to start if it cannot obtain location - eg the user has disabled wireless and
*/
void cannotReceiveLocationUpdates();
/**
* Called whenever the location has changed (at least non-trivially)
* #param location
*/
void updateLocation(Location location);
/**
* Called when GoogleLocationServices detects that the device has moved to a new location.
* #param localityName The name of the locality (somewhere below street but above area).
*/
void updateLocationName(String localityName, Location location);
}
Step 3. Call this on your oncreate
private GoogleLocationService googleLocationService;
googleLocationService = new GoogleLocationService(context, new LocationUpdateListener() {
#Override
public void canReceiveLocationUpdates() {
}
#Override
public void cannotReceiveLocationUpdates() {
}
//update location to our servers for tracking purpose
#Override
public void updateLocation(Location location) {
if (location != null ) {
Timber.e("updated location %1$s %2$s", location.getLatitude(), location.getLongitude());
}
}
#Override
public void updateLocationName(String localityName, Location location) {
googleLocationService.stopLocationUpdates();
}
});
googleLocationService.startUpdates();
and call this onDestroy
if (googleLocationService != null) {
googleLocationService.stopLocationUpdates();
}
Hope this will help you to solve your problem.
Take a look to RxGpsService (An Android service to retrieve GPS locations and route stats using RxJava). It retrieves a RouteStats object which contains the current speed, distance, time elapsed and waypoints. Also you can play/stop the chrono in order to discard locations when chrono is stopped.

Samsung Note 2 can't reach onLocationChanged()

It works on most devices except Galaxy Note 2. It connects to Google Client, but can't reach onLocationChanged() that implements LocationListener. Anyone has any idea what it causes and why only on this device?
#Override
public void onLocationChanged(Location location) {
mLastLocation = location;
if (mLastLocation != null) {
lat = mLastLocation.getLatitude();
lng = mLastLocation.getLongitude();
Toast.makeText(getApplicationContext(), String.valueOf(lat) + "/" + String.valueOf(lng), Toast.LENGTH_LONG).show();
serverUrl = "http://(my server)/offers?lat=" + String.valueOf(mLastLocation.getLatitude())
+ "&lng=" + String.valueOf(mLastLocation.getLongitude()) + "&distance=1";
// save
makeTag(serverUrl);
// after getting location data - unregister listener
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, mFusedLocationCallback);
new GetBackgroundUpdate().execute();
} else {
// get data from server and update GridView
new GetBackgroundUpdate().execute();
Toast.makeText(getApplicationContext(), R.string.no_location_detected, Toast.LENGTH_LONG).show();
}
/**
Location methods
*/
protected synchronized void buildGoogleApiClient() {
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
}
/**
* Runs when a GoogleApiClient object successfully connects.
*/
#Override
public void onConnected(Bundle connectionHint) {
// Provides a simple way of getting a device's location and is well suited for
// applications that do not require a fine-grained location and that do not need location
// updates. Gets the best and most recent location currently available, which may be null
// in rare cases when a location is not available.
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(1000);
mLocationRequest.setFastestInterval(500);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mFusedLocationCallback);
}
#Override
public void onConnectionFailed(ConnectionResult result) {
// Refer to the javadoc for ConnectionResult to see what error codes might be returned in
// onConnectionFailed.
Log.i(TAG, "Connection failed: ConnectionResult.getErrorCode() = " + result.getErrorCode());
if (mResolvingError) {
// Already attempting to resolve an error.
return;
} else if (result.hasResolution()) {
try {
mResolvingError = true;
result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR);
} catch (IntentSender.SendIntentException e) {
// There was an error with the resolution intent. Try again.
mGoogleApiClient.connect();
}
} else {
// Show dialog using GooglePlayServicesUtil.getErrorDialog()
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setMessage(String.valueOf(result.getErrorCode()))
.setCancelable(false)
.setNegativeButton("Ok", new DialogInterface.OnClickListener() {
public void onClick(final DialogInterface dialog, final int id) {
dialog.cancel();
}
});
final AlertDialog alert = builder.create();
alert.show();
mResolvingError = true;
}
//new GetBackgroundUpdate().execute();
}
#Override
public void onConnectionSuspended(int cause) {
// 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
protected void onStart() {
super.onStart();
mGoogleApiClient.connect();
}
#Override
protected void onStop() {
super.onStop();
if (mGoogleApiClient.isConnected()) {
mGoogleApiClient.disconnect();
}
}
Edit: From the line in your comment where the NullPointerException is happening, just ensure that mLastLocation is not null.
if (mLastLocation != null){
address = server + String.valueOf(mLastLocation.getLatitude()) + "&lng=" + String.valueOf(mLastLocation.getLongitude()) + "&distance=" + distance;
}
Another thing to note is that you should always ensure that mGoogleApiClient is not null and connected before using it.
if (mGoogleApiClient != null && mGoogleApiClient.isConnected()){
//..... use mGoogleApiClient.....
}
See documentation here
You should also add a check to see if Google Play Services are available, as sometimes the version available on the device is below the version that you compile your app with. There is a dialog that you can show if that is the case.
Below is how to check if Google Play Services are available.
private boolean isGooglePlayServicesAvailable() {
int status = GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if (ConnectionResult.SUCCESS == status) {
return true;
} else {
GooglePlayServicesUtil.getErrorDialog(status, this, 0).show();
return false;
}
}
Note that getLastLocation() has a high tendency to return null, so a good approach would be to register a location listener if you get a null value from the first call to getLastLocation().
See this post: LocationClient getLastLocation() return null
Here is a guide for how to register a LocationListener:
Creating a listener:
LocationCallback mFusedLocationCallback = new LocationCallback();
Class definition:
private class LocationCallback implements LocationListener {
public LocationCallback() {
}
#Override
public void onLocationChanged(Location location) {
mLastLocation = location;
lat = String.valueOf(mLastLocation.getLatitude());
lng = String.valueOf(mLastLocation.getLongitude());
}
};
Then just register the LocationListener :
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(minTime);
mLocationRequest.setFastestInterval(fastestTime);
mLocationRequest.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
mLocationRequest.setSmallestDisplacement(distanceThreshold);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mFusedLocationCallback);
Edit: You should wait for the API to be connected before you register for location callbacks, it should be something like this:
/**
* Runs when a GoogleApiClient object successfully connects.
*/
#Override
public void onConnected(Bundle connectionHint) {
// Provides a simple way of getting a device's location and is well suited for
// applications that do not require a fine-grained location and that do not need location
// updates. Gets the best and most recent location currently available, which may be null
// in rare cases when a location is not available.
mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
if (mLastLocation != null) {
lat = String.valueOf(mLastLocation.getLatitude());
lng = String.valueOf(mLastLocation.getLongitude());
} else {
Toast.makeText(this, R.string.no_location_detected, Toast.LENGTH_LONG).show();
}
mLocationRequest = new LocationRequest();
mLocationRequest.setInterval(1000);
mLocationRequest.setFastestInterval(500);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, mLocationRequest, mFusedLocationCallback);
}
Documentation: for requestLocationUpdates.... and LocationRequest.
One last thing, make sure that you have this in your AndroidManifest.xml inside the application tag:
<meta-data
android:name="com.google.android.gms.version"
android:value="#integer/google_play_services_version" />

Categories

Resources