Comment régler une minuterie dans Android

172

Quelle est la bonne façon de définir une minuterie dans Android afin de lancer une tâche (une fonction que je crée qui ne change pas l'interface utilisateur)? Utilisez-le à la manière Java: http://docs.oracle.com/javase/1.5.0/docs/api/java/util/Timer.html

Ou il existe un meilleur moyen dans Android (gestionnaire d'Android)?

n179911
la source

Réponses:

143

La méthode Java standard pour utiliser les minuteries via java.util.Timer et java.util.TimerTask fonctionne correctement sous Android, mais vous devez être conscient que cette méthode crée un nouveau thread.

Vous pouvez envisager d'utiliser la classe Handler très pratique (android.os.Handler) et envoyer des messages au gestionnaire via sendMessageAtTime(android.os.Message, long)ou sendMessageDelayed(android.os.Message, long). Une fois que vous recevez un message, vous pouvez exécuter les tâches souhaitées. La deuxième option serait de créer un objet Runnable et de le planifier via les fonctions de Handler postAtTime(java.lang.Runnable, long)ou postDelayed(java.lang.Runnable, long).

MannyNS
la source
10
Ce n'est pas la bonne façon de faire les choses sous Android. Dans Android, vous souhaitez utiliser le gestionnaire d'alarmes (developer.android.com/reference/android/app/AlarmManager.html) pour exécuter des tâches dans le futur.
Kurtis Nusbaum
9
@Kurtis Nusbaum La question ne dit pas jusqu'où va la tâche dans le futur.
Christopher Perry
67
@KurtisNusbaum Ce n'est pas forcément vrai, cela dépend du contexte. La documentation sur AlarmManager dit: "Remarque: le gestionnaire d'alarme est destiné aux cas où vous souhaitez que le code de votre application s'exécute à une heure précise, même si votre application n'est pas en cours d'exécution. Pour les opérations de synchronisation normales (ticks, délais, etc.) il est plus facile et beaucoup plus efficace d’utiliser Handler. »
Christopher Perry
5
@Scienceprodigy Ah, je vois. C'est suffisant.
Kurtis Nusbaum
10
l'utilisation de la méthode Handler pour planifier une tâche n'est fiable que si l'application a acquis un wakeLock et que vous êtes donc sûr que le téléphone ne passera pas en état de veille. Si le téléphone passe en état de veille, sendMessageDelayed ainsi que sendMessageAtTime ne fonctionneront pas. Par conséquent, dans ce scénario, AlarmManager serait un choix fiable.
crazyaboutliv
159

oui la minuterie de java peut être utilisée , mais comme la question demande une meilleure façon (pour mobile). Ce qui est expliqué ici .


Pour le bien de StackOverflow:

Puisque Timer crée un nouveau thread, il peut être considéré comme lourd,

si tout ce dont vous avez besoin est d'obtenir un rappel pendant que l'activité est en cours d'exécution, un gestionnaire peut être utilisé en conjonction avec un

Exécutable :

private final int interval = 1000; // 1 Second
private Handler handler = new Handler();
private Runnable runnable = new Runnable(){
    public void run() {
        Toast.makeText(MyActivity.this, "C'Mom no hands!", Toast.LENGTH_SHORT).show();
    }
};
...
handler.postAtTime(runnable, System.currentTimeMillis()+interval);
handler.postDelayed(runnable, interval);

ou un message

private final int EVENT1 = 1; 
private Handler handler = new Handler() {
    @Override
    public void handleMessage(Message msg) {
        switch (msg.what) {         
        case Event1:
            Toast.makeText(MyActivity.this, "Event 1", Toast.LENGTH_SHORT).show();
            break;

        default:
            Toast.makeText(MyActivity.this, "Unhandled", Toast.LENGTH_SHORT).show();
            break;
        }
    }
};

...

Message msg = handler.obtainMessage(EVENT1);
handler.sendMessageAtTime(msg, System.currentTimeMillis()+interval);
handler.sendMessageDelayed(msg, interval);

en passant, cette approche peut être utilisée si vous souhaitez exécuter un morceau de code dans le thread d'interface utilisateur à partir d'un autre thread.

si vous avez besoin d'un rappel même si votre activité n'est pas en cours, vous pouvez utiliser un AlarmManager

Samuel
la source
1
Oh oui, j'ai recherché exactement cette réponse sur Google. Prenez mon vote pour le lien utile.
ulidtko
1
mis à jour le lien, parlez de google ayant du mal à maintenir les liens.
Samuel
1
Cet article de 2007 - je ne dis pas que c'est faux, mais je me méfie toujours d'un article mobile s'il a plus de 3 ans. Les choses changent assez rapidement.
StackOverflowed le
1
ce serait plutôt StackOverflow si vous pouviez citer les principaux points de votre lien ici dans votre réponse.
n611x007
1
@naxa, mes deux cents pour que cette réponse ressemble plus à StackOverflow.
Samuel
131

Comme je l'ai vu, java.util.Timer est le plus utilisé pour implémenter une minuterie.

Pour une tâche répétitive:

new Timer().scheduleAtFixedRate(task, after, interval);

Pour une seule exécution d'une tâche:

new Timer().schedule(task, after);


tâche étant la méthode à exécuter
après le temps d'exécution initiale
( intervalle le temps de répétition de l'exécution)

Thizzer
la source
9
J'ajouterai simplement que le temps est en millisecondes
Uri
2
Documents de développement
1
taskpeut être une instance de votre classe qui hérite de java.util.TimerTask et des substitutions void run().
n611x007
2
Tant de votes positifs. Vous pourriez à cela. Mais @Samuel décrit la meilleure façon. Voir son lien «Ce qui est expliqué ici» pour savoir pourquoi. De plus, impossible de mettre à jour l'interface utilisateur à partir d'un thread Timer! Et voyez ces autres réponses basées sur Handler dans d'autres threads: (simple) stackoverflow.com/a/6702767/199364 , ou (montre diverses alternatives) stackoverflow.com/a/4598737/199364
ToolmakerSteve
@quemeful car il ne s'agit pas toujours de commodité, mais de qualité et d'efficacité du code. Cette façon de faire utilise plus de ressources que l'utilisation du gestionnaire
inDepth
33

J'espère que celui-ci est utile et peut nécessiter moins d'efforts à implémenter, classe Android CountDownTimer

par exemple

 new CountDownTimer(30000, 1000) {
      public void onTick(long millisUntilFinished) {
          mTextField.setText("seconds remaining: " + millisUntilFinished / 1000);
      }

      public void onFinish() {
          mTextField.setText("done!");
      }  
}.start();
Gaurav Adurkar
la source
1
C'est le plus facile et frappe directement sur la tête pour un compte à rebours simple comme une vidéo de début de lecture
Vijay Kumar Kanta
17

Probablement Timerconcept

new CountDownTimer(40000, 1000) { //40000 milli seconds is total time, 1000 milli seconds is time interval

 public void onTick(long millisUntilFinished) {
  }
  public void onFinish() {
 }
}.start();

ou

Méthode 2 ::

Programmer la minuterie

Ajoutez une nouvelle variable de temps nommé int. Définissez-le sur 0. Ajoutez le code suivant à la fonction onCreate dans MainActivity.java.

//Declare the timer
Timer t = new Timer();
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {

    @Override
    public void run() {
        //Called each time when 1000 milliseconds (1 second) (the period parameter)
    }

},
//Set how long before to start calling the TimerTask (in milliseconds)
0,
//Set the amount of time between each execution (in milliseconds)
1000);

Accédez à la méthode d'exécution et ajoutez le code suivant.

//We must use this function in order to change the text view text
runOnUiThread(new Runnable() {

    @Override
    public void run() {
        TextView tv = (TextView) findViewById(R.id.main_timer_text);
        tv.setText(String.valueOf(time));
        time += 1;
    }

});
vkulkarni
la source
11

C'est situationnel.

La documentation Android suggère que vous devez utiliser AlarmManager pour enregistrer une intention qui se déclenchera à l'heure spécifiée si votre application n'est peut-être pas en cours d'exécution.

Sinon, vous devez utiliser Handler.

Remarque: Le gestionnaire d'alarme est destiné aux cas où vous souhaitez que le code de votre application s'exécute à un moment précis, même si votre application n'est pas en cours d'exécution. Pour les opérations de synchronisation normales (ticks, timeouts, etc.), il est plus facile et beaucoup plus efficace d'utiliser Handler.

Timothy Lee Russell
la source
11

On y va .. Nous aurons besoin de deux classes. Je poste un code qui change le profil audio du mobile toutes les 5 secondes (5000 mili secondes) ...

Notre 1ère classe

public class ChangeProfileActivityMain extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);

        Timer timer = new Timer();
        TimerTask updateProfile = new CustomTimerTask(ChangeProfileActivityMain.this);
        timer.scheduleAtFixedRate(updateProfile, 0, 5000);
    }

}

Notre 2e classe

public class CustomTimerTask extends TimerTask {

    private AudioManager audioManager;
    private Context context;
    private Handler mHandler = new Handler();

    // Write Custom Constructor to pass Context
    public CustomTimerTask(Context con) {
        this.context = con;
    }

    @Override
    public void run() {
        // TODO Auto-generated method stub

        // your code starts here.
        // I have used Thread and Handler as we can not show Toast without starting new thread when we are inside a thread.
        // As TimePicker has run() thread running., So We must show Toast through Handler.post in a new Thread. Thats how it works in Android..
        new Thread(new Runnable() {
            @Override
            public void run() {
                audioManager = (AudioManager) context.getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
                mHandler.post(new Runnable() {
                    @Override
                    public void run() {
                        if(audioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
                            audioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
                            Toast.makeText(context, "Ringer Mode set to Normal", Toast.LENGTH_SHORT).show();
                        } else {
                            audioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
                            Toast.makeText(context, "Ringer Mode set to Silent", Toast.LENGTH_SHORT).show();
                        }
                    }
                });
            }
        }).start();

    }

}
Rizwan Sohaib
la source
5

Je suis un débutant Android, mais voici la classe de minuterie que j'ai créée en fonction des réponses ci-dessus. Cela fonctionne pour mon application, mais j'accueille toutes les suggestions.

Exemple d'utilisation:

...{
public Handler uiHandler = new Handler();

  private Runnable runMethod = new Runnable()
    {
        public void run()
        {
              // do something
        }
    };

    timer = new UITimer(handler, runMethod, timeoutSeconds*1000);       
        timer.start();
}...

public class UITimer
{
    private Handler handler;
    private Runnable runMethod;
    private int intervalMs;
    private boolean enabled = false;
    private boolean oneTime = false;

    public UITimer(Handler handler, Runnable runMethod, int intervalMs)
    {
        this.handler = handler;
        this.runMethod = runMethod;
        this.intervalMs = intervalMs;
    }

    public UITimer(Handler handler, Runnable runMethod, int intervalMs, boolean oneTime)
    {
        this(handler, runMethod, intervalMs);
        this.oneTime = oneTime;
    }

    public void start()
    {
        if (enabled)
            return;

        if (intervalMs < 1)
        {
            Log.e("timer start", "Invalid interval:" + intervalMs);
            return;
        }

        enabled = true;
        handler.postDelayed(timer_tick, intervalMs);        
    }

    public void stop()
    {
        if (!enabled)
            return;

        enabled = false;
        handler.removeCallbacks(runMethod);
        handler.removeCallbacks(timer_tick);
    }

    public boolean isEnabled()
    {
        return enabled;
    }

    private Runnable timer_tick = new Runnable()
    {
        public void run()
        {
            if (!enabled)
                return;

            handler.post(runMethod);

            if (oneTime)
            {
                enabled = false;
                return;
            }

            handler.postDelayed(timer_tick, intervalMs);
        }
    }; 
}
user868020
la source
3

J'utilise un gestionnaire et exécutable pour créer une minuterie. J'emballe cela dans une classe abstraite. Il suffit de le dériver / l'implémenter et vous êtes prêt à partir:

 public static abstract class SimpleTimer {
    abstract void onTimer();

    private Runnable runnableCode = null;
    private Handler handler = new Handler();

    void startDelayed(final int intervalMS, int delayMS) {
        runnableCode = new Runnable() {
            @Override
            public void run() {
                handler.postDelayed(runnableCode, intervalMS);
                onTimer();
            }
        };
        handler.postDelayed(runnableCode, delayMS);
    }

    void start(final int intervalMS) {
        startDelayed(intervalMS, 0);
    }

    void stop() {
        handler.removeCallbacks(runnableCode);
    }
}

Notez que le handler.postDelayedest appelé avant le code à exécuter - cela rendra le temporisateur plus fermé comme "attendu". Cependant, dans les cas où le minuteur s'exécute trop fréquemment et que la tâche ( onTimer()) est longue, il peut y avoir des chevauchements. Si vous souhaitez commencer à compter une intervalMSfois la tâche terminée, déplacez l' onTimer()appel d'une ligne au-dessus.

elcuco
la source
2

Je pense que le moyen de le faire sur Android est que vous avez besoin d'un service d'arrière-plan pour fonctionner. Dans cette application d'arrière-plan, créez le minuteur. Lorsque la minuterie "coche" (définissez l'intervalle de temps pendant lequel vous souhaitez attendre), lancez votre activité que vous souhaitez démarrer.

http://developer.android.com/guide/topics/fundamentals.html (<- cet article explique la relation entre les activités, les services, les intentions et les autres principes fondamentaux du développement Android)

Crowe T. Robot
la source
1

J'avais l'habitude d'utiliser (Timer , TimerTask) ainsi que Handlerpour lancer des tâches (chronophages) périodiquement. Maintenant, j'ai changé le tout sur RxJava. RxJava fournit Observable.timerce qui est plus simple, moins sujet aux erreurs et sans tracas à utiliser.

public class BetterTimerFragment extends Fragment {
  public static final String TAG = "BetterTimer";
  private TextView timeView;
  private Subscription timerSubscription;

  @Override
  public View onCreateView(LayoutInflater inflater,
                           @Nullable ViewGroup container,
                           @Nullable Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment_timer, container, false);
  }

  @Override
  public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
    super.onViewCreated(view, savedInstanceState);
    timeView = (TextView) view.findViewById(R.id.timeView);
  }

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

    // Right after the app is visible to users, delay 2 seconds
    // then kick off a (heavy) task every 10 seconds.
    timerSubscription = Observable.timer(2, 10, TimeUnit.SECONDS)
        .map(new Func1<Long, String>() {
          @Override
          public String call(Long unused) {
            // TODO: Probably do time-consuming work here.
            // This runs on a different thread than the main thread.
            return "Time: " + System.currentTimeMillis();
          }
        })
        .observeOn(AndroidSchedulers.mainThread())
        .subscribe(new Action1<String>() {
          @Override
          public void call(String timeText) {
            // The result will then be propagated back to the main thread.
            timeView.setText(timeText);
          }
        }, new Action1<Throwable>() {
          @Override
          public void call(Throwable throwable) {
            Log.e(TAG, throwable.getMessage(), throwable);
          }
        });
  }

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

    // Don't kick off tasks when the app gets invisible.
    timerSubscription.unsubscribe();
  }
}
Thuy Trinh
la source
1

Pour l'opération de chronométrage, vous devez utiliser Handler .

Si vous avez besoin d'exécuter un service d'arrière-plan, AlarmManager est la solution.

robsf
la source
0

cet exemple démarre l'unité de minuterie détruite à Kotlin

private lateinit var timerTask: TimerTask

 timerTask = object : TimerTask() {
        override fun run() {
            Log.d("KTZ", "$minutes:$seconds");
            timeRecordingLiveData.postValue("$minutes:$seconds")
            seconds += 1;
            if (seconds == 60) {
                Log.d("KTZ", "$minutes:$seconds");
                timeRecordingLiveData.postValue("$minutes:$seconds")
                seconds = 0;
                minutes += 1;
            }
        }

    }

Annuler la tâche de temps dans onDestroy ()

timerTask.cancel()
Kshitiz Mishra
la source