Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best approach to execute service in Android

I have a service that have an variable life time. It may execute from 5 minutes to 2 hours (for example). So I'm looking for the best approach to do that, and my service must achieve the following features:

  • Send (to my server) lat-long every 5 seconds and some extra information (string's, boolean's and int's)

I have tried a "normal" service and tried to do something like this to achieve this:

public class MyFiveSecondsService extends Service {
    private Handler handler;
    Runnable r = new Runnable() {
        @Override
        public void run() {
            //here send my new data
        }
    };

    public void onCreate(){
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if(handler == null){
            handler = new Handler();
        }

        handler.post(r);

        return super.onStartCommand(intent, flags, startId);
    }
}

Actually that code works, but I got some performance problems with that approach, so I tried to do something like this:

public class SendUniquePositionIntentService extends IntentService {

    public SendUniquePositionIntentService() {
        super("co.bomboapp.Service.IntentService.SendUniquePositionIntentService");
    }

    @Override
    public void onCreate() {
        super.onCreate();
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        //do the logic here
    }
}

public class MyFiveSecondsService extends Service {
    private Handler handler;
    Runnable r = new Runnable() {
        @Override
        public void run() {
            //call my SendUniquePositionIntentService here
        }
    };

    public void onCreate(){
        super.onCreate();
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if(handler == null){
            handler = new Handler();
        }

        handler.post(r);

        return super.onStartCommand(intent, flags, startId);
    }
}

And that approach haven't worked, when I had closed the app any service kept running. So before start any other attempt to achieve this, I want some direction, what's the best approach to do this "infinite loop service" and keep the performance?

I'm using Android 4.1 as min API, and targeting 5.0 API. My test device is a Nexus 5 running Android 6. Right now I'm using parse.com as database.

like image 925
guisantogui Avatar asked Sep 26 '22 07:09

guisantogui


1 Answers

"I have tried a "normal" service...but I got some performance problems"

By default, a service runs on the application's main thread, so when you create a handler with

public int onStartCommand(Intent intent, int flags, int startId) {
    if(handler == null){
        handler = new Handler();
    }
    ...
}

the handler is associated with the main thread's Looper and all messages and runnables are delivered and later executed on the thread. That's the reason of the "performance problems". From the documentation:

Remember that if you do use a service, it still runs in your application's main thread by default...

Regarding the second approach and the part

"...when I had closed the app any service kept running"

you haven't mentioned how exactly you "close" the app, but what I can see is

public int onStartCommand(Intent intent, int flags, int startId) {
    ...
    return super.onStartCommand(intent, flags, startId);
}

which means that if the system kills the service it, by default, will be recreated. So if "closing" your app means killing it, the following chain of actions takes place:

  1. The system recreates MyFiveSecondsService,
  2. onStartCommand() is called and the handler posts the runnable
  3. within the run() method SendUniquePositionIntentService is started

From the documentation of onStartCommand():

the default implementation calls onStart(Intent, int) and returns either START_STICKY or START_STICKY_COMPATIBILITY.

Note that starting a service from another one (like starting SendUniquePositionIntentService from MyFiveSecondsService in your case) is redundant unless you intended to.

The final part of your question is confusing to me. On one hand it doesn't work for you because "...any service kept running" but, on the other hand, you'd like "do this "infinite loop service""...?

If you only need to send such information as "strings, booleans and ints" to a server (without any feedback to the component that started the service), I suppose it's simply enough for you to use IntentService. This is a "out-of-box" framework that does its work on a background thread (letting you avoid freezing the main thread) and stops itself once it's done. As an example you can use the documentation on IntentService - it's well written.

Also note that the behaviour of a service after killing it by the system depends on the flag returned by onStartCommand(). E.g. use START_NOT_STICKY to not recreate the service after killing the app or START_REDELIVER_INTENT to recreate it with the last Intent redelivered.

like image 122
Onik Avatar answered Sep 30 '22 07:09

Onik