Update service running on background

2020-06-21 03:30发布

问题:

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.

回答1:

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;
    }       
};
}


回答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);
    }
}