codepath / android_guides

Extensive Open-Source Guides for Android Developers
guides.codepath.com
MIT License
28.3k stars 6.37k forks source link

A good example of background service getting location updates #220

Closed betorobson closed 6 years ago

betorobson commented 7 years ago

Please I really need help.

I'm facing a problem here. It is impossible to find A GOOD EXAMPLE of how create a service that must run in background and receive location updates. Also, all examples on developer.android.com are terrible, any one of them really works.

My app aim is get location updates in background, something which looks like so simple but unfortunately I could not find any good example about how to do it.

veda2vratha commented 7 years ago

Hello

Please try these links once

http://stackoverflow.com/questions/28535703/best-way-to-get-user-gps-location-in-background-in-android

http://stackoverflow.com/questions/8828639/android-get-gps-location-via-a-service

betorobson commented 7 years ago

@veda2vratha thank you very much, I have tried these examples that you have sent to me. Actually they are both the same.

Testing it I could see in this example it runs every second requesting a very precisely location. So, I change it to every 20 seconds and 500m for distance which gives me a service running every 20 seconds.

Then I realise that I could be making a mistake about what I'm looking for. The point is, my app does not need a very precisely location in addition, it does not need to run every second ou minute. My app needs only collect relevant location position.

So, what I need indeed is a passive location collector. I've been told that Android OS can broadcast for any app in passive mode location position changes. This is great because reduce battery usage. So, my service location (listener location) will be fired only when Android OS has been request for location by other app for example, Google Maps.

After a few hours looking deep on location providers I found it which is called LocationManager.PASSIVE_PROVIDER. Thats is really cool, looks like exactly what I need.

Here I will share how my service looks like now.

Now, I would like to make a question, why any developer and even on Android Training do NOT talk about LocationManager.PASSIVE_PROVIDER? What is the matter about using it?

I would like to say tank you @veda2vratha again ๐Ÿ‘

My Location Service:

package com.example.roberto.trainingbackgroundservice;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.location.Location;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.ActivityCompat;
import android.util.Log;

/**
 * Created by roberto on 9/29/16.
 */

public class MyLocationService extends Service {
    private static final String TAG = "MyLocationService";
    private LocationManager mLocationManager = null;
    private static final int LOCATION_INTERVAL = 1000;
    private static final float LOCATION_DISTANCE = 10f;

    private class LocationListener implements android.location.LocationListener {
        Location mLastLocation;

        public LocationListener(String provider) {
            Log.e(TAG, "LocationListener " + provider);
            mLastLocation = new Location(provider);
        }

        @Override
        public void onLocationChanged(Location location) {
            Log.e(TAG, "onLocationChanged: " + location);
            mLastLocation.set(location);
        }

        @Override
        public void onProviderDisabled(String provider) {
            Log.e(TAG, "onProviderDisabled: " + provider);
        }

        @Override
        public void onProviderEnabled(String provider) {
            Log.e(TAG, "onProviderEnabled: " + provider);
        }

        @Override
        public void onStatusChanged(String provider, int status, Bundle extras) {
            Log.e(TAG, "onStatusChanged: " + provider);
        }
    }

    /*
    LocationListener[] mLocationListeners = new LocationListener[]{
            new LocationListener(LocationManager.GPS_PROVIDER),
            new LocationListener(LocationManager.NETWORK_PROVIDER)
    };
    */

    LocationListener[] mLocationListeners = new LocationListener[]{
            new LocationListener(LocationManager.PASSIVE_PROVIDER)
    };

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.e(TAG, "onStartCommand");
        super.onStartCommand(intent, flags, startId);
        return START_STICKY;
    }

    @Override
    public void onCreate() {

        Log.e(TAG, "onCreate");

        initializeLocationManager();

        try {
            mLocationManager.requestLocationUpdates(
                    LocationManager.PASSIVE_PROVIDER,
                    LOCATION_INTERVAL,
                    LOCATION_DISTANCE,
                    mLocationListeners[0]
            );
        } catch (java.lang.SecurityException ex) {
            Log.i(TAG, "fail to request location update, ignore", ex);
        } catch (IllegalArgumentException ex) {
            Log.d(TAG, "network provider does not exist, " + ex.getMessage());
        }

        /*try {
            mLocationManager.requestLocationUpdates(
                    LocationManager.GPS_PROVIDER,
                    LOCATION_INTERVAL,
                    LOCATION_DISTANCE,
                    mLocationListeners[1]
            );
        } catch (java.lang.SecurityException ex) {
            Log.i(TAG, "fail to request location update, ignore", ex);
        } catch (IllegalArgumentException ex) {
            Log.d(TAG, "gps provider does not exist " + ex.getMessage());
        }*/
    }

    @Override
    public void onDestroy() {
        Log.e(TAG, "onDestroy");
        super.onDestroy();
        if (mLocationManager != null) {
            for (int i = 0; i < mLocationListeners.length; i++) {
                try {
                    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) {
                        return;
                    }
                    mLocationManager.removeUpdates(mLocationListeners[i]);
                } catch (Exception ex) {
                    Log.i(TAG, "fail to remove location listener, ignore", ex);
                }
            }
        }
    }

    private void initializeLocationManager() {
        Log.e(TAG, "initializeLocationManager - LOCATION_INTERVAL: "+ LOCATION_INTERVAL + " LOCATION_DISTANCE: " + LOCATION_DISTANCE);
        if (mLocationManager == null) {
            mLocationManager = (LocationManager) getApplicationContext().getSystemService(Context.LOCATION_SERVICE);
        }
    }
}
doyer3112 commented 7 years ago

also,i was confused with this problem these days,and found some mobile (maybe the Android Version caused)is ok,some is not work,can't get the location callback (location changed) in background.i have try your written service,didn't work after App switch to background

vani-chikaraddi-28 commented 7 years ago

How can i call in MainActivity? Can you please help me

YogeshLegendkiller commented 7 years ago

startservice(new Intent(this,MyLocationService .class)); @vani-chikaraddi

KingIdee commented 7 years ago

You can try out Google's Locations API...

vani-chikaraddi-28 commented 7 years ago

I want to display longitude and latitude how can i do that. I tried the following code but getting white screen

@Override public void onLocationChanged(Location location) { Log.e(TAG, "onLocationChanged: " + location); mLastLocation.set(location); longitude=location.getLongitude(); latitude=location.getLatitude(); Toast.makeText(MyLocationService.this,longitude + "yes" + latitude + " ",Toast.LENGTH_LONG).show(); }

AndyCr15 commented 6 years ago

I have used the code from SO linked above too, but how do I pass the location found back to my app, so I can update location information in Firebase? Thanks!

Avrahamreiss commented 6 years ago

"but how do I pass the location found back to my app,?" I have created a background app where the user determines how often (in minutes) location services should be activated. It then sends info to SMS, email or both. The easy way (which I use) to transfer data within an app is SharedPreferences. It looks so easy that I was at first sceptical about its use - but it has never failed me.

AndyCr15 commented 6 years ago

Thanks, I found I send it back in a broadcast.

betorobson commented 6 years ago

@AndyCr15 , definitely one of the easiest way to do that is sending the collected data trough broadcast message. :-)

gvsharma commented 6 years ago

really helpful

hetang-shah commented 6 years ago

very helpful. Thanks for sharing.

NancyAndroid commented 6 years ago

Hello

I am developing an app and there is one scenario in which I have to send user location on server every 15 min all is working but sometime current location is incorrect.

I need help to get current accurate location . I have created GPS Tracker - Using GPS provider and Network provider Now the issue is

Please suggest what I have to implement to get current & accurate location in Android app.

RaziaSandhu commented 6 years ago

You can use

mGoogleApiClient = new GoogleApiClient.Builder(this) .addConnectionCallbacks(this) .addOnConnectionFailedListener(this) .addApi(LocationServices.API) .build(); and when it is connected

try { Location mLastLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); if (mLastLocation != null) {
lat = mLastLocation.getLatitude(); longi = mLastLocation.getLongitude();
} else { Toast.makeText(getApplicationContext(), "Unable to get Location Check GPS is ON", Toast.LENGTH_LONG).show(); showSettingsAlert(); } } catch (Exception e) { e.printStackTrace();
}

NancyAndroid commented 6 years ago

@RaziaSandhu Hi Thanks for your reply. I need location without Google play services. This app is for dummy devices for security purpose So there is no play store and play services.

balireddy commented 6 years ago

Hi,

I am working on Gps to open the plot details if the plot(lat,long)details is matched open the plot details otherwise it show a message , and it working for 5 plots after that if he is not in that plot and the plot is opend . In other case when i restart the device again it works fine. this is the problem i am facing can any one help me fast.. i have only one day to submit this work.

this is my location service class pice of code boolean gpsProviderEnabled = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); boolean networkProviderEnabled = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);

        if (gpsProviderEnabled || networkProviderEnabled) {
            if (networkProviderEnabled) {
                locationManager.requestLocationUpdates(LocationManager.NETWORK_PROVIDER, MIN_UPDATE_TIME, MIN_UPDATE_DISTANCE, this);
                if (locationManager != null) {
                    location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
                    providerType = "network";
                    Log.d(LOG_TAG, "network lbs provider:" + (location == null ? "null" : String.valueOf(location.getLatitude()) + "," + String.valueOf(location.getLongitude())));
                    updateLocation(location);
                }
            }

            if (gpsProviderEnabled && location == null) {
                locationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, MIN_UPDATE_TIME, MIN_UPDATE_DISTANCE, this);
                if (locationManager != null) {
                    if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED && ActivityCompat.checkSelfPermission(this, 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;
                    }
                    location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
                    providerType = "gps";
                    Log.d(LOG_TAG, "gps lbs provider:" + (location == null ? "null" : String.valueOf(location.getLatitude()) + "," + String.valueOf(location.getLongitude())));
                    updateLocation(location);
                }
            }
        }
arahant commented 6 years ago

This is exactly I was looking for, thanks. Iโ€™m building a custom gps tracker, with plotting my path as my location changes, and plotting an altitude-time graph parallely. This creates too much workload on the main UI thread, and it was skipping frames. This will reduce considerable overload.

Veer50 commented 6 years ago

@betorobson and @veda2vratha Thank you so much. I was looking for this everywhere. finally i got the answer from you.

Veer50 commented 6 years ago

@toton6868 I am not sure about it will work in this case, but whenever i want to pass data between activities i use Dependancy Injection which is achieved by Dagger.You can inject the instance of this class into another class and get access to the location variable from where you can fetch longitude and latitude(Read about Dagger..). As I am also beginner i don't know how to send location details to server periodically.

betorobson commented 6 years ago

I'm so glad to help others with my snippet code. @toton6868 , have you tried a simple? Here you go a very simple straightforward example that I believe can help you: https://developer.android.com/reference/android/os/AsyncTask.html

Flul2F commented 6 years ago

@veda2vratha , @betorobson

Hello and thanks ! it's working great !

i have a question and post it here 'cause i explored internet since solution.

Let me explain:

I had to send my location to our server (REST API with Express) with a Simply GET +appId (generate Unique ID) + lat + long. For that i had to retrieve my appId and my token for my request, store in my share preferences.

I used final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this );

I saw a lot of responses about context in services and i already test every solutions, i try in the onCreate / onLocationChanged / onStartCommand but its not recognize my Token or appId .

is there a solution? i'm sorry i'm from the web (html/css/js/php) and i'm juste start learning Java Life Cycle it's hard for right now

mehulgohil9957 commented 6 years ago

Try this, final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()); or Try using name of sharePreference where you stored it by, SharedPreferences.getSharedPreferences("NAME_HERE",Context.MODE_PRIVATE);

noman-iu commented 6 years ago

OnLocationChange is not running...

AroundPixels commented 6 years ago

About the topic, 3 good recomendations based on my experience:

betorobson commented 6 years ago

@AroundPixels , The aim of my question in this topic is opposite of your suggestion. It is pretty common to see implementation of location update running in Foreground Service however, my poporse here is that app listener for those location updates running in passive mode which means less CPU and Battery usage.

For who is looking for a foreground service here it NOT the case.

betorobson commented 6 years ago

This issue has been solved by the following solution: https://github.com/codepath/android_guides/issues/220#issuecomment-250756857

AroundPixels commented 6 years ago

Hi @betorobson . I'm only sharing my experience about location services, because I've done several with huge precision requirements. If you need precision, you will need wake locks. I've tested them in hundreds of devices in production mode, and as you may know, every device is different when screen is switch off. Lot of Android surprises.

Anyway, the differences between Service and Foreground Service are minimal: widget, and priority level. Implementation is the same, so I though my advices my be useful for someone who get this thread and needs a minimal slightly different location service than you.

If you were offended by my post, I'm really sorry for you.

betorobson commented 6 years ago

It is all good @AroundPixels , I have not get offended. My concern it is just keep the mindset of this topic about Location service in Passive Mode.

sairamandroid commented 6 years ago

Hi @betorobson ,

I have checked your example above for fetching the latitude and longitude via service.

Can you please let me know how to get the latitude and longitude when the Mobile Device is Offline state (i.e when there is no Internet Data connectivity and Wifi Connectivity ) only GPS is enabled. Will we be able to fetch the Latitude and Longitude.

betorobson commented 6 years ago

@sairamandroid , the OS will handle it for you, which means you do not to do anything. What I can tell you is that under hood the Android APP request/listen for location changes and the Android OS will grab it for you using what is available at the moment.

In my example. it is not requesting, just listening for significantly location changes. I've try it for 3 months, collecting location data, saving on user data and displaying in a mapview. The result was impressive. Driving all weekends to the beach, 120km far way from my home it display my history position including places that has no mobile networking or wifi.

I will share my demo project for everyone here in a couple of days, it looks like that will help more than just a code snippet.

sairamandroid commented 6 years ago

Hi @betorobson ,

Thank You for the Reply.

I have two queries :

  1. You were mentioning as to request/listen for location changes and Android will grap and provide. Can you please share piece of code for this. So that I can check this functionality.

  2. Also I have other query i.e If we call the this MyLocationService Service class once, Will it be keep on running in the background to fetch the current latitude and longitude. ( i.e will the service be running constantly or we need to invoke it at a certain interval to get latitude and longitude. )

betorobson commented 6 years ago

@sairamandroid Here you go:

  1. If you want request the location for instance, create a button and every time the user that it your app request the current location, here is the example for it. https://github.com/codepath/android_guides/wiki/Retrieving-Location-with-LocationServices-API

  2. Indeed, it will running in background forever and the cool part is that it will not drain you battery because it is just listening for location changes in passive mode. Here is the code snippet. https://github.com/codepath/android_guides/issues/220#issuecomment-250756857

sairamandroid commented 6 years ago

Hi @betorobson ,

So in the code snippet that you had shared, For the first time if we register the location listener then whenever there is location change then directly onLocationChanged will be called and we can get the updated locations?

Kindly confirm the flow as this process is running in background I need clarity regarding the flow.

Basically my requirement is a service should run in background constantly and fetch the location without users intervention.

betorobson commented 6 years ago

@sairamandroid , for sure it will run in background, once the app starts the background service will start at the same time listening for location changes. This snippet code will address exactly your requirements.

sairamandroid commented 6 years ago

@betorobson ,

Thank You.

Zain-khan commented 6 years ago

how can i get latitude and longitude from another activity ..

mirzaadil commented 6 years ago

B

Hi,

Used Google api location change listener

On Apr 20, 2018 7:35 PM, Zain notifications@github.com wrote:

how can i get latitude and longitude from another activity ..

โ€” You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHubhttps://github.com/codepath/android_guides/issues/220#issuecomment-383116285, or mute the threadhttps://github.com/notifications/unsubscribe-auth/ADn8Iu2jtvLSPemZA-AiKu5Lqu_Hox--ks5tqfJHgaJpZM4KKc6K.

M1Ergash commented 5 years ago

thank you veda2vrath ! it was very helpful

JorgeNamitleCh commented 5 years ago

@betorobson

and as you show the values โ€‹โ€‹of latitude and longitude in the MainActivity consuming the service, the code that you provided, and knowing what would be the most correct way to be sending the information to a server to show in web platform, new in this help

JorgeNamitleCh commented 5 years ago

Run Gps as background service and send coordinates to web server (PHP)

zzzaJ commented 5 years ago

Not getting location update information, even when location changes...

Any suggestions? Using your code exactly.

amitdoiphode11 commented 5 years ago

Hello Everyone,

I have some question about location tracking and background services like (background/foreground/firebasejobdispatcher/jobshedular/workmanager).

Currently, my app is targetting android 28 version. And I am testing on Android 8.0 now. Above code is working perfectly on pre-O and post-O android version. But getting location after every 1 min.

I am creating foreground service but some devices like MI which stop this foreground service. And is the correct way to get locations or service will run in the background?

jobshedular/workmanager periodic time which is not lesser than 15 min.

Have anyone working on facing the same problem and resolving then please guide me.

Thank you

saadmohammed commented 5 years ago

can anyone tell me how to store that location in the MySql database using Rest API

amitdoiphode11 commented 5 years ago

can anyone tell me how to store that location in the MySql database using Rest API

@saadmohammed I don't know is this correct or not but currently, We are creating a geoJson for location parameter. And push this geoJson to the server using API.

You can store this geoJson in your MySQL Db.

mackongo commented 5 years ago

How can i call in MainActivity? Can you please help me

Intent startLocationService = new Intent(MainActivity.this, MyLocationService.class); startService(startLocationService);

Make sure you declare the service on androidmanifest.xml

ipsjolly commented 5 years ago

Check Example Application usimg latest version of location upsate service FusedLocationProviderClient tutorial link https://www.freakyjolly.com/android-background-geolocation-service-without-any-kill/

hafiz013 commented 5 years ago

Since android Oreo above limit background service location, None of those above works. i try sample by google (https://github.com/googlecodelabs/background-location-updates-android-o/blob/af7660cfb91d114f330818e2033ca0175f11e07d/BackgroundLocationUpdates/app/src/main/AndroidManifest.xml) also not working for broadcast and intent service. This is sample code I did

In Main Activity :- private PendingIntent getPendingIntent() { Intent intent = new Intent(getActivity(), LocationUpdatesBroadcastReceiver.class); intent.setAction(LocationUpdatesBroadcastReceiver.ACTION_PROCESS_UPDATES); return PendingIntent.getBroadcast(getActivity(), 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } Running the service/ broadcast: `FusedLocationProviderClient mFusedLocationClient = LocationServices.getFusedLocationProviderClient(getActivity()); mLocationRequest = new LocationRequest();

    mLocationRequest.setInterval(UPDATE_INTERVAL);

    // 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);
    mLocationRequest.setPriority(LocationRequest.PRIORITY_HIGH_ACCURACY);

mFusedLocationClient.requestLocationUpdates(mLocationRequest, getPendingIntent());`

My BroadCast:

LocationUpdatesBroadcastReceiver extends BroadcastReceiver{ public static final String ACTION_PROCESS_UPDATES = "<idpackage>.action" + ".PROCESS_UPDATES"; private Location lstart = null; private Location lEnd = null; private Location mCurrentLocation; private double speed = 0.0, distance_accumalative = 0.0; private String cumulativeDistance; private ClientInterface client; private PersonModel personModel; private SessionManager sessionManager;

@Override
public void onReceive(Context context, Intent intent) {
    if (intent != null) {
        sessionManager = new SessionManager(context);
        personModel = sessionManager.getDriver();

        final String action = intent.getAction();

        if (ACTION_PROCESS_UPDATES.equals(action)) {
            LocationResult result = LocationResult.extractResult(intent);
            if (result != null) {
                List<Location> locations = result.getLocations();

                for (Location b: result.getLocations()){
                    mCurrentLocation = b;

                    if (lstart == null) {
                        lstart = mCurrentLocation;
                        lEnd = mCurrentLocation;
                    } else {
                        lEnd = mCurrentLocation;
                    }

                    speed = ((double)b.getSpeed() * 18 /5);
                }

                //if speed more than zero update location
                updateLocation();

                LocationResultHelper locationResultHelper = new LocationResultHelper(
                        context, locations);
                // Save the location data to SharedPreferences.
                locationResultHelper.saveResults();
                // Show notification with the location data.
                locationResultHelper.showNotification();
                //Log.i(TAG, LocationResultHelper.getSavedLocationResult(context));
            }
        }
    }
}`

Anyone know how to run service location in background for oreo above.

betorobson commented 5 years ago

@hafiz013 you cannot do this in background. You need create a intent and run as a persistent notification. Move you code into a intent with persistent notification and it will works like a charm

hafiz013 commented 5 years ago

@hafiz013 you cannot do this in background. You need create a intent and run as a persistent notification. Move you code into a intent with persistent notification and it will works like a charm

Yeah I did as move to getpending notification but somewhore it only work on 10 minute then it gone and not call anymore. For your information, when i check result android 7 will show up notification above while doing in getpending intent meanwhile, android 8 and 9 does not show notification above.