4

In Android I have an application with different activities and a service. The activities are for direct user interaction, in which the user can change data, update data, entry data, whatever.

The application also has a (background) service running, using the data the user has entered (stock data). The service regulary checks the stock prices (e.g. every 12 hours) and gives the user a notification, if some conditions match.

However, as an user I want to be able to change settings of this service. I start the application, and can enter an update interval in one of the application's activity which is stored in a SharedPreference. I am able to change the value, for example, to have the service check the stock data every 3 hours instead.

Do I then have to 'restart' the service? As I understand 'services' in an operating system, the service runs until stopped by some event. But if the service is not stopped, the service does not 'notice' that the update interval has changed! So, in my activity from my main application I need some way to 'restart' the service, so it checks the stock prices in 3 hours, and not in 12 hours!

Any idea how to accomplish this? A google search did not really help. Maybe I looked for the wrong keywords...?

Alex
  • 41,580
  • 88
  • 260
  • 469
  • Does the service run in the same process as your application? You can register a shared preference changed listener in the service and act accordingly when the preference is changed without restarting the service. – George Mulligan Mar 23 '16 at 19:37
  • It is in the same Manifest file. – Alex Mar 23 '16 at 19:38
  • Also, I have absolutely no idea what it means to 'register a shared preference changed listener'. An example code would be great... – Alex Mar 23 '16 at 19:42
  • It will always be in the same manifest file. As long as you aren't using the `android:process` and possibly `android:isolatedProcess` attributes on the service in a way that puts the service into its own process using an `OnSharedPreferenceChangeListener` should work for you. – George Mulligan Mar 23 '16 at 19:43
  • And how to use it? Shall I ask a new question? Or can you provide some example code...? – Alex Mar 23 '16 at 19:44
  • 1
    It's really straightforward to use. You can find an example [here](http://stackoverflow.com/questions/4997907/how-to-detect-if-changes-were-made-in-the-preferences). – George Mulligan Mar 23 '16 at 19:45
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/107176/discussion-between-alex-and-george-mulligan). – Alex Mar 23 '16 at 19:45

4 Answers4

1

The standard way to make activities communicate with a service is through an interface using AIDL. In AIDL you define calls to the service, and callbacks from the service. With that, the service behaves like just another class that you can call methods of. There's documentation here.

Emzor
  • 1,380
  • 17
  • 28
Christine
  • 5,617
  • 4
  • 38
  • 61
  • Uhmm what? Please read the first sentence inside link you provided (especially the ending). Now read again what OP said and if possible explain how two are connected. – Wukash Mar 23 '16 at 19:58
  • How is that unclear? Alex wants to change the behavior of the service. Aidl provides a way to do that in a straightforward way. You can make it call stopself, then restart it, if that's what you want. – Christine Mar 23 '16 at 20:09
  • The problem is that there is absolutely no need to use AIDL here since OP didn't say service runs in separate process. OP just thinks you need to 'restart' the service instead of implementing some logic in service like sharedpref change listener. – Wukash Mar 23 '16 at 20:11
  • @Wukash you can still use AIDL to work in the same process as well, it is really no desgined to work with different processes only – pskink Mar 23 '16 at 20:17
  • I might be from another planet but in my opinion 'The standard way to make activities communicate with a service is through binding' , not AIDL – Wukash Mar 23 '16 at 20:20
  • @Wukash and AIDL is not binding? how come? read http://developer.android.com/guide/components/bound-services.html – pskink Mar 23 '16 at 20:21
  • Please stop commenting, I'll just paste what documentation says: Note: Using AIDL is necessary only if you allow clients from different applications to access your service for IPC and want to handle multithreading in your service. If you do not need to perform concurrent IPC across different applications, you should create your interface by implementing a Binder. – Wukash Mar 23 '16 at 20:22
  • @Wukash then see generated code and you will know how it really works – pskink Mar 23 '16 at 20:25
  • `bindService(intent, serviceConnection, BIND_ALLOW_OOM_MANAGEMENT);` and you get service reference in onServiceConnected callback. No need for any aidl files whatsoever. – Wukash Mar 23 '16 at 20:29
  • @Wukash something.aidl -> AIDL compiler -> something.java, then see something.java, when no crossing processes the local `Binder` is used with direct method calls (just like in "bound local service" pattern) – pskink Mar 23 '16 at 20:48
0

A simple solution to this since your service runs in the same process as your application would be to register an OnSharedPreferenceChangeListener in your service's onCreate method to the same SharedPreferences that the activity is updating.

Then every time a preference changes onSharedPreferenceChanged(...) will be called in your service. You can check when the update interval preference in particular changes and update your service accordingly without ever having to restart it.

George Mulligan
  • 11,813
  • 6
  • 37
  • 50
0

You can send to your service data and actions with Intents. Simply inside your Service class create a BroadcastReceiver and register it with the service life cycle. Now you can update the values from the service. An example:

Your Service

public class CustomService extends Service {

    private BroadcastReceiver mReceiver;

    @Override
    public void onCreate() {
        mReceiver = new BroadcastReceiver() {
            @Override
            public void onReceive(Context context, Intent intent) {
              // get the action from the intent
            }
        }
        registerReceiver(wifiChangedState, new IntentFilter("CUSTOM");
    }

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        return START_NOT_STICKY;
    }

    @Override
    public void onDestroy() {
        unregisterReceiver(mReceiver);
    }

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

Now you can create your custom Intent to send actions from an Activity that the BroadcastReceiver can receive and apply.

newhouse
  • 946
  • 8
  • 12
0

You can create a Service with a custom Binder

    public class CustomService extends Service {

        public IBinder onBind(Intent intent) {
            return new CustomBinder(this);
        }

        public void doSomething(){
            //Do somthing with service
        }

    }

    public class CustomBinder extends Binder {

        private CustomService mCustomService;

        public MyBinder(CustomService service) {
            mCustomService = service;
        }

        public CustomService getCustomService() {
            return mCustomService;
        }
    }

At this point you can bind to CustomService through ServiceConnection (I cannot link ServiceConnection because of my reputation...) implements all functions you need in service.

    Intent customServiceIntent = new Intent(context, CustomService.class);
    context.bindService(
            customServiceIntent, 
            mServiceConnection, 
            Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
    );

    private ServiceConnection mServiceConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName name, IBinder service) {
            CustomBinder binder = (CustomBinder) service;
            binder.getCustomService().doSomething();
        }
        public void onServiceDisconnected(ComponentName name) {
            /* Do something when service disconnect */
        }
    };

This method is not applicable with services over processes. (multiples applications) take a look at AIDL files to achieve that.

To make sure "work" was execute in time you can use AlarmManager to start your service.

    /* define when intent will be send */
    long wakeUpTimeInMillis = Calendar.getInstance().getTimeInMillis() + 1000 * 30 ; // now + 30second 

    AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
    Intent intent = new Intent(context, CustomService.class);
    PendingIntent pending = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_NO_CREATE);
    alarmManager.set(AlarmManager.RTC_WAKEUP, wakeUpTimeInMillis, pending);

You can add some additionnal parametters to intent and catch it by overriding onStartCommand(Intent intent, int flags, int startId) in CustomService

Lionel Briand
  • 1,732
  • 2
  • 13
  • 21