LocationClient doesn't give a callback when the screen turns off, but my WakefulThread works flawlessly as expected

To get a fused location in the background, I created a library that is very similar to cwac-locpoll created by Commonsguy.

Inside PollerThread, I am trying to connect, request and retrieve locations using LocationClient .

I can connect by receiving a callback using the onConnected method, but I cannot receive a callback in the onLocationChanged method.so my onTimeout thread runs according to a specific interval.

NOTE: This problem only occurs when the screen turns off. Otherwise, it works completely.

I suspect that an error may occur in the new Api location.

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 ,

  /** * 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; } } 
+6
source share
2 answers

If you want to listen to frequent location updates in the background (for example, every second), you must run your code inside the Service :

http://developer.android.com/reference/android/app/Service.html

Activity can be completed by the Android platform at any time when they are not in the foreground.

When using the Service, I would recommend that the Service implement the LocationListener directly, and not Thread inside the Service. For example, use:

 public class LocListener extends Service implements com.google.android.gms.location.LocationListener, ...{ 

I used this project to implement a LocationListener directly in the Service with a LocationClient and a smooth access provider in my application 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 document:

This method is suitable for use in the background, more specifically for receiving location updates, even when the application was killed by the system. To do this, use PendingIntent for the running service. For cases of using the foreground, it is recommended to use the version of the LocationListener method, see RequestLocationUpdates (LocationRequest, LocationListener).

All previous LocationRequests registered on this PendingIntent will be replaced.

Location updates are sent with the key KEY_LOCATION_CHANGED and the location value in the intent.

For a more detailed description of using PendingIntents see the Activity Recognition example to receive updates while running in the background:

https://developer.android.com/training/location/activity-recognition.html

Modified excerpts from this documentation are given below, modified by me to be specific to location updates.

First declare the intention:

 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 are now, but this time pass in anticipation of an intention:

 /* * 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); 

Location Updates for Mailboxes

To handle the intent that location services send for each update interval, define the IntentService and its required onHandleIntent () method. Location services send ... updates as Intent objects using the PendingIntent that you specified when calling requestLocationUpdates (). Since you provided an explicit intent for the PendingIntent, the only component that receives the intent is the IntentService that you define.

Define the class and the required onHandleIntent () method:

 /** * 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 - in order to be as efficient as possible, your code in onHandleIntent() must return as soon as possible to allow the IntentService to exit. From the IntentService docs:

http://developer.android.com/reference/android/app/IntentService.html#onHandleIntent(android.content.Intent)

This method is called on a workflow with a request for processing. Only one Intent is processed at a time, but processing occurs in a workflow that runs independently of other application logic. Thus, if this code takes a lot of time, it will delay other requests to the same IntentService, but it will not delay anything. When all requests are processed, the IntentService stops, so you should not call stopSelf ().

My understanding of the design of IntentService is that you can create threads inside onHandleIntent() to avoid blocking other location updates through platform calls on onHandleIntent() , just keep in mind that the Service will continue to work until all running threads to stop.

+17
source

I spent several days trying to get WiFi and cellular addresses with a locked screen from Android 6.0 to Nexus 6. And it seems that the usual location service on Android does not allow this. After the device is locked, it still collects location update events for 10-15 minutes, then stops to provide any location update.

In my case, the solution was to switch from Android's own location service to a Google Play Services shell called com.google.android.gms.location: https://developers.google.com/android/reference/com/google / android / gms / location / package-summary

Yes, I know that on some Android devices there is no GMS, but for my application this is the only solution to execute.

It does not stop sending location updates, even if in the background and the device’s screen is locked.

I personally prefer the RxJava library for transferring this service to a stream (examples included): https://github.com/mcharmas/Android-ReactiveLocation

0
source

Source: https://habr.com/ru/post/949310/


All Articles