Getting the user's location in Android when the phone is locked

泪湿孤枕 提交于 2021-01-23 01:55:21

问题


I am looking for a solution to get the user's location in a specific time-interval in Android API 17 (Android 4.2) and when the phone is locked.

I've already tried some different code, checked a lot tutorials and searched almost everywhere on the web. The solution might be there, but I think it's a combination of lack of experience with Android developing and interpreting the different right solutions and approaches.

At first I had some pretty basic code, which worked very well when the screen was turned on. Even in the background, the location got updated (as I could check via a Toast message with the longitude and latitude). I used a Handler to do so:

    public void locationRunnable() {
    final Handler locationHandler = new Handler();
    final int distanceDelay = 5000; // milliseconds

    locationHandler.postDelayed(new Runnable(){
        public void run() {
            // code
            mMap.clear();

            if (ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, android.Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                // TODO: Consider calling
                //    ActivityCompat#requestPermissions
                // here to request the missing permissions, and then overriding
                //   public void onRequestPermissionsResult(int requestCode, String[] permissions,
                //                                          int[] grantResults)
                // to handle the case where the user grants the permission. See the documentation
                // for ActivityCompat#requestPermissions for more details.
                return;
            }

            mMap.setMyLocationEnabled(true);
            mMap.setBuildingsEnabled(true);
            mMap.getUiSettings().setMyLocationButtonEnabled(false);

            LocationManager locationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
            Criteria criteria = new Criteria();
            String provider = locationManager.getBestProvider(criteria, true);

            myLocation = locationManager.getLastKnownLocation(provider);

            if (myLocation != null) {
                latitudeCurrentPosition = myLocation.getLatitude();
                longitudeCurrentPosition = myLocation.getLongitude();
            }

            currentPattern = shortTest;
            Notification.Builder notificationBuilderChecking = new Notification.Builder(this)
                    .setSmallIcon(R.mipmap.ic_launcher)
                    .setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher))
                    .setContentTitle("Test app")
                    .setAutoCancel(true)
                    .setOnlyAlertOnce(false)
                    .setContentText("Getting location!")
                    .setPriority(Notification.PRIORITY_MAX)
                    .setLights(0xffffffff, 200, 200)
                    .setVibrate(currentPattern);

            Notification notification2 = notificationBuilderChecking.build();

            NotificationManager notificationMngr2 = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
            notificationMngr2.notify(NOTIFICATION_ID, notification2);

            locationHandler.postDelayed(this, distanceDelay);
        }

    }, distanceDelay);

}

It's just a snippet and the purpose is that in the background, when the screen is locked, this will loop every 10 seconds. And it does. Even when the phone is locked, but only for about 3 times. After 3 times the timer goes up and the phone vibrates less frequent (Doze feature in the way?). Also, the phone does vibrate, but the location isn't updated. When I unlock the phone with the app in the foreground, the location is still at the place when I locked the phone. After a while (10 seconds) it updates. I use a marker on the map to check.

Again: it works when the phone is unlocked.

Now I'm trying to use a Service, a Service (Intent Service), or a Broadcast Receiver, and start a new Thread, but I don't know how and nothing is working.

Some of the last code I have contains a not functioning Broadcast Receiver and the most recent code contains a AlarmManager:

    public void getLocation(Context context) {

    AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context, AlarmIntent.class);
    PendingIntent pi = PendingIntent.getBroadcast(context, 0, intent, 0);
    //After after 30 seconds
    am.setRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, System.currentTimeMillis(), 10000, pi);


    context.getSystemService(Context.CONNECTIVITY_SERVICE);

    mMap.clear();

    mMap.setMyLocationEnabled(true);
    mMap.getUiSettings().setMyLocationButtonEnabled(false);

    LocationManager locationManager = (LocationManager) context.getSystemService(Context.LOCATION_SERVICE);
    Criteria criteria = new Criteria();
    String provider = locationManager.getBestProvider(criteria, true);

    myLocation = locationManager.getLastKnownLocation(provider);

    latitudeCurrentPosition = myLocation.getLatitude();
    longitudeCurrentPosition = myLocation.getLongitude();

    LatLng latLngCurrent = new LatLng(latitudeCurrentPosition, longitudeCurrentPosition);

    mMap.moveCamera(CameraUpdateFactory.newLatLng(latLngCurrent));
    mMap.animateCamera(CameraUpdateFactory.zoomTo(distZoom));

    currentPattern = shortTest;
    showNotification(context);

    mHereIAm = mMap.addMarker(new MarkerOptions()
            .position(new LatLng(latitudeCurrentPosition, longitudeCurrentPosition))
            .title(weAreHere)
            .draggable(false)
            .icon(BitmapDescriptorFactory
                    .fromResource(R.drawable.marker_iconv3)));
    mHereIAm.setTag(0);
    mHereIAm.showInfoWindow();
}

AndroidManifest permissions:

    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="com.android.alarm.permission.SET_ALARM" />

But at the long of 10000, Android Studio is telling me "Value will be forced up to 60000 as of Android 5.1; don't rely on this to be exact..." etc. So an AlarmManager isn't useful either. With the last code, my app isn't even running anymore.

But still: vibrations and stuff still occur, but location updates don't.

In short: I need some basic (at least, I think it just can't be so difficult, as the only problem is that it's not working when the screen is locked) code, that updates my location on a certain, variable interval.

Maybe I have to use a Handler/Runnable, start a new Thread, use a Service or a Broadcast Receiver. Maybe an AlarmManager may work as well, but I don't know how and which to use.

This is my first post. If anything misses or you guys need more information, please ask. I'm trying to be as precise as possible, without using to much overhead.

Edit 01 Can I use a Job Service to do so? - I've updated the API to 21, so I can make use of this service, but I don't know if that's the right solution I'm looking for? Got some great tutorials for the use of it.

Edit 02 Let me be more clear with less overhead: I am looking for a solution to get the user's current location when the device is locked: with an API, a Service, an IntentService, a BroadcastReceiver, ... - every tutorial tells me something different, even here at Stack Overflow I have troubles with finding the right solution. I was able to use a Service as well as an Intent Service, but I cannot request any location updates, because of some errors, like: java.lang.RuntimeException: Unable to start activity ComponentInfo{com.name.name/com.name.name.MapsActivity}: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.google.android.gms.maps.GoogleMap.setMyLocationEnabled(b‌​oolean)' on a null object reference - searching for a solution for this error, gives me another error later on, and on, and on... I got myself stuck in an error-loop and a lot of unnecessary code.

I hope there is a simple way to get the user's location and you guys could help me. Thanks again.

Edit 03 I've followed the instructions on this tutorial and the location is checking. See the following code:

public class LocationService extends Service implements LocationListener {

private final Context mContext;

// flag for GPS status
boolean isGPSEnabled = false;

// flag for network status
boolean isNetworkEnabled = false;

// flag for GPS status
boolean canGetLocation = false;

Location location; // location
double latitude; // latitude
double longitude; // longitude

// The minimum distance to change Updates in meters
private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters

// The minimum time between updates in milliseconds
private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute

// Declaring a Location Manager
protected LocationManager locationManager;

public LocationService(Context context) {
    this.mContext = context;
    getLocation();
}

public Location getLocation() {
    try {
        locationManager = (LocationManager) mContext
                .getSystemService(LOCATION_SERVICE);

        // getting GPS status
        isGPSEnabled = locationManager
                .isProviderEnabled(LocationManager.GPS_PROVIDER);

        // getting network status
        //isNetworkEnabled = locationManager
        //        .isProviderEnabled(LocationManager.NETWORK_PROVIDER);

        if (!isGPSEnabled && !isNetworkEnabled) {
            // no network provider is enabled
        } else {
            this.canGetLocation = true;
            // First get location from Network Provider
            if (isNetworkEnabled) {
                locationManager.requestLocationUpdates(
                        LocationManager.NETWORK_PROVIDER,
                        MIN_TIME_BW_UPDATES,
                        MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                Log.d("Network", "Network");
                if (locationManager != null) {
                    location = locationManager
                            .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                    if (location != null) {
                        latitude = location.getLatitude();
                        longitude = location.getLongitude();
                    }
                }
            }
            // if GPS Enabled get lat/long using GPS Services
            if (isGPSEnabled) {
                if (location == null) {
                    locationManager.requestLocationUpdates(
                            LocationManager.GPS_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                    Log.d("GPS Enabled", "GPS Enabled");
                    if (locationManager != null) {
                        location = locationManager
                                .getLastKnownLocation(LocationManager.GPS_PROVIDER);
                        if (location != null) {
                            latitude = location.getLatitude();
                            longitude = location.getLongitude();
                        }
                    }
                }
            }
        }

    } catch (Exception e) {
        e.printStackTrace();
    }

    return location;
}

/**
 * Stop using GPS listener
 * Calling this function will stop using GPS in your app
 * */
public void stopUsingGPS(){
    if(locationManager != null){
        locationManager.removeUpdates(LocationService.this);
    }
}

/**
 * Function to get latitude
 * */
public double getLatitude(){
    if(location != null){
        latitude = location.getLatitude();
    }

    // return latitude
    return latitude;
}

/**
 * Function to get longitude
 * */
public double getLongitude(){
    if(location != null){
        longitude = location.getLongitude();
    }

    // return longitude
    return longitude;
}

/**
 * Function to check GPS/wifi enabled
 * @return boolean
 * */
public boolean canGetLocation() {
    return this.canGetLocation;
}

/**
 * Function to show settings alert dialog
 * On pressing Settings button will lauch Settings Options
 * */
public void showSettingsAlert(){
    AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);

    // Setting Dialog Title
    alertDialog.setTitle("GPS is settings");

    // Setting Dialog Message
    alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");

    // On pressing Settings button
    alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog,int which) {
            Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
            mContext.startActivity(intent);
        }
    });

    // on pressing cancel button
    alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
        public void onClick(DialogInterface dialog, int which) {
            dialog.cancel();
        }
    });

    // Showing Alert Message
    alertDialog.show();
}

@Override
public void onLocationChanged(Location location) {
}

@Override
public void onProviderDisabled(String provider) {
}

@Override
public void onProviderEnabled(String provider) {
}

@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}

@Override
public IBinder onBind(Intent arg0) {
    return null;
}

}

I've disabled Network location and only allowed GPS location for testing - tested both.

And my MapsActivity:

    public void getLocation(){
    gps = new LocationService(MapsActivity.this);
    if(gps.canGetLocation()) { // gps enabled} // return boolean true/false
        latitudeCurrentPosition = gps.getLatitude(); // returns latitude
        longitudeCurrentPosition = gps.getLongitude(); // returns longitude

        latLngCurrent = new LatLng(latitudeCurrentPosition, longitudeCurrentPosition);

        Toast toastLatCur = makeText(getApplicationContext(), "Lat Current: " + latitudeCurrentPosition + "" ,Toast.LENGTH_SHORT);
        toastLatCur.show();

        Toast toastLongCur = makeText(getApplicationContext(), "Long Current: " + longitudeCurrentPosition + "" ,Toast.LENGTH_SHORT);
        toastLongCur.show();
    }

    else {
        gps.showSettingsAlert();
    }

    if(goToLocation){
        mMap.moveCamera(CameraUpdateFactory.newLatLng(latLngCurrent));
        goToLocation = false;

        if(firstStart){
            mMap.animateCamera(CameraUpdateFactory.zoomTo(distZoom));
            firstStart = false;
        }
    }

    vibrateNotification();
}

When the screen is locked, the phone vibrates as I told in vibrateNotificatoin() - works perfectly every 10 seconds. But the location doesn't get updated! So a Service is not the right way to solve this. Help!


回答1:


You should use the service to perform tasks which are needed to be done even when the application is not running. Give a try.




回答2:


So, I've followed some tutorials and searched and tried more to find out the solution:

  1. Make a new Service:
package com.name.name;


import android.app.AlertDialog;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.Service;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.provider.Settings;
import android.util.Log;

import static com.name.name.MapsActivity.NOTIFICATION_ID;

public class LocationService extends Service implements LocationListener {

    private final Context mContext;

    // flag for GPS status
    boolean isGPSEnabled = false;

    // flag for network status
    boolean isNetworkEnabled = false;

    // flag for GPS status
    boolean canGetLocation = false;

    Location location; // location
    double latitude; // latitude
    double longitude; // longitude

    // The minimum distance to change Updates in meters
    private static final long MIN_DISTANCE_CHANGE_FOR_UPDATES = 10; // 10 meters

    // The minimum time between updates in milliseconds
    private static final long MIN_TIME_BW_UPDATES = 1000 * 60 * 1; // 1 minute

    // Declaring a Location Manager
    protected LocationManager locationManager;

    public LocationService(Context context) {
        this.mContext = context;
        getLocation();
    }

    public Location getLocation() {
        try {
            locationManager = (LocationManager) mContext
                    .getSystemService(LOCATION_SERVICE);

            // getting GPS status
            isGPSEnabled = locationManager
                    .isProviderEnabled(LocationManager.GPS_PROVIDER);

            // getting network status
            isNetworkEnabled = locationManager
                    .isProviderEnabled(LocationManager.NETWORK_PROVIDER);

            if (!isGPSEnabled && !isNetworkEnabled) {
                // no network provider is enabled
            } else {
                this.canGetLocation = true;
                // First get location from Network Provider
                if (isNetworkEnabled) {
                    locationManager.requestLocationUpdates(
                            LocationManager.NETWORK_PROVIDER,
                            MIN_TIME_BW_UPDATES,
                            MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                    Log.d("Network", "Network");
                    if (locationManager != null) {
                        location = locationManager
                                .getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                        if (location != null) {
                            latitude = location.getLatitude();
                            longitude = location.getLongitude();
                        }
                    }
                }
                // if GPS Enabled get lat/long using GPS Services
                if (isGPSEnabled) {
                    if (location == null) {
                        locationManager.requestLocationUpdates(
                                LocationManager.GPS_PROVIDER,
                                MIN_TIME_BW_UPDATES,
                                MIN_DISTANCE_CHANGE_FOR_UPDATES, this);
                        Log.d("GPS Enabled", "GPS Enabled");
                        if (locationManager != null) {
                            location = locationManager
                                    .getLastKnownLocation(LocationManager.GPS_PROVIDER);
                            if (location != null) {
                                latitude = location.getLatitude();
                                longitude = location.getLongitude();
                            }
                        }
                    }
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        return location;
    }

    /**
     * Stop using GPS listener
     * Calling this function will stop using GPS in your app
     * */
    public void stopUsingGPS(){
        if(locationManager != null){
            locationManager.removeUpdates(LocationService.this);
        }
    }

    /**
     * Function to get latitude
     * */
    public double getLatitude(){
        if(location != null){
            latitude = location.getLatitude();
        }

        // return latitude
        return latitude;
    }

    /**
     * Function to get longitude
     * */
    public double getLongitude(){
        if(location != null){
            longitude = location.getLongitude();
        }

        // return longitude
        return longitude;
    }

    /**
     * Function to check GPS/wifi enabled
     * @return boolean
     * */
    public boolean canGetLocation() {
        return this.canGetLocation;
    }

    /**
     * Function to show settings alert dialog
     * On pressing Settings button will lauch Settings Options
     * */
    public void showSettingsAlert(){
        AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);

        // Setting Dialog Title
        alertDialog.setTitle("GPS is settings");

        // Setting Dialog Message
        alertDialog.setMessage("GPS is not enabled. Do you want to go to settings menu?");

        // On pressing Settings button
        alertDialog.setPositiveButton("Settings", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog,int which) {
                Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
                mContext.startActivity(intent);
            }
        });

        // on pressing cancel button
        alertDialog.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int which) {
                dialog.cancel();
            }
        });

        // Showing Alert Message
        alertDialog.show();
    }

    @Override
    public void onLocationChanged(Location location) {
    }

    @Override
    public void onProviderDisabled(String provider) {
    }

    @Override
    public void onProviderEnabled(String provider) {
    }

    @Override
    public void onStatusChanged(String provider, int status, Bundle extras) {
    }

    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }

}
  1. Run this service every time you want, with a Handler and Runnable and use a Wakelock:
    • in OnCreate:
    PowerManager powerManager = (PowerManager) getSystemService(POWER_SERVICE);
    wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
            "MyWakelockTag");
  • do stuff with the aquired data form the LocationService.
  • Start this function whenever you need the data and do things with it:

public void startDoingStuffWithLocationData(){

                final Runnable runnable= new Runnable() {
                    public void run() {
                        // do stuff here
                        getLocation();
                        }

                        else {
                            handler.postDelayed(this, 10000);
                        }
                    }
                };
                runnable.run();
            }
    }
  • Get the data from the Service:

public void getLocation(){

    wakeLock.acquire();

    gps = new LocationService(MapsActivity.this);
    if(gps.canGetLocation()) { // gps enabled} // return boolean true/false
        latitudeCurrentPosition = gps.getLatitude(); // returns latitude
        longitudeCurrentPosition = gps.getLongitude(); // returns longitude

        latLngCurrent = new LatLng(latitudeCurrentPosition, longitudeCurrentPosition);
    }

    else {
        gps.showSettingsAlert();
    }

    wakeLock.release();
}

Be sure to use the release() function to end the wakelock to preserve battery.



来源:https://stackoverflow.com/questions/42304377/getting-the-users-location-in-android-when-the-phone-is-locked

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!