Search code examples
javaandroidgpslocationfusedlocationproviderclient

Requesting location updates using FusedLocationProviderClient is not working, callback is never called


I'm trying to use Firebase Cloud Messaging to send a command to my Android app that prompts it to determine its current location. This is done in the class "FCMService".

The class "SingleShotLocationProvider" then performs the actual work by requesting location updates using the FusedLocationProviderClient. However, the callback "fusedTrackerCallback" is never called ever, although the necessary permissions are granted and GPS is switched on. Why?

FCMService class

public class FCMService extends FirebaseMessagingService {

    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
        Log.d("FCMService", "Message received");

            if(remoteMessage.getData().containsKey("command") && remoteMessage.getData().get("command").equalsIgnoreCase("locate")) {
                // get current location
                SingleShotLocationProvider locProv = new SingleShotLocationProvider(getApplicationContext());
                locProv.requestSingleUpdate();
                System.out.println("Requested single location update.");
            }

        }
    }

}

SingleShotLocationProvider class

public class SingleShotLocationProvider {

    private Context context;

    final LocationManager locManager;
    private FusedLocationProviderClient mFusedLocationClient;
    private LocationCallback fusedTrackerCallback;

    public SingleShotLocationProvider(Context context) {
        this.context = context;
        mFusedLocationClient = LocationServices.getFusedLocationProviderClient(context);

        locManager = (LocationManager) context.getSystemService( Context.LOCATION_SERVICE );
        if (locManager != null && !locManager.isProviderEnabled(LocationManager.GPS_PROVIDER)){
            Log.e("SiSoLocProvider", "GPS IS NOT enabled.");
        } else {
            Log.d("SiSoLocProvider", "GPS is enabled.");
        }
    }

    @TargetApi(16)
    public void requestSingleUpdate() {
        Looper.prepare();

        // only works with SDK Version 23 or higher
        if (android.os.Build.VERSION.SDK_INT >= 23) {
            if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                // permission is not granted
                Log.e("SiSoLocProvider", "Permission not granted.");
                return;
            } else {
                Log.d("SiSoLocProvider", "Permission granted.");
            }
        } else {
            Log.d("SiSoLocProvider", "SDK < 23, checking permissions should not be necessary");
        }

        final long startTime = System.currentTimeMillis();
        fusedTrackerCallback = new LocationCallback(){
            @Override
            public void onLocationResult(LocationResult locationResult) {
                if((locationResult.getLastLocation() != null) && (System.currentTimeMillis() <= startTime+30*1000)) {
                    System.out.println("LOCATION: " + locationResult.getLastLocation().getLatitude() + "|" + locationResult.getLastLocation().getLongitude());
                    System.out.println("ACCURACY: " + locationResult.getLastLocation().getAccuracy());
                    mFusedLocationClient.removeLocationUpdates(fusedTrackerCallback);
                } else {
                    System.out.println("LastKnownNull? :: " + (locationResult.getLastLocation() == null));
                    System.out.println("Time over? :: " + (System.currentTimeMillis() > startTime+30*1000));
                }
            }
        };

        LocationRequest req = new LocationRequest();
        req.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        req.setFastestInterval(2000);
        req.setInterval(2000);
        mFusedLocationClient.requestLocationUpdates(req, fusedTrackerCallback, null);
    }
}

Output is:

//Message received
//GPS is enabled.
//Permission granted.
//Requested single location update.

But nothing more then. Why? Is it perhaps because the permission was granted via the service application context?


Solution

  • From your code I guess you want to get location and receive result in a background thread. Here is a solution.

    @TargetApi(16)
    public void requestSingleUpdate() {
        // TODO: Comment-out this line.
        // Looper.prepare();
    
        // only works with SDK Version 23 or higher
        if (android.os.Build.VERSION.SDK_INT >= 23) {
            if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                // permission is not granted
                Log.e("SiSoLocProvider", "Permission not granted.");
                return;
            } else {
                Log.d("SiSoLocProvider", "Permission granted.");
            }
        } else {
            Log.d("SiSoLocProvider", "SDK < 23, checking permissions should not be necessary");
        }
    
        // TODO: Start a background thread to receive location result.
        final HandlerThread handlerThread = new HandlerThread("RequestLocation");
        handlerThread.start();
    
        final long startTime = System.currentTimeMillis();
        fusedTrackerCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                // TODO: Those lines of code will run on the background thread.
                if ((locationResult.getLastLocation() != null) && (System.currentTimeMillis() <= startTime + 30 * 1000)) {
                    System.out.println("LOCATION: " + locationResult.getLastLocation().getLatitude() + "|" + locationResult.getLastLocation().getLongitude());
                    System.out.println("ACCURACY: " + locationResult.getLastLocation().getAccuracy());
                    mFusedLocationClient.removeLocationUpdates(fusedTrackerCallback);
                } else {
                    System.out.println("LastKnownNull? :: " + (locationResult.getLastLocation() == null));
                    System.out.println("Time over? :: " + (System.currentTimeMillis() > startTime + 30 * 1000));
                }
                // TODO: After receiving location result, remove the listener.
                mFusedLocationClient.removeLocationUpdates(this);
    
                // Release the background thread which receive the location result.
                handlerThread.quit();
            }
        };
    
        LocationRequest req = new LocationRequest();
        req.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        req.setFastestInterval(2000);
        req.setInterval(2000);
        // TODO: Pass looper of background thread indicates we want to receive location result in a background thread instead of UI thread.
        mFusedLocationClient.requestLocationUpdates(req, fusedTrackerCallback, handlerThread.getLooper());
    }
    

    If you want to receive location result on UI thread.

    @TargetApi(16)
    public void requestSingleUpdate() {
        // TODO: Comment-out this line.
        // Looper.prepare();
    
        // only works with SDK Version 23 or higher
        if (android.os.Build.VERSION.SDK_INT >= 23) {
            if (context.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED || context.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                // permission is not granted
                Log.e("SiSoLocProvider", "Permission not granted.");
                return;
            } else {
                Log.d("SiSoLocProvider", "Permission granted.");
            }
        } else {
            Log.d("SiSoLocProvider", "SDK < 23, checking permissions should not be necessary");
        }
    
        final long startTime = System.currentTimeMillis();
        fusedTrackerCallback = new LocationCallback() {
            @Override
            public void onLocationResult(LocationResult locationResult) {
                // TODO: These lines of code will run on UI thread.
                if ((locationResult.getLastLocation() != null) && (System.currentTimeMillis() <= startTime + 30 * 1000)) {
                    System.out.println("LOCATION: " + locationResult.getLastLocation().getLatitude() + "|" + locationResult.getLastLocation().getLongitude());
                    System.out.println("ACCURACY: " + locationResult.getLastLocation().getAccuracy());
                    mFusedLocationClient.removeLocationUpdates(fusedTrackerCallback);
                } else {
                    System.out.println("LastKnownNull? :: " + (locationResult.getLastLocation() == null));
                    System.out.println("Time over? :: " + (System.currentTimeMillis() > startTime + 30 * 1000));
                }
    
                // TODO: After receiving location result, remove the listener.
                mFusedLocationClient.removeLocationUpdates(this);
            }
        };
    
        LocationRequest req = new LocationRequest();
        req.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);
        req.setFastestInterval(2000);
        req.setInterval(2000);
        // Receive location result on UI thread.
        mFusedLocationClient.requestLocationUpdates(req, fusedTrackerCallback, Looper.getMainLooper());
    }
    

    I add each comment that start with TODO: keyword to explain my intended.