requestLocationUpdates after a exact fixed interval - android

Objective: To save current locations in Service in database after exact 15 min with in service (using less battery).I use these location at various points in my app.
locationrequest = LocationRequest.create();
locationrequest.setInterval(5*60000);
locationrequest
.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
locationclient.requestLocationUpdates(locationrequest, mPendingIntent);
Problem: I'm using the above code does not request location according to set interval value.Although, I'm aware that This interval is inexact. You may not receive updates at all, or you may receive them slower than requested. You may also receive them faster than requested. Sometimes, the location is updated after 1 min , I don't want to waste processing and battery to get locations at small intervals.
public class LoginActivity extends Activity implements OnClickListener
,
GooglePlayServicesClient.ConnectionCallbacks,GooglePlayServicesClient.OnConnectionFailedListener,LocationListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.login_screen);
///my code
mIntentService = new Intent(LoginActivity.this,LocationService.class);
mIntentService.putExtra("time",String.valueOf(System.currentTimeMillis()) );
mPendingIntent = PendingIntent.getService(LoginActivity.this, 1, mIntentService, 0);
int resp =GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
if(resp == ConnectionResult.SUCCESS){
locationclient = new LocationClient(this,this,this);
locationclient.connect();
}
else{
Toast.makeText(this, "Google Play Service Error " + resp, Toast.LENGTH_LONG).show();
}
#Override
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
// TODO Auto-generated method stub
}
#Override
public void onConnected(Bundle arg0) {
// TODO Auto-generated method stub
Log.i("fused", " onConnected " );
// mIntentService = new Intent(LoginActivity.this,LocationService.class);
// mPendingIntent = PendingIntent.getService(LoginActivity.this, 1, mIntentService, 0);
locationrequest = LocationRequest.create();
locationrequest.setInterval(5*60000);
// locationrequest
// .setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
locationclient.requestLocationUpdates(locationrequest, mPendingIntent);
// locationrequest = LocationRequest.create();
// locationrequest.setInterval(1000);//??
// locationclient.requestLocationUpdates(locationrequest, this);
}
#Override
public void onDisconnected() {
// TODO Auto-generated method stub
}
}
LocationService
public class LocationService extends IntentService {
private String TAG = this.getClass().getSimpleName();
public LocationService() {
super("Fused Location");
}
public LocationService(String name) {
super("Fused Location");
}
#Override
protected void onHandleIntent(Intent intent) {
// Log.i("fused", "onHandleIntent LocationService");
Location location = intent.getParcelableExtra(LocationClient.KEY_LOCATION_CHANGED);
if(location !=null){
String time= intent.getStringExtra("time");
Log.i("fused", "onHandleIntent LocationService " +time+"---"+ location.getLatitude() + "," + location.getLongitude());
updateTransientLocation(getApplicationContext(), location);
}
}
Also, I need to save these locations periodically in database in background only and hence cannot use requestLocationUpdates without pending intent to service.
I have refered to this for the code
Thanks.
EDIT -SOLUTION This is how my problem was solved
Code in Activity
Intent myIntent = new Intent(context,LocationReceiver.class);
PendingIntent pi = PendingIntent.getBroadcast(context, 0, myIntent, 0);
alarmMgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
// 120000,pi);
I removed the location Service class and added location receiver
LocationReceiver
public class LocationReceiver extends BroadcastReceiver implements
GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener, LocationListener {
SharedPreferences prefs = null;
LocationClient locationclient = null;
Context contxt;
/** For location poller NO LONGER IN USE **/
#Override
public void onReceive(Context context, Intent intent) {
contxt=context;
//Log.i("locationreciever", "in location rec");
Log.i("fused", "in location rec");
int resp = GooglePlayServicesUtil
.isGooglePlayServicesAvailable(context);
if (resp == ConnectionResult.SUCCESS) {
locationclient = new LocationClient(context, this, this);
locationclient.connect();
} else {
Log.i("fused", "loc client Google Play Service Error");
}
}
#Override
public void onLocationChanged(Location location) {
Log.i("fused", " onLocationChanged Location Request :" + location.getLatitude() + "," + location.getLongitude());
updateTransientLocation(contxt, location);
if (locationclient != null) {
if (locationclient.isConnected()) {
locationclient.removeLocationUpdates(this);
locationclient.disconnect();
}
}
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
Log.i("fused", "loc client connection failed");
}
#Override
public void onConnected(Bundle arg0) {
Log.i("fused", "loc client onConnected");
LocationRequest locationrequest = LocationRequest.create();
locationrequest
.setPriority(LocationRequest.PRIORITY_BALANCED_POWER_ACCURACY);
locationclient.requestLocationUpdates(locationrequest, this);
}
#Override
public void onDisconnected() {
Log.i("fused", "loc client disconnected");
}
}

The best solution would be to use your current approach. You're tell the OS that you don't need locations more often, but something else might be requesting locations, in which case you might as well just accept it, now that the phone has already woken up to get a GPS fix and broadcast it to every process that's interested in a location. This way, your application may actually never have to turn on the GPS, because you're basically just using a location fix that was requested by another process more often that every 15 minutes. The keyword to search for here is the new fused location provider.
If you insist on getting a location exactly every 15 minutes, you can, instead of scheduling a location request, use an AlarmManager to schedule a job to run every 15 minutes. In your alarm manager, you can then immediately request a new single location, and then completely stop requesting new locations until your job is scheduled to run again. If you go down this route, you'll likely run into problems with your service ending before you get a result, because of the asynchronous nature of the location service. Therefore, you want to poll for a location in your alarm manager. You can use a project like CWAC LocationPoller for that
The documentation has examples of how to schedule recurring events:
https://developer.android.com/training/scheduling/alarms.html
Depending on your need, you should be think about the fact that a location may not be available every 15 minutes. Maybe the user is outside of GPS/wifi/phone range. So it may or may not be beneficial to start a task a bit early, or more often, to make sure you have a reasonable fix after your 15 minute window has elapsed.
With all that said, here's the code snippet you're actually interested in to solve your specific problem (taken directly from the CWAC locationpoller site):
1. Create a recurring alarm manager
mgr=(AlarmManager)getSystemService(ALARM_SERVICE);
Intent i=new Intent(this, LocationPoller.class);
Bundle bundle = new Bundle();
LocationPollerParameter parameter = new LocationPollerParameter(bundle);
parameter.setIntentToBroadcastOnCompletion(new Intent(this, LocationReceiver.class));
// try GPS and fall back to NETWORK_PROVIDER
parameter.setProviders(new String[] {LocationManager.GPS_PROVIDER, LocationManager.NETWORK_PROVIDER});
parameter.setTimeout(60000);
i.putExtras(bundle);
pi=PendingIntent.getBroadcast(this, 0, i, 0);
mgr.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime(),
PERIOD,
pi);
2. Create a BroadcastReceiver to receive your location data
Bundle b=intent.getExtras();
LocationPollerResult locationResult = new LocationPollerResult(b);
Location loc=locationResult.getLocation();
String msg;
if (loc==null) {
loc=locationResult.getLastKnownLocation();
if (loc==null) {
msg=locationResult.getError();
}
else {
msg="TIMEOUT, lastKnown="+loc.toString();
}
}
else {
msg=loc.toString();
}
if (msg==null) {
msg="Invalid broadcast received!";
}

From http://developer.android.com/reference/android/app/AlarmManager.html#setRepeating%28int,%20long,%20long,%20android.app.PendingIntent%29
as of API 19, all repeating alarms are inexact. If your application needs precise delivery times then it must use one-time exact alarms, rescheduling each time as described above. Legacy applications whose targetSdkVersion is earlier than API 19 will continue to have all of their alarms, including repeating alarms, treated as exact.
So you will have to do something like this:
public void startTheClock(int interval) {
Intent pingerIntent = new Intent(this, findLoc.class);
pingerIntent.setAction("start_clock");
PendingIntent pendingIntent = PendingIntent.getBroadcast(
this.getApplicationContext(),
0,
pingerIntent,
PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) this.getSystemService(
Context.ALARM_SERVICE);
alarms.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
SystemClock.elapsedRealtime() + interval,
pendingIntent);
}
And in the class that captures that intent (in this example, findLoc.java):
public class findLoc extends BroadcastReceiver{
#Override
public void onReceive(Context context, Intent intent) {
callMethodThatSearchesForLocation();
startTheClock(INTERVAL);
}
}
Where interval is a constant in miliseconds.
NOTE: I actually had some problems with that because it displayed an error on setExact(..) since my minimum SDK did not support this. Which is a bit of a paradox if you want the same behaviour on SDK lower than 19 and higher or equal to 19.

Related

How to restart service after the app is killed from recent tasks

I have created a service to fetch current location of the device in periodic intervals. I want the service to run in the background even if the app is cleared from recently opened apps. Currently the service runs in background only until app is present in the recently opened apps but stop immediately when app is swiped off (or killed in some other way).
I have tried all sort of help available in stack overflow yet I am unable to solve this. Please help. Here is my code for the service.
package com.packr.services;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ServiceInfo;
import android.location.Location;
import android.os.Bundle;
import android.os.IBinder;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.util.Log;
import android.widget.Toast;
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 java.text.DateFormat;
import java.util.Date;
/**
* Created by Arindam on 11-Dec-15.
*/
public class LocationService extends Service implements
GoogleApiClient.ConnectionCallbacks, GoogleApiClient.OnConnectionFailedListener, LocationListener {
protected static final String TAG = "packrMATE";
/**
* The desired interval for location updates. Inexact. Updates may be more or less frequent.
*/
public static final long UPDATE_INTERVAL_IN_MILLISECONDS = 10000;
/**
* The fastest rate for active location updates. Exact. Updates will never be more frequent
* than this value.
*/
public static final long FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS =
UPDATE_INTERVAL_IN_MILLISECONDS / 2;
// 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";
protected final static String LAST_UPDATED_TIME_STRING_KEY = "last-updated-time-string-key";
/**
* 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 buttons.
*/
protected Boolean mRequestingLocationUpdates;
/**
* Time when the location was updated represented as a String.
*/
protected String mLastUpdateTime;
#Override
public void onCreate() {
Log.d(TAG,"Service started");
super.onCreate();
mRequestingLocationUpdates = false;
mLastUpdateTime = "";
// Kick off the process of building a GoogleApiClient and requesting the LocationServices
// API.
buildGoogleApiClient();
}
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG,"Service fucking started");
mGoogleApiClient.connect();
if (mGoogleApiClient.isConnected()) {
startLocationUpdates();
}
return Service.START_STICKY;
}
#Override
public void onDestroy() {
mGoogleApiClient.disconnect();
super.onDestroy();
}
#Nullable
#Override
public IBinder onBind(Intent intent) {
return null;
}
#Override
public void onConnected(Bundle bundle) {
Log.i(TAG, "Connected to GoogleApiClient");
// If the initial location was never previously requested, we use
// FusedLocationApi.getLastLocation() to get it. If it was previously requested, we store
// its value in the Bundle and check for it in onCreate(). We
// do not request it again unless the user specifically requests location updates by pressing
// the Start Updates button.
//
// Because we cache the value of the initial location in the Bundle, it means that if the
// user launches the activity,
// moves to a new location, and then changes the device orientation, the original location
// is displayed as the activity is re-created.
if (mCurrentLocation == null) {
mCurrentLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient);
mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
Toast.makeText(getApplicationContext(),"Hello Babe",Toast.LENGTH_SHORT).show();
}
// If the user presses the Start Updates button before GoogleApiClient connects, we set
// mRequestingLocationUpdates to true (see startUpdatesButtonHandler()). Here, we check
// the value of mRequestingLocationUpdates and if it is true, we start location updates.
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 onLocationChanged(Location location) {
mCurrentLocation = location;
mLastUpdateTime = DateFormat.getTimeInstance().format(new Date());
Toast.makeText(this, String.valueOf(location.getLatitude() + " "+ String.valueOf(location.getLongitude())),
Toast.LENGTH_SHORT).show();
Log.e(TAG,"fuck man location found");
}
#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());
}
protected synchronized void buildGoogleApiClient() {
Log.i(TAG, "Building GoogleApiClient");
mGoogleApiClient = new GoogleApiClient.Builder(this)
.addConnectionCallbacks(this)
.addOnConnectionFailedListener(this)
.addApi(LocationServices.API)
.build();
createLocationRequest();
}
/**
* Sets up the location request. Android has two location request settings:
* {#code ACCESS_COARSE_LOCATION} and {#code ACCESS_FINE_LOCATION}. These settings control
* the accuracy of the current location. This sample uses ACCESS_FINE_LOCATION, as defined in
* the AndroidManifest.xml.
* <p/>
* When the ACCESS_FINE_LOCATION setting is specified, combined with a fast update
* interval (5 seconds), the Fused Location Provider API returns location updates that are
* accurate to within a few feet.
* <p/>
* These settings are appropriate for mapping applications that show real-time location
* updates.
*/
protected void createLocationRequest() {
mLocationRequest = new LocationRequest();
// Sets the desired interval for active location updates. This interval is
// inexact. You may not receive updates at all if no location sources are available, or
// you may receive them slower than requested. You may also receive updates faster than
// requested if other applications are requesting location at a faster interval.
mLocationRequest.setInterval(UPDATE_INTERVAL_IN_MILLISECONDS);
// Sets the fastest rate for active location updates. This interval is exact, and your
// application will never receive updates faster than this value.
mLocationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL_IN_MILLISECONDS);
mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
}
/**
* Requests location updates from the FusedLocationApi.
*/
protected void startLocationUpdates() {
// The final argument to {#code requestLocationUpdates()} is a LocationListener
// (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html).
LocationServices.FusedLocationApi.requestLocationUpdates(
mGoogleApiClient, mLocationRequest, this);
}
/**
* Removes location updates from the FusedLocationApi.
*/
protected void stopLocationUpdates() {
// It is a good practice to remove location requests when the activity is in a paused or
// stopped state. Doing so helps battery performance and is especially
// recommended in applications that request frequent location updates.
// The final argument to {#code requestLocationUpdates()} is a LocationListener
// (http://developer.android.com/reference/com/google/android/gms/location/LocationListener.html).
LocationServices.FusedLocationApi.removeLocationUpdates(mGoogleApiClient, this);
}
#Override
public void onTaskRemoved(Intent rootIntent) {
Log.e("FLAGX : ", ServiceInfo.FLAG_STOP_WITH_TASK + "");
Intent restartServiceIntent = new Intent(getApplicationContext(),
this.getClass());
restartServiceIntent.setPackage(getPackageName());
PendingIntent restartServicePendingIntent = PendingIntent.getService(
getApplicationContext(), 1, restartServiceIntent,
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmService = (AlarmManager) getApplicationContext()
.getSystemService(Context.ALARM_SERVICE);
alarmService.set(AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + 1000,
restartServicePendingIntent);
super.onTaskRemoved(rootIntent);
}
}
Override onTaskRemoved() in your service and use alarm manager to start the service again. Below is code from our app that does the same and works fine:
#Override
public void onTaskRemoved(Intent rootIntent) {
super.onTaskRemoved(rootIntent);
Log.d(TAG, "TASK REMOVED");
PendingIntent service = PendingIntent.getService(
getApplicationContext(),
1001,
new Intent(getApplicationContext(), MyService.class),
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service);
}
As you may want to send location periodically even in the case if the service gets killed on low memory (or for any reason), I suggest you to handle the uncaughtException to restart it after N seconds. This is how we have done in our app that works perfectly:
private Thread.UncaughtExceptionHandler defaultUEH;
private Thread.UncaughtExceptionHandler uncaughtExceptionHandler = new Thread.UncaughtExceptionHandler() {
#Override
public void uncaughtException(Thread thread, Throwable ex) {
Log.d(TAG, "Uncaught exception start!");
ex.printStackTrace();
//Same as done in onTaskRemoved()
PendingIntent service = PendingIntent.getService(
getApplicationContext(),
1001,
new Intent(getApplicationContext(), MyService.class),
PendingIntent.FLAG_ONE_SHOT);
AlarmManager alarmManager = (AlarmManager) getSystemService(Context.ALARM_SERVICE);
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service);
System.exit(2);
}
};
Note: I THINK and I remember I verified it on Kitkat that START_STICKY does not work on Kitkat and higher API levels. Please verify this for yourself.
MORE:
As you do loc sending periodically, you may have to consider the deep sleep mode. To get things work in deep sleep, use WakefulBroadcastReceiver combined with AlarmManager. Take a look at my other post How to use http in deep sleep mode.
UPDATE:
This solution does not work (in fact need not to work) if user "FORCE STOP" the application from Settings. This is good in fact as restarting the service is not a good way if user himself wants to stop application. So, it is fine.
replace return Service.START_NOT_STICKY; with return START_STICKY;
If you ONLY want to restart service after it kill from Recent Task, simple use
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
return START_STICKY;
}
If you use START_STICKY, when you kill app from recent task, your service will be killed (onTaskRemoved fired, onDestroy NOT fired) THEN it will auto start again (onCreate fired, onStartComand fired)
I use android 9 and the solution works partially for me.
I had a case with foreground service (working 24/7), which I wanted to restart after the application was crashed.
When the event uncaughtExceptionHandler was caught, the application got frozen besides public void onTaskRemoved(Intent rootIntent) { event doesn't work anymore in latest Android versions (I suppose O+).
My application has only one activity with fragments if you need solution for more activities just use this link.
To solve that problem I've added a function which checks if the activity is working (to kill it) and some instructions to kill the process:
class MyApplication : Application() {
private var currentActivity: Activity? = null
override fun onCreate() {
super.onCreate()
StorageManager.init()
this.registerActivityLifecycleCallbacks(object : ActivityLifecycleCallbacks {
override fun onActivityPaused(activity: Activity) {
}
override fun onActivityStarted(activity: Activity) {
currentActivity = activity
}
override fun onActivityDestroyed(activity: Activity) {
}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {
}
override fun onActivityStopped(activity: Activity) {
currentActivity = null
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
}
override fun onActivityResumed(activity: Activity) {
}
})
Thread.setDefaultUncaughtExceptionHandler { _, e ->
// Close current activity
currentActivity?.finish()
val service : PendingIntent? = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
// Start service in Oreo and latest
PendingIntent.getForegroundService(
applicationContext,
8888,
Intent(applicationContext, SensorService::class.java),
PendingIntent.FLAG_ONE_SHOT)
} else {
// Start service in Nougat and older
PendingIntent.getService(
applicationContext,
8888,
Intent(applicationContext, MyService::class.java),
PendingIntent.FLAG_ONE_SHOT)
}
// The great solution introduced by #cgr
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
alarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, 1000, service)
// Kill the current application process to avoid freezing activity
android.os.Process.killProcess(android.os.Process.myPid())
exitProcess(10)
}
}
}
Add to manifest:
<application
android:name="com.example.MyApplication"
...

Frequency of location updates decreases with background location tracking and FusedLocationApi

Google Play Services' Fused Location Provider Api lets you request location updates with location listeners or pending intents. I can successfully request location updates with the location listener but I have been struggling to replicate the same behaviour with pending intents. For the latter, I launch an intent service which handles the location data. What I have noticed during testing is that the location updates correspond with the interval I set in the location request. However, as time goes by the interval between updates increases tremendously even though the interval in the location request has remained constant. I have noticed this behaviour on several occasions with multiple devices. Does anyone have an idea what could be going on?
Foreground location tracking
protected void requestLocationUpdates()
{
mIsTracking = true;
LocationRequest locationRequest = mLocationRequests.get(mPreviousDetectedActivity.getType());
if (locationRequest != null)
{
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient, locationRequest, this);
}
}
#Override
public void onLocationChanged(Location location)
{
Log.i(TAG, "onLocationChanged");
handleLocationChanged(location);
}
Background location tracking
protected void requestLocationUpdates()
{
LocationRequest locationRequest = mLocationRequests.get(mPreviousDetectedActivity.getType());
if (locationRequest != null)
{
mResultCallabackMessage = "Request location updates ";
PendingIntent pendingIntent = getLocationPendingIntent();
LocationServices.FusedLocationApi.requestLocationUpdates(mGoogleApiClient,
locationRequest, pendingIntent).setResultCallback(this);
}
}
protected PendingIntent getLocationPendingIntent()
{
if (mLocationPendingIntent != null) return mLocationPendingIntent;
Intent intent = new Intent(this, LocationUpdatesIntentService.class);
mLocationPendingIntent = PendingIntent.getService(this, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
return mLocationPendingIntent;
}
public class LocationUpdatesIntentService extends IntentService
{
public LocationUpdatesIntentService()
{
// Use the TAG to name the worker thread.
super(TAG);
}
#Override
public void onCreate()
{
super.onCreate();
}
#Override
protected void onHandleIntent(Intent intent)
{
Bundle bundle = intent.getExtras();
Location location = (Location) bundle.get(FusedLocationProviderApi.KEY_LOCATION_CHANGED);
handleLocationUpdates(location);
}
}
Any help is greatly appreciated.
It could be because some other application in the device must have requested for more frequent updates. Please refer this link for more details
This interval is inexact. You may not receive updates at all (if no
location sources are available), or you may receive them slower than
requested. You may also receive them faster than requested (if other
applications are requesting location at a faster interval). The
fastest rate that that you will receive updates can be controlled with
setFastestInterval(long)
I'm having the same problem too, but as #Shiv said you should be testing different parameters:
mClient.requestLocationUpdates(LocationRequest.create(), mLocationIntent)
try this:
mClient.requestLocationUpdates(createLocationRequest(), mLocationIntent)
private LocationRequest createLocationRequest() {
LocationRequest locationRequest = new LocationRequest();
locationRequest.setInterval(UPDATE_INTERVAL);
locationRequest.setFastestInterval(FASTEST_UPDATE_INTERVAL);
locationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
locationRequest.setMaxWaitTime(MAX_WAIT_TIME);
return locationRequest;
}

LocationClient doesn't give callback when screen light goes off but my WakefulThread is running flawlessly as expected

To retrieve fused location in background, I have created a library which is very similar to cwac-locpoll library created by Commonsguy.
Inside PollerThread , I am trying to connect, request and retrieve the locations using LocationClient.
I am able to get connected by receiving callback on onConnected method but I am not able to get callback on onLocationChanged method.so my onTimeout thread executes as per decided interval.
NOTE: This issue happens only when screen light goes off.otherwise it works completely fine.
I suspect there might be bug in new Location Api.
Here is the implementation of my PollerThread,
private class PollerThread extends WakefulThread implements GooglePlayServicesClient.ConnectionCallbacks,
GooglePlayServicesClient.OnConnectionFailedListener,LocationListener{
private static final String TAG = "PollerThread";
//context
private Context mContext=null;
private LocationClient mLocationClient=null;
private LocationRequest mLocationRequest=null;
private LocationManager locMgr=null;
private Intent intentTemplate=null;
private Handler handler=new Handler();
private Runnable onTimeout = new Runnable() {
#Override
public void run() {
Log.e(TAG, "onTimeout");
//prepare broadcast intent
Intent toBroadcast=new Intent(intentTemplate);
toBroadcast.putExtra(FusedPoller.EXTRA_ERROR, "Timeout!");
toBroadcast.putExtra(
FusedPoller.EXTRA_ERROR_PROVIDER_DISABLED, false);
toBroadcast.putExtra(FusedPoller.EXTRA_LASTKNOWN,
mLocationClient.getLastLocation());
sendBroadcast(toBroadcast);
//stop the thread
quit();
}
};
PollerThread(Context mContext,LocationRequest mLocationRequest,PowerManager.WakeLock lock, LocationManager locMgr,
Intent intentTemplate) {
super(lock, "LocationPoller-PollerThread");
Log.e(TAG, "PollerThread");
this.mContext=mContext;
this.mLocationRequest=mLocationRequest;
this.locMgr=locMgr;
this.intentTemplate=intentTemplate;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
Log.e(TAG, "onPreExecute");
//setup timeout
setTimeoutAlarm();
//initiate connection
initiateConnection();
}
#Override
protected void onPostExecute() {
super.onPostExecute();
Log.e(TAG, "onPostExecute");
//remove timeout
removeTimeoutAlarm();
//disconnect
initiateDisconnection();
}
/**
* Called when the WakeLock is completely unlocked.
* Stops the service, so everything shuts down.
*/
#Override
protected void onUnlocked() {
Log.e(TAG, "onUnlocked");
stopSelf();
}
private void setTimeoutAlarm() {
Log.e(TAG, "setTimeoutAlarm");
handler.postDelayed(onTimeout, FusedLocationUtils.DEFAULT_TIMEOUT);
}
private void removeTimeoutAlarm()
{
Log.e(TAG, "removeTimeoutAlarm");
handler.removeCallbacks(onTimeout);
}
private void initiateConnection()
{
Log.e(TAG, "initiateConnection");
mLocationClient = new LocationClient(this.mContext, this, this);
mLocationClient.connect();
}
private void initiateDisconnection()
{
Log.e(TAG, "initiateDisconnection");
if(mLocationClient.isConnected())
{
mLocationClient.disconnect();
}
}
#Override
public void onConnected(Bundle arg0) {
Log.e(TAG, "onConnected");
Log.e(TAG, "provider: GPS-"+locMgr.isProviderEnabled(LocationManager.GPS_PROVIDER)+" NETWORK-"+locMgr.isProviderEnabled(LocationManager.NETWORK_PROVIDER));
if (!(locMgr.isProviderEnabled(LocationManager.GPS_PROVIDER)) && !(locMgr.isProviderEnabled(LocationManager.NETWORK_PROVIDER))) {
Log.e(TAG, "both disabled");
//get last location and broadcast it
getLastLocationAndBroadcast();
//stop the thread
quit();
}
else
{
Log.e(TAG, "provider enabled");
//get latest location and broadcast it
getLatestLocationAndBroadcast();
//don't quit from here,quit from onLocationChanged
}
}
#Override
public void onDisconnected() {
Log.e(TAG, "onDisconnected");
// TODO Auto-generated method stub
}
#Override
public void onConnectionFailed(ConnectionResult arg0) {
Log.e(TAG, "onConnectionFailed");
// TODO Auto-generated method stub
}
#Override
public void onLocationChanged(Location location) {
Log.e(TAG, "onLocationChanged");
//prepare broadcast intent
Intent toBroadcast=new Intent(intentTemplate);
toBroadcast.putExtra(FusedPoller.EXTRA_LOCATION, location);
sendBroadcast(toBroadcast);
//stop further updates
stopUpdates();
//stop the thread
quit();
}
private void getLatestLocationAndBroadcast() {
Log.e(TAG, "getLatestLocationAndBroadcast");
if(mLocationClient.isConnected() && servicesConnected())
{
Log.e(TAG, "going to request updates");
Log.e(TAG, "lockStatic.isHeld(): "+lockStatic.isHeld());
mLocationClient.requestLocationUpdates(mLocationRequest, this);
}
else
{
Log.e(TAG, "not going to request updates");
}
}
private void stopUpdates() {
Log.e(TAG, "stopUpdates");
if(servicesConnected())
{
Log.e(TAG,getString(R.string.location_updates_stopped));
mLocationClient.removeLocationUpdates(this);
}
else
{
Log.e(TAG,"can't do:"+getString(R.string.location_updates_stopped));
}
}
private void getLastLocationAndBroadcast() {
Log.e(TAG, "getLastLocationAndBroadcast");
if(mLocationClient.isConnected() && servicesConnected())
{
Log.e(TAG, "going to get last location: "+mLocationClient.getLastLocation());
Intent toBroadcast = new Intent(intentTemplate);
toBroadcast.putExtra(FusedPoller.EXTRA_ERROR,
"Location Provider disabled!");
toBroadcast.putExtra(
FusedPoller.EXTRA_ERROR_PROVIDER_DISABLED, true);
toBroadcast.putExtra(FusedPoller.EXTRA_LASTKNOWN,
mLocationClient.getLastLocation());
sendBroadcast(toBroadcast);
}
else
{
Log.e(TAG, "not going to get last location");
}
}
}
and servicesConnected method implementation,
/**
* Verify that Google Play services is available before making a request.
*
* #return true if Google Play services is available, otherwise false
*/
private boolean servicesConnected() {
Log.e(TAG, "servicesConnected");
// Check that Google Play services is available
int resultCode =
GooglePlayServicesUtil.isGooglePlayServicesAvailable(this);
// If Google Play services is available
if (ConnectionResult.SUCCESS == resultCode) {
// In debug mode, log the status
Log.d(FusedLocationUtils.APPTAG, getString(R.string.play_services_available));
// Continue
return true;
// Google Play services was not available for some reason
} else {
// Display an error dialog
Log.d(FusedLocationUtils.APPTAG, getString(R.string.play_services_unavailable));
Toast.makeText(this, getString(R.string.play_services_unavailable), Toast.LENGTH_SHORT).show();
return false;
}
}
If you want to listen to frequent location updates in the background (e.g., every second), you should be running your code inside a Service:
http://developer.android.com/reference/android/app/Service.html
Activities can be ended by the Android platform at any point in time in which they are not in the foreground.
When using a Service, I would recommend having the Service implement the LocationListener directly, and not a Thread inside the Service. For example, use:
public class LocListener extends Service implements com.google.android.gms.location.LocationListener, ...{
I've used this design of implementing the LocationListener directly on the Service with the LocationClient and fused location provider in my GPS Benchmark app and I can confirm that this works even when the screen is off and the app is running in the background.
If you want to listen to occasional location updates in the background (e.g., every minute) using the fused location provider, a better design is to use PendingIntents, using the LocationClient.requestLocationUpdates(Location Request, PendingIntent callbackIntent) method:
https://developer.android.com/reference/com/google/android/gms/location/LocationClient.html#requestLocationUpdates(com.google.android.gms.location.LocationRequest,%20android.app.PendingIntent)
From the above Android doc:
This method is suited for the background use cases, more specifically for receiving location updates, even when the app has been killed by the system. In order to do so, use a PendingIntent for a started service. For foreground use cases, the LocationListener version of the method is recommended, see requestLocationUpdates(LocationRequest, LocationListener).
Any previous LocationRequests registered on this PendingIntent will be replaced.
Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value on the intent.
See the Activity Recognition example for a more detailed description of using PendingIntents to get updates while running in the background:
https://developer.android.com/training/location/activity-recognition.html
Modified excerpts from this documentation are below, changed by me to be specific to location updates.
First declare the Intent:
public class MainActivity extends FragmentActivity implements
ConnectionCallbacks, OnConnectionFailedListener {
...
...
/*
* Store the PendingIntent used to send location updates
* back to the app
*/
private PendingIntent mLocationPendingIntent;
// Store the current location client
private LocationClient mLocationClient;
...
}
Request updates as you currently are, but this time pass in the pending intent:
/*
* Create the PendingIntent that Location Services uses
* to send location updates back to this app.
*/
Intent intent = new Intent(
mContext, LocationIntentService.class);
...
//Set up LocationRequest with desired parameter here
...
/*
* Request a PendingIntent that starts the IntentService.
*/
mLocationPendingIntent =
PendingIntent.getService(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT);
/*
* Request location updates
*/
mLocationClient.requestLocationUpdates(mLocationRequest, callbackIntent);
Handle Location Updates
To handle the Intent that Location Services sends for each update interval, define an IntentService and its required method onHandleIntent(). Location Services sends out ... updates as Intent objects, using the the PendingIntent you provided when you called requestLocationUpdates(). Since you provided an explicit intent for the PendingIntent, the only component that receives the intent is the IntentService you're defining.
Define the class and the required method onHandleIntent():
/**
* Service that receives Location updates. It receives
* updates in the background, even if the main Activity is not visible.
*/
public class LocationIntentService extends IntentService {
...
/**
* Called when a new location update is available.
*/
#Override
protected void onHandleIntent(Intent intent) {
Bundle b = intent.getExtras();
Location loc = (Location) b.get(LocationClient.KEY_LOCATION_CHANGED);
Log.d(TAG, "Updated location: " + loc.toString());
}
...
}
IMPORTANT - to be as efficient as possible, your code in onHandleIntent() should return as quickly as possible to allow the IntentService to shut down. From IntentService docs:
http://developer.android.com/reference/android/app/IntentService.html#onHandleIntent(android.content.Intent)
This method is invoked on the worker thread with a request to process. Only one Intent is processed at a time, but the processing happens on a worker thread that runs independently from other application logic. So, if this code takes a long time, it will hold up other requests to the same IntentService, but it will not hold up anything else. When all requests have been handled, the IntentService stops itself, so you should not call stopSelf().
My understanding of the IntentService design is that you can spawn Threads inside onHandleIntent() to avoid blocking other location updates via platform calls to onHandleIntent(), just be aware that the Service will continue to run until all the running threads terminate.
I've spent days trying to get WiFi and cell-based locations with locked screen with Android 6.0 on Nexus 6.
And looks like the native android location service simple does not allow to do it.
Once device got locked it still collects location update events for 10-15 minutes then stops to providing any of location updates.
In my case the solution was to switch from native Android location service to Google Play Services wrapper called com.google.android.gms.location: https://developers.google.com/android/reference/com/google/android/gms/location/package-summary
Yes, I know that some of Android devices lack of GMS, but for my application this is the only solution to perform.
It does not stop sending location updates even when in the background and device screen is locked.
Me personally prefer RxJava library to wrap this service into a stream (examples included): https://github.com/mcharmas/Android-ReactiveLocation

Need help creating an Android Service which checks if conditions in a Database are met

I am creating an Android application that stores a couple of conditions like Location, Time, Contact and Battery level. What I intend for my application to do is to store these conditions along with some phone setting changes that the user wants to activate if these conditions are met. It would be better if I put my questions in a list :
How do I check the Location of the device at regular intervals ? Is there a listener service (like for the Contacts option the TelephonyManager can be used) that can be used to listen for a callback in case the device location changed ?
Is there a way to schedule a particular function to be called based on Time in Android ?
Is there a way to call a particular function when the battery level of the device changes or the device is charging or unplugged ?
Here's your answer point wise:
1) You can use LocationManager to get the location of your device.
LocationManager locationManager = (LocationManager)
this.getSystemService(Context.LOCATION_SERVICE);
LocationListener locationListener = new LocationListener()
{
public void onLocationChanged(Location location)
{
makeUseOfNewLocation(location); //here you get the new location
}
public void onStatusChanged(String provider, int status, Bundle extras) {}
public void onProviderEnabled(String provider) {}
public void onProviderDisabled(String provider) {}
};
locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, 0, 0, locationListener);
You can also use GeoCoder class to exactly point out the address using the longitude and latitude received from locationManager.
Detail tutorial here - LocationManager
2) You can schedule the particular function to be called - using BroadcastReceiver and AlarmManager service.
private void setRecurringAlarm(Context context) {
// we know mobiletuts updates at right around 1130 GMT.
// let's grab new stuff at around 11:45 GMT, inexactly
Calendar updateTime = Calendar.getInstance();
updateTime.setTimeZone(TimeZone.getTimeZone("GMT"));
updateTime.set(Calendar.HOUR_OF_DAY, 11);
updateTime.set(Calendar.MINUTE, 45);
Intent downloader = new Intent(context, AlarmReceiver.class);
PendingIntent recurringDownload = PendingIntent.getBroadcast(context,
0, downloader, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager alarms = (AlarmManager) getActivity().getSystemService(
Context.ALARM_SERVICE);
alarms.setInexactRepeating(AlarmManager.RTC_WAKEUP,
updateTime.getTimeInMillis(),
AlarmManager.INTERVAL_DAY, recurringDownload);
}
Detail tutorial BroadCastReceiver and AlarmManager
So, if you want you can recurrently check for the present location of device ( using LocationManager as shown in point 1) inside OnReceive of your BroadCastReceiver.
3) There is a BroadCastReceiver to catch when BatteryLevel changes as below:
private BroadcastReceiver mBatInfoReceiver
= new BroadcastReceiver(){
#Override
public void onReceive(Context arg0, Intent intent) {
int level = intent.getIntExtra("level", 0);
// do something...
}
}
registerReceiver(this.mBatInfoReceiver,
new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
For plugin or charging use following code:
// Are we charging / charged?
int status = batteryStatus.getIntExtra(BatteryManager.EXTRA_STATUS, -1);
boolean isCharging = status == BatteryManager.BATTERY_STATUS_CHARGING ||
status == BatteryManager.BATTERY_STATUS_FULL;
// How are we charging?
int chargePlug = battery.getIntExtra(BatteryManager.EXTRA_PLUGGED, -1);
boolean usbCharge = chargePlug == BATTERY_PLUGGED_USB;
boolean acCharge = chargePlug == BATTERY_PLUGGED_AC;
I Hope All this will help you...

android development: get data from BroadcastReceiver called via requestLocationUpdates

I'm new to Android and I'm having the following problem. I'm writing a sample application where I have an intent service that first checks all location providers to get the last known location. If none of the last known locations provides an accurate (or timely) enough location then the location manager's requestLocationUpdates method is called with a BroadcastReceiver intent. Each time the broadcast receiver's onReceive method is called it should check the location to see if it is accurate and/or timely enough. I also have a TimerTask in the intent service that eventually goes off and should check to see if an accurate and/or timely enough location update has been obtained. The problem I'm having is that I don't know how to get the location data obtained in the broadcast receiver back to the intent service. Seems like this should be an easy thing to do but I've been agonizing over this for days. The only way I can think to do it is to write the data to an SQLite db in the broadcast receiver and then read those records back in the intent service, but this seems unnecessarily complicated. Does anyone know what the right way is to get the data back to the intent service? Should I even be using a broadcast receiver for requestLocationUpdates? Is there an easier way to do this? Here is the code
public class GetLocationService extends IntentService {
public GetLocationService() {
super("something");
}
LocationManager locationManager;
long maxFixLateness;
float maxFixPosUncertainty;
boolean usableLocObtained;
Location bestLoc = null;
float bestLocScore = 0;
Intent locChangeI;
PendingIntent pLocChangeI;
#Override
final protected void onHandleIntent(Intent intent) {
maxFixLateness = 30000;
maxFixPosUncertainty = 30;
long curTime = System.currentTimeMillis();
LocationManager locationManager = (LocationManager) this
.getSystemService(Context.LOCATION_SERVICE);
// Check for a usable location fix
List<string> matchingProviders = locationManager.getAllProviders();
for (String provider : matchingProviders) {
Location location = locationManager.getLastKnownLocation(provider);
if (location != null) {
// ...some code to check if the location is accurate or timely
// enough
}
}
if (bestLoc == null) {
locChangeI = new Intent(this, HandleLocationUpdateReceiver.class);
pLocChangeI = PendingIntent.getBroadcast(this, 0, locChangeI,
PendingIntent.FLAG_UPDATE_CURRENT);
usableLocObtained = false;
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER, 0, 0, pLocChangeI);
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER, 0, 0, pLocChangeI);
// Call the timer that will periodically check to see if a usable
// location has been obtained.
new LocFixCheckTimer(60000, 30, 1000);
}
}
private class LocFixCheckTimer {
Timer timer;
long numChecks;
public LocFixCheckTimer(long initSearchTime, long maxRechecks,
long recheckFreq) {
numChecks = maxRechecks;
timer = new Timer();
// Wait 2 seconds before checking for a fix again
timer.schedule(new CheckLocTask(), initSearchTime, recheckFreq);
}
class CheckLocTask extends TimerTask {
public void run() {
if (numChecks > 0) {
if (usableLocObtained == true) {
// I want to use the location data obtained from the
// HandleLocationUpdateReceiver's onReceive method
// but I don't how to get that data here.
}
} else {
// Cancel the timer. We've timed-out on searching
// for a usable location fix
timer.cancel();
}
--numChecks;
}
}
}
}
Here is the broadcast receiver:
public class HandleLocationUpdateReceiver extends BroadcastReceiver
{
#Override
public void onReceive(Context context, Intent intent)
{
Location loc = (Location) intent.getExtras().get(LocationManager.KEY_LOCATION_CHANGED);
if (loc != null)
{
double lat = loc.getLatitude();
double lon = loc.getLongitude();
// Do some checking to see how accurate and timely the location is
// here and somehow get it back to the intent service.
}
}
}
Thanks for the help!
Use a listener for sending back data to your activity or service. It is provided in this link

Categories

Resources