How to return calculations performed in Service to Activity?

Asked

Viewed 747 times

1

I would like to send the result of a calculation back to Activity, I was trying to use a Binder as recommended, but I cannot recover this value. It would be possible to make Activity know that the service finished the calculation and then get the value?

My Activity:

public class MainActivity extends ActionBarActivity  implements ServiceConnection{

private TextView numero1;
private TextView resultado;
private TextView numero2;
private TextView resultadosoma;
private EditText numero1informado;
private EditText numero2informado;
private Valores valores;
final ServiceConnection conexao = this;
final Intent it = new Intent("Service");

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

    Button biniciar = (Button) findViewById(R.id.button1);
    Button bfechar = (Button) findViewById(R.id.button2);
    numero1 = (TextView) findViewById(R.id.numero1);
    numero2 = (TextView) findViewById(R.id.numero2);
    resultado = (TextView) findViewById(R.id.resultado);
    resultadosoma = (TextView) findViewById(R.id.resultadosoma);
    numero1informado = (EditText) findViewById(R.id.numero1informado);
    numero2informado = (EditText) findViewById(R.id.numero2informado);





    biniciar.setOnClickListener(new Button.OnClickListener(){
        public void onClick(View v){
            it.putExtra("numero1", numero1informado.getText().toString());
            it.putExtra("numero2", numero2informado.getText().toString());

            //startService(it);
            Class<Service> classeServico = Service.class;
            bindService(new Intent(MainActivity.this, classeServico), conexao, Context.BIND_AUTO_CREATE);
            //classeServico.get
            startService(it);


        }
    });



    bfechar.setOnClickListener(new Button.OnClickListener(){
        public void onClick(View v){
            //stopService(it);
            unbindService(conexao);
            stopService(it);
        }
    });
}


@Override
protected void onDestroy() {

    unbindService(conexao);
    super.onDestroy();
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
    // TODO Auto-generated method stub
    LocalBinder binder = (LocalBinder) service;

    valores = binder.getValores();

}

@Override
public void onServiceDisconnected(ComponentName name) {
    // TODO Auto-generated method stub
    valores = null;
}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    // Inflate the menu; this adds items to the action bar if it is present.
    getMenuInflater().inflate(R.menu.main, menu);
    return true;
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}

public TextView getNumero1() {
    return numero1;
}

public void setNumero1(TextView numero1) {
    this.numero1 = numero1;
}

public TextView getResultado() {
    return resultado;
}

public void setResultado(TextView resultado) {
    this.resultado = resultado;
}

public TextView getNumero2() {
    return numero2;
}

public void setNumero2(TextView numero2) {
    this.numero2 = numero2;
}

public TextView getResultadosoma() {
    return resultadosoma;
}

public void setResultadosoma(TextView resultadosoma) {
    this.resultadosoma = resultadosoma;
}

public EditText getNumero1informado() {
    return numero1informado;
}

public void setNumero1informado(EditText numero1informado) {
    this.numero1informado = numero1informado;
}

public EditText getNumero2informado() {
    return numero2informado;
}

public void setNumero2informado(EditText numero2informado) {
    this.numero2informado = numero2informado;
}

public Valores getValores() {
    return valores;
}

public void setValores(Valores valores) {
    this.valores = valores;
}

}

Service:

public class Service extends android.app.Service implements Runnable, Valores{

private static final int MAX = 10;
protected int cont = 0;
private boolean ativo;
private double numero1;
private double numero2;
private double resultado;

private final IBinder conexao = new LocalBinder();

public class LocalBinder extends Binder{

    public Valores getValores(){
        return Service.this;
    }
}


public IBinder onBind(Intent intent){

    return conexao;
}



@Override
public void onCreate() {
    // TODO Auto-generated method stub
    Log.i("and","onCreate()");

    super.onCreate();
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
    // TODO Auto-generated method stub
    Log.i("and","onStartCommand()");
    //cont = 0;
    //ativo = true;
    //new Thread(this,"Exemplo Serviço: "+startId).start();

    String valor1 = intent.getStringExtra("numero1");
    String valor2 = intent.getStringExtra("numero2");
    Double numero1 = intent.getDoubleExtra("numero1", 0);
    resultado = Double.valueOf(valor1) + Double.valueOf(valor2);


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


public void run(){
    while(ativo && cont < MAX){
        fazAlgo();
        Log.i("and", "Exemplo serviço executando: "+cont);
        cont++;
    }
}

public void fazAlgo(){
    try{
        Thread.sleep(1000);
    }catch(Exception e){
        e.printStackTrace();
    }
}

@Override
public void onDestroy() {
    // TODO Auto-generated method stub
    Log.i("and","onDestroy()");
    ativo = false;


    super.onDestroy();
}

@Override
public double numero1() {
    // TODO Auto-generated method stub
    return numero1;
}

@Override
public double numero2() {
    // TODO Auto-generated method stub
    return numero2;
}

@Override
public double resultado() {
    // TODO Auto-generated method stub
    return resultado;
}

}

  • It seems to be a relatively simple calculation to use in a service. Have you tried the calculation when passing to the other Activity or is it mandatory to use the service?

  • I was just making an example, to make a more complex calculation later. If it is a calculation that takes a long time to complete, putting this to be processed in the service the operating system will not close it, or it will have the same behavior as if to do it in Activity?

  • Service is for longer operations that run in the background, so use is correct. Activity can receive the result through Binder or through a broadcast receiver (the service can broadcast the result). If you want to use Binder, specify the problem you are having better. Saying "I’m not getting it" is not very clear.

  • When the service finishes a calculation, as I warn Activity that it can catch the result?

  • In that case I would recommend a BroadcastReceiver to notify the Activity of the end of the asynchronous calculation, but record a Listener in the Service is also another solution. It would be a simpler way than using a Binder. I can put together an answer soon.

2 answers

1

The problem you are having is because you are recovering the values before the onStartCommand have finished, so the values were not calculated because the calculation is asynchronous.

My suggestion is to use a BroadcastReceiver location, without exposing private data out of your application.

Remembering that you need to declare the dependency in the support library v4, if you can’t, use Broadcastmanager, but then it will be global.

My Service example using LocalBroadcastManager to notify the termination of an asynchronous job:

public class TheService extends Service {

    public static final String BROADCAST_FILTER = "the_service_broadcast";
    public static final String PARAMETER_EXTRA_KEY = "the_service_key";

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

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        int result = super.onStartCommand(intent, flags, startId);

        new Thread() {
            @Override
            public void run() {
                try {
                    // Doing Heavy Work...
                    Thread.sleep(TimeUnit.SECONDS.toMillis(5));
                    broadcast(true);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    broadcast(false);
                }
            }
        }.start();

        return result;
    }

    void broadcast(boolean success) {
        LocalBroadcastManager.getInstance(this).sendBroadcast(new Intent(BROADCAST_FILTER).putExtra(PARAMETER_EXTRA_KEY, success));
    }
}

Example of Activity that records a BroadcastReceiver for the broadcast of Service:

public class MainActivity extends Activity {

    // BroadcastReceiver responsavel por escutar o broadcast do Service
    BroadcastReceiver mReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "Broadcast from TheService(" + intent.getBooleanExtra(TheService.PARAMETER_EXTRA_KEY, false) + ")", Toast.LENGTH_LONG).show();
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Todo seu codigo de inicializacao...
        startTheService();
    }

    @Override
    public void onResume() {
        super.onResume();

        // Registra o BroadcastReceiver para escutar por broadcast's do Service.
        LocalBroadcastManager.getInstance(this).registerReceiver(mReceiver, new IntentFilter(TheService.BROADCAST_FILTER));
    }

    @Override
    public void onPause() {
        super.onPause();

        // Remove o Receiver quando a Activity for pausada.
        LocalBroadcastManager.getInstance(this).unregisterReceiver(mReceiver);
    }

    private void startTheService() {
        // Inicia o servico
        startService(new Intent(this, TheService.class));
    }
}

I recommend using the onPause to remove (avoid Leaks of memory) and the onResume to register. Case your Activity be placed in background the BroadcastReceiver will be removed and in case the Activity get back to foreground the BroadcastReceiver will be re-registered.

If you need the result even without the Activity be visible (foreground), then recommend removing only on onStop or onDestroy. It would be valid to save the result, but it is not recommended to change the View's in such cases.

1


Your Activity needs to wait for the result to be ready, and the service needs to notify Activity when it finishes the calculation. To do this, he needs to keep a reference from Activity, so he can know who he will warn. The way to do this is by registering Activity as a service manager (default Observer).

First, create the following interface:

public interface ReceptorDeResultado {
    public void receberResultado(double resultado);
}

Then create a mListener attribute in the service that implements this interface (for information only, the letter m in the name is to differentiate from local variables. mListener is a member of the Service class - in this case, an instance variable):

private ReceptorDeResultado mListener = null;

Then modify the Localbinder class to support logging and removing the Listener:

public class LocalBinder extends Binder{

    public Valores getService(){
        return Service.this;
    }

    public void registerListener(ReceptorDeResultado listener) {
        Service.this.mListener = listener;
    }

    public void unregisterListener() {
        Service.this.mListener = null;
    }
}

Now, have Mainactivity implement the Receptorderesult interface:

public class MainActivity extends ActionBarActivity
        implements ServiceConnection, ReceptorDeResultado {

Since the Mainactivity class now implements the result Receptorde interface, it is required to have a receive method():

public void receberResultado() {
    // Neste método você faz uso do resultado, por exemplo exibindo-o
    // na tela. O código para fazer isso eu deixo por sua conta.
}

Now let us put into use the preparations we have made. But before that, in the Mainactivity class, it is necessary to make the variable Binder an instance variable and not a local variable. So declare it along with the other instance variables:

private LocalBinder mBinder = null;

and correct its use in the onServiceConnected method():

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
    // TODO Auto-generated method stub
    mBinder = (LocalBinder) service;
}

Now come on, let’s go. Respecting Activity’s life cycle and avoiding memory Leaks, we will register Activity as a service manager and cancel this record when Activity is destroyed. So, in the same onServiceConnected() method that we just touched:

@Override
public void onServiceConnected(ComponentName name, IBinder service) {
    LocalBinder binder = (LocalBinder) service;
    binder.registerListener(MainActivity.this);
}

Removing the Systener in the onPause method (create this method in the Mainactivity class):

@Override
public void onPause() {
    super.onPause();
    if (binder != null) {
        binder.unregisterListener();
    }
}

Registering the system again in the onResume method, in case the Activity has been destroyed and recreated for example in the case of screen rotation (create the onResume() method in the Mainactivity class):

@Override
public void onResume() {
    super.onResume();
    if (binder != null) {
        binder.registerListener(this);
    }
}

Ready, the service now has a System (or Observer) to warn you when the result is ready. Let’s do this in the Service class:

resultado = Double.valueOf(valor1) + Double.valueOf(valor2);
if (mListener != null) {
    mListener.receberResultado(resultado);
}

Voilà! If all went well (I did not test the code), the service will inform the calculated result for Activity (if she is observing the service at that time).

Now a caveat for the future. For longer calculations, you will need your Service to run this calculation on a separate thread (today it runs on the main thread and only allows quick calculations, which do not cause the main thread to be blocked). The easiest way to allow this is to make your service extend the Intentservice class and not android.app.Service. This is because Intentservice executes commands in a separate thread by default. Therefore, when implementing your most time-consuming calculations, study Intentservice.

  • It worked like this. Thank you so much for sharing your knowledge.

  • If the answer met your need, do not forget to accept it.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.