5

I need to implement such a procedure:

  1. Start a background service
  2. Update the service with parameters (from UI - user input)
  3. After activity ended the service should keep on running and preform requests to HTTP server every minute. in this stage i still need the parameters I updated in the second stage - I send them to the server.
  4. The service should store the server last response and compere each with the last. if there is a change, notify the user.
  5. Finally, when the activity starts again, the service should update UI with latest the server response.

What I tried: BroadcastReciver - The problem is after onRecive ended all the arguments which aren't declared as final will wipe out, as well as I didn't found a way to update the Intent being sent automatically every minute.

Service - Using startService() - The problem is when the activity ended the service like stops and starts , flushing all it's arguments. and once again I didn't figured out how to update the arguments after the service is already started.

So how to handle such a situation?

Thanks.

SMD
  • 404
  • 1
  • 4
  • 9
  • this might help - http://stackoverflow.com/questions/4300291/example-communication-between-activity-and-service-using-messaging – coderplus Jul 07 '13 at 16:09

2 Answers2

3

It sounds like what you need to do is to be able to "bind" to your service. What I have posted below is a simple template of how to do that. For your purposes you will need to store variables in your Service class and create getters so that when you re-launch your activity you can get the most up to date variables. Also - please note that I start and stop the Service example below in onResume and onPause. You will no doubt want to do this differently.

//Activity

//Bind to Service Example

public class ExampleActivity extends Activity implements OnClickListener {

// UI
private Button binderButton;

// service
private MyService myService;
private Intent serviceIntent;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.your_layout);

    // binder button
    binderButton = (Button) findViewById(R.id.button1);
    binderButton.setOnClickListener(this);
    binderButton.setText("start");

    serviceIntent = new Intent(this, MyService.class);
}

private ServiceConnection serviceConnection = new ServiceConnection() {

    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        myService = ((MyService.MyBinder) service).getService();
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        myService = null;
    }
};

@Override
protected void onResume() {
    super.onResume();
    // start the service
    startService(serviceIntent);
    // bind to the service
    bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}

@Override
public void onClick(View v) {
    switch (v.getId()) {
    case R.id.button1:
        // call method within the service
        myService.doServiceStuff();
        break;
    }
}

@Override
protected void onPause() {
    super.onPause();
    stopService(serviceIntent);
    unbindService(serviceConnection);
}
}

//Service

public class MyService extends Service {
private final IBinder binder = new MyBinder();

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

public void doServiceStuff() {
    task.execute();
}

// create an inner Binder class
public class MyBinder extends Binder {
    public MyService getService() {
        return MyService.this;
    }
}

AsyncTask<Void, Void, Void> task = new AsyncTask<Void, Void, Void>() {

    @Override
    protected Void doInBackground(Void... params) {
        Log.d("yourTag", "long running service task");
        return null;
    }       
};
}
Swifty McSwifterton
  • 2,637
  • 1
  • 30
  • 37
  • Thanks but unfortunately the service timer task still stops after unBinding. BTW, maybe the problem is with the timer task: `code` private TimerTask updateTask = new TimerTask() { @Override public void run() { test++; Log.d("OREN", "Timer task doing work" + test ); } }; public void runOnTimer(double rad){ radius = rad; timer = new Timer("UpdateTimer"); timer.schedule(updateTask, 1000L, 10 * 1000L); } `code` – SMD Jul 07 '13 at 17:20
  • Try using a repeating AlarmManager instead. It will register with the OS via pending intents so you will be assured of it not stopping unless you specifically tell it to. – Swifty McSwifterton Jul 08 '13 at 02:56
2

Thanks javaJoe, although your answer didn't solved my problem it gave me some a good ideas.

What I did:

  1. in the Activity onCreate, check if my service is running, if so bind it else, create new one and bind it.

  2. Transferring arguments between the Service and the Activity using setters and getters.

  3. in the Activity onDestroy (the problem was that the service calls self Destory) the Activity sends the final arguments through Intent to a Broadcastreciver. The Broadcastreciver than starts the Service again, initiating it with the correct arguments.

I don't know if this architecture is ideal, i'd like to get some feedback.

Here is the code:

Activity:

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

    //Set Service Intent
    serviceIntent = new Intent(this, UpdateService.class);
    if (isMyServiceRunning()) {
        //Bind to the service
        bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    }else{
        updateService=new UpdateService();
        //Start the service
        startService(serviceIntent);
        //Bind to the service
        bindService(serviceIntent, serviceConnection, Context.BIND_AUTO_CREATE);
    }
}

private boolean isMyServiceRunning() {
    ActivityManager manager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
    for (RunningServiceInfo service : manager.getRunningServices(Integer.MAX_VALUE)) {
        if (UpdateService.class.getName().equals(service.service.getClassName())) {
            return true;
        }
    }
    return false;
}

private ServiceConnection serviceConnection = new ServiceConnection() {
    @Override
    public void onServiceConnected(ComponentName name, IBinder service) {
        updateService = ((UpdateService.MyBinder) service).getService();
        //Set Initial Args
        updateService.setParams(int arg0);
    }

    @Override
    public void onServiceDisconnected(ComponentName name) {
        updateService = null;
    }
};

@Override
protected void onDestroy() {
    //UnBind from service
    unbindService(serviceConnection);
    //Stop Service
    stopService(serviceIntent);
    //Prepare intent to broadcast reciver
    Intent intent = new Intent(MainActivity.this,ServiceRunnerBCR.class);
    intent.setAction(ServiceRunnerBCR.ACTION_SET_UpdateService);
    intent.putExtra(ServiceRunnerBCR.keyVal_arg0, arg0);
    intent.putExtra(ServiceRunnerBCR.keyVal_arg1, arg1);
    //Send broadcast to start UpdateService after the activity ended
    sendBroadcast(intent);

    super.onStop();
}

Broadcastreciver:

public class ServiceRunnerBCR extends BroadcastReceiver {


    public static final String ACTION_SET_UpdateService = "ACTION_ALARM";

    public static final String keyVal_arg0="ARG0";
    public static final String keyVal_arg1="ARG1";


    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(ACTION_SET_UpdateService)){   
             updateIntent(context, intent.getDoubleExtra(keyVal_arg0, 0.02), intent.getStringExtra(keyVal_arg1));
        }
    }

    private void updateIntent(Context context, double arg0, String arg1){
        Intent intent = new Intent(context,UpdateService.class);
        intent.setAction(ACTION_SET_UpdateService);
        intent.putExtra(keyVal_arg0, arg0);
        intent.putExtra(keyVal_arg1, arg1);
        synchronized (this){
            try {
                this.wait(6000);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        context.startService(intent);
        Log.d("OREN","ServiceRunner");
    }
}

Service:

public class UpdateService extends Service {

    private final IBinder binder = new MyBinder();
    public static final String keyVal_arg0="ARG0";
        public static final String keyVal_arg1="ARG1";
    private Timer timer;
    private HTTPHandler http = new HTTPHandler();
    private int test=0;
    double arg0=0;
    String arg1= "";

    private TimerTask updateTask = new TimerTask() {
        @Override
        public void run() {
            test++;
            Log.d("OREN", "Timer task doing work " + test + " arg0: " + arg0);
                        //Do some work here
        }
    };


    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        if (intent!=null){
            arg0=intent.getDoubleExtra(keyVal_arg0, 0.002);
                        arg1=intent.getStringExtra(keyVal_arg1);
            timer = new Timer("UpdateTimer");
            timer.schedule(updateTask, 1000L, 10 * 1000L);
            Log.d("OREN", "ServiceStarted" + test);
        }
        return super.onStartCommand(intent, flags, startId);
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d("OREN", "OnBind" + test);
        return binder;
    }

    public void setArg0(double d){
        arg0=d;
    }

    // create an inner Binder class
    public class MyBinder extends Binder {
        public UpdateService getService() {
            return UpdateService.this;
        }
    }

    @Override
    public void onDestroy() {
        Log.d("OREN", "OnDestroy" + test);
        super.onDestroy();
    }

    @Override
    public boolean onUnbind(Intent intent) {
        Log.d("OREN", "OnUnBind" + test);
        return super.onUnbind(intent);
    }
}
Bahadir Tasdemir
  • 10,325
  • 4
  • 49
  • 61
SMD
  • 404
  • 1
  • 4
  • 9
  • Without knowing the specific problem being solved, I can't really say if this is the best way to do it or not. However, I can say a couple things. 1) You are calling super.onStop() from within onDestroy(). That will no doubt eventually create issues. 2), you are calling sleep(6000) on the UI thread. You are also synchronizing on it which is unnecessary. Better to use a handler there if you need some sort of delay. However, that hard coded delay could be an indication that your architecture needs to change. I would suggest trying to avoid that. :) – Swifty McSwifterton Jul 08 '13 at 13:03
  • thanks, i changed the wait(6000) to while (isMyServiceRunning()). If i'll stop the service in the onStop event instead of the onDestroy event it wont cause issues? – SMD Jul 08 '13 at 15:51
  • 1
    I think leaving it in onDestroy is fine. The issue is that you are overriding onDestroy but then calling onStop(). For example, it should be "public void onDestroy() { super.onDestroy(); }". You have "public void onDestroy() { super.onStop(); }". Also, it sounds like you are still blocking your main UI thread with "while { (isMyServiceRunning()) }". – Swifty McSwifterton Jul 08 '13 at 15:58