Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cordova android background plugin killed after 5 min

I'm developing a Cordova Android/iOS app with some location tracking. To continuously update the location, I use a background geolocation plugin (https://github.com/mauron85/cordova-plugin-background-geolocation).

This plugin creates a service which listen to the system LocationManager. When the system gets a location update, the plugin fires an event chain/some calculations until it reaches the final JavaScript callback I use in my cordova App.

It works fine on iOS and never gets killed, but I'm having trouble with Android, when you put the phone to sleep (lock the screen) while not having the app in the foreground (let's say you switched to another app before locking). If you move (and thus get locations updates) the process keeps running. But if you stand still for more than 5 min, the background location service stops and no more locations are passed to my JS callback. After this, if you move again the service stays stopped, until you unlock your screen and bring the app to the foreground.

It's annoying because my app is entirely based on this stop and go pattern, needs to be running under a locked screen (phone in the pocket), and my server checks the timestamp of the locations to filter who is considered online and active with a fresh location and who's not. So I basically need 2 things : be able to restart the location tracking when the user moves again, and keep sending a few locations to my server if the user is idle.

From my research, I ended up creating a thread that sleeps for 60s and loops back, calling each minute the handle position function of my background service (which sends the position to the front JS). Unfortunately I'm new to Cordova Plugin and didn't manage to get it work. I'm having a null pointer exception while trying to access to the systemService of the app context :

E/AndroidRuntime( 4627): java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object android.content.Context.getSystemService(java.lang.String)' on a null object reference
E/AndroidRuntime( 4627):        at android.content.ContextWrapper.getSystemService(ContextWrapper.java:583)
E/AndroidRuntime( 4627):        at com.tenforwardconsulting.cordova.bgloc.ForcedUpdateLocation.handleLocationFromOutside(ForcedUpdateLocation.java:12)
E/AndroidRuntime( 4627):        at com.tenforwardconsulting.cordova.bgloc.BackgroundGeolocationPlugin$3.run(BackgroundGeolocationPlugin.java:266)
E/AndroidRuntime( 4627):        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
E/AndroidRuntime( 4627):        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
E/AndroidRuntime( 4627):        at java.lang.Thread.run(Thread.java:818)

The start function of the plugin :

public ComponentName startBackgroundService () {
    if (isEnabled) { return null; }
    Class serviceProviderClass = null;

    try {
        serviceProviderClass = ServiceProvider.getClass(config.getServiceProvider());
    } catch (ClassNotFoundException e) {
        callbackContext.error("Configuration error: provider not found");
        return null;
    }

    Activity activity = this.cordova.getActivity();
    Log.d(TAG, "Starting bg service");

    registerActionReceiver();
    locationServiceIntent = new Intent(activity, serviceProviderClass);
    locationServiceIntent.addFlags(Intent.FLAG_FROM_BACKGROUND);
    // locationServiceIntent.putExtra("config", config.toParcel().marshall());
    locationServiceIntent.putExtra("config", config);
    isEnabled = true;


    //Start a thread to send a position every 60s
    final ForcedUpdateLocation ful = new ForcedUpdateLocation();

    cordova.getThreadPool().execute(new Runnable(){
        public void run(){
            try {
                while(isEnabled){
                    Thread.sleep(3000);
                    Log.d(TAG, "Force location sent");

                    ful.handleLocationFromOutside();

                }
            } catch (InterruptedException e){
                Log.d(TAG, "EXCEPTION ! Force location sent", e);
            }
        }
    });


    return activity.startService(locationServiceIntent);
}

My stub "ForcedUpdateLocation" service that extends the LocationService of the plugin (to call for handleLocation) :

package com.tenforwardconsulting.cordova.bgloc;

import android.location.Location;
import android.content.Context;
import android.location.LocationManager;


public class ForcedUpdateLocation extends com.tenforwardconsulting.cordova.bgloc.AbstractLocationService {

    public void handleLocationFromOutside (){

        LocationManager locationMangerWakeThread = (LocationManager) getSystemService(Context.LOCATION_SERVICE);

        Location location = locationMangerWakeThread.getLastKnownLocation(locationMangerWakeThread.GPS_PROVIDER);

        handleLocation(location);
    }

    @Override
    protected void cleanUp(){
        //Stub
    };

    @Override
    protected void startRecording(){
        //Duh
    };

    @Override
    protected void stopRecording(){
        //Wouf ?
    };
}

Besides the fact I'm stuck with this, I find this solution really ugly...

So what's the best way to keep this service running ? Is there a best practice to keep long running idle process in the background ?

Many thanks

like image 803
Clem Avatar asked Nov 08 '22 10:11

Clem


1 Answers

try using 'stopOnTerminate' and 'startOnBoot' options of this plugin. which will help you keep getting updates in the background even the app is killed by OS.

also keep in mind that your callbk function will not always be executed because if your app is killed by system, this will kill your activity and your callbk. So try using url option of the plugin to keep posting your location to the server even in the background.

like image 82
max_code Avatar answered Nov 14 '22 21:11

max_code