Apostrofix
Apostrofix

Reputation: 2180

How to keep polling a bound service?

I looked up on the internet, but couldn't find an example covering my scenario. What I am trying to do is:

1) To start and bind to a service as soon as my activity starts (done)

2) The service then binds itself to another service looking for a user input from a connected device, and saves a string a string to a variable (done)

3) I would like to send back this string to the activity, so I can check what it is and based on it to make a network call.

Now number 3) is my challenge. I managed to do it with a Timer that runs for one second and then checks the value written in the service, but somehow this doesn't seem to be the right way and I think that there might be a more mature solution. However, I can't seem to figure it out.

I've taken the code from the documentation and only added the timer. It is just one service in this example that just generates a random number (this will normally be replaced by my second service). This is the code for the service:

public class LocalService extends Service {

    private final IBinder mBinder = new LocalBinder();

    private final Random mGenerator = new Random();

    public class LocalBinder extends Binder {
        LocalService getService() {
            return LocalService.this;
        }
    }

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

    public int getRandomNumber() {
        return mGenerator.nextInt(100);
    }
}

And this is the code in my activity:

public class MainActivity extends AppCompatActivity {

    LocalService mService;
    boolean mBound = false;
    Timer timer;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        timer = new Timer();
    }

    @Override
    protected void onStart() {
        super.onStart();
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, mConnection, Context.BIND_AUTO_CREATE);

        timer.schedule(new MyTimerTask(new Handler(), this), 1000, 1000); // run on every second
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mBound) {
            unbindService(mConnection);
            mBound = false;
        }

        timer.cancel();
        timer.purge();
    }

    private class MyTimerTask extends TimerTask {
        Handler handler;
        MainActivity ref;

        public MyTimerTask(Handler handler, MainActivity ref) {
            super();
            this.handler = handler;
            this.ref = ref;
        }

        @Override
        public void run() {
            handler.post(new Runnable() {
                @Override
                public void run() {
                    if (mBound) {
                        int num = ref.mService.getRandomNumber();
                        // just as an example, raise a toast to see if it works
                        // but otherwise the value will be handled
                        Toast.makeText(ref, "number: " + num, Toast.LENGTH_SHORT).show();
                    }
                }
            });
        }
    }

    private ServiceConnection mConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                                       IBinder service) {
            LocalService.LocalBinder binder = (LocalService.LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mBound = false;
        }
    };
}

My question is: is this a good approach (it works) or is it bad and what's the alternative?

Upvotes: 2

Views: 266

Answers (2)

Niels Masdorp
Niels Masdorp

Reputation: 2428

You can use LocalBroadcastManager to send broadcasts from your Service to your Activity. For example, in your Service declare:

public static final String BROADCAST_INTENT = "broadcast_intent";
public static final String BROADCAST_VALUE = "broadcast_value";

private LocalBroadcastManager broadcastManager;

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

    broadcastManager = LocalBroadcastManager.getInstance(this);
}

Now whenever you want to send a String to your Activity you can do so like this:

private void sendBroadcast(String value) {

    Intent intent = new Intent(BROADCAST_INTENT);
    intent.putExtra(BROADCAST_VALUE, value);
    broadcastManager.sendBroadcast(intent);
}

In your Activity declare a BroadcastReceiver:

private BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {

    @Override
    public void onReceive(Context context, Intent intent) {

        handleIntent(intent);
    }
};

Register the receiver when you bind to your Service:

IntentFilter broadcastIntentFilter = new IntentFilter();
broadcastIntentFilter.addAction(StreamService.BROADCAST_INTENT);
LocalBroadcastManager.getInstance(context).registerReceiver((broadcastReceiver), broadcastIntentFilter);

And unregister where you unbind from your Service:

LocalBroadcastManager.getInstance(context).unregisterReceiver(broadcastReceiver);

Now when your service sends the broadcast you can handle it in your Activity:

private void handleIntent(Intent intent) {

    if (intent.getAction().equals(StreamService.BROADCAST_INTENT)) {
        String value = intent.getStringExtra(StreamService.BROADCAST_VALUE, "default");
    }
}

Upvotes: 3

CommonsWare
CommonsWare

Reputation: 1007296

I would like to send back this string to the activity, so I can check what it is and based on it to make a network call.

Use LocalBroadcastManager, greenrobot's EventBus, Square's Otto, or some other in-process event bus implementation. Raise an event when you have changed data. Have the activity register with the bus to find out about the event. Have the activity use the changed data when the change occurs.

is this a good approach

No.

Upvotes: 2

Related Questions