How to avoid restarting the screen elements when turning the phone?

Asked

Viewed 818 times

3

I have an Activity with only one timer basically, and when turning the phone, the chronometer is reset.

Is there an event that causes Activity to restart while doing so? I would like to prevent this behavior.

2 answers

5


The problem

When you change the orientation of the device, Android destroys and recreates the Activity. What’s going on is that you’re probably starting your stopwatch on onCreate. Like the onCreate is being called again, the status your timer is being restarted.

What you should do will solve not only the rotation problem but the problem of when your Activity is destroyed in the background because the system needed memory (you can simulate this situation by enabling "Do not keep activities" in the device developer settings)

I imagine your code goes something like this:

public class CronometroActivity extends Activity  {
    // ...
    // campos da sua classe
    // ...
    private Cronometro meuCronometro;
    // ...        
    // mais campos da sua classe
    // ...

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // ...
        // inicialização dos elementos da tela
        // ...

        this.meuCronometro = new Cronometro();
        atualizarCronometroDaTela();

    }

    // demais métodos

}    

Maybe you don’t have a Chronometer class, but other simpler variables to compose the chronometer state, but the principle is the same.

Solution

You must override the method onSaveInstanceState of Activity, which is called when the Activity is destroyed by the system. The system sends to this method a Bundle, called here from estadoDeSaida, within which you will put the data you want to persist between screen reboots. As you may already know, a Bundle accepts data from Java primitive types, Strings, objects Serializable and objects Parcelable (among a few others). Assuming here that the class Cronometro implements Parcelable, the code would look like this:

@Override
public void onSaveInstanceState(Bundle estadoDeSaida) {
    super.onSaveInstanceState(estadoDeSaida);
    estadoDeSaida.putParcelable("chaveDoMeuCronometro", meuCronometro);
}

After the recreation, the onCreate will be called, and will be passed on to him that same Bundle in the parameter savedInstanceState, instead of null which stays there when the Activity is starting normally. From it, you should remove the data you had placed and recreate your stopwatch.

In this example, it would look like this:

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...
    // inicialização dos elementos da tela
    // ...
    if (savedInstanceState) {
        this.meuCronometro = savedInstanceState.getParcelable("chaveDoMeuCronometro")
    } else {
        this.meuCronometro = new Cronometro();
    }

    atualizarCronometroDaTela();
}

That way, your Activity is protected from the loss of this information and your timer will not reset.

However, in the specific case of the stopwatch, you may need a more ingenious solution to prevent the loss of seconds in which the stopwatch would be frozen, waiting for destruction and re-creation. This solution is for the simplest (and most common) case where this is not important.

  • 2

    It remains to add that the class Chronometer has to implement Parcelable. However, the simplest solution would be to store the condition in milliseconds of the chronometer and the team current. When the Activity is recreated, the chronometer is recreated with a state equal to the sum of the recorded state and the difference between the team current and the team saved, thus avoid implementing Parceable and solve the problem of stopwatch.

  • @ramaral I came to say "assuming that Cronometro implements Parcelable". : ) I didn’t want to go into the details of how to do this because it would be a subject for another question. And it’s very interesting your idea of a solution. Simple enough, even giving a good result.

  • You’re right, I’m sorry, I didn’t understand that phrase.

  • No problem. :)

  • One more note in relation to my solution: it makes no sense to pass classes with behavior between activities, what must be passed is its state to, from it, recreate the new instance.

  • I’m not sure I understand. The way you said it, I understood that you were spending two timestamps, something like two longs.

  • But then you spoke as if you were failing to recommend the solution you gave. Or I got it wrong?

  • What I mean is that you should not pass the "chronometer object" but your state (value marked on the dial).

  • In my head, Cronometro is an object that contains only the data. Anyway, it has some reference resource on this principle that you mentioned?

  • I saw Chronometer also with behavior, like: start(), stop(), reset() etc. Why pass (in this case) the whole class if only one value is required?

  • I understand that it makes no sense to pass objects that own resources like streams and network connections, but in cases like the stopwatch that has these methods there that you quoted, I see no harm. Maybe I’m not seeing beyond this example, so I asked for a reference to this principle. With other examples maybe I understand better.

  • 1

    Of course, in a simple example like this, it is of little importance. Suppose that the Activity represents a car race, besides the stopwatch would have to pass the list of cars. Both Chronometer as Car would have to implement Parcelable. What I say is that it’s best to just pass the race state. A class that implements Parcelable and representing that state, eg: chronometer value and position of cars. Think of other situations where there may be a larger number of classes involved. Regarding a reference: I do not know if it exists.

  • 1

    Right. I think I understand the advice. I’ll keep that in mind. Thank you for the clarification. :)

Show 8 more comments

1

Great solution Pablo Almeida, I used this same, with a Serializable object, so I did not need to implement anything of Parceable. But you have to cast the object on the return of onCreate...

@Override
public void onSaveInstanceState(Bundle estadoDeSaida) {
    super.onSaveInstanceState(estadoDeSaida);
    estadoDeSaida.putSerializable("chaveDoMeuCronometro", meuCronometro);
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    // ...
    // inicialização dos elementos da tela
    // ...
    if (savedInstanceState) {
        this.meuCronometro = (Cronometro) savedInstanceState.getSerializable("chaveDoMeuCronometro")
    } else {
        this.meuCronometro = new Cronometro();
    }

    atualizarCronometroDaTela();
}

Browser other questions tagged

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