FeeeeeLog

CountDownTimerを使ってはいけない2つの理由「Android」(2)

   

CountDownTimerを使ってはいけない2つの理由「Android」(1)」の続きです。

2つ目の問題点は「端末がディープスリープするとカウントが止まる」です。

スポンサーリンク

もくじ

端末がディープスリープするとCountDownTimerのカウントが止まる

文字通り、端末がディープスリープするとカウントが止まります。

CountDownTimerでは「Handler#sendMessageDelayed(Message, long)」を使用してカウントダウンする間隔を遅延させています。
メソッドの抜粋です。

内部で「Handler#sendMessageAtTime(android.os.Message, long)」を呼び出しています。
次にAPIのコメントを見てみます。

Enqueue a message into the message queue after all pending messages before the absolute time (in milliseconds) uptimeMillis. The time-base is uptimeMillis(). Time spent in deep sleep will add an additional delay to execution. You will receive it in handleMessage(Message), in the thread attached to this handler.

Parameters
uptimeMillis The absolute time at which the message should be delivered, using the uptimeMillis() time-base.
Returns
  • Returns true if the message was successfully placed in to the message queue. Returns false on failure, usually because the looper processing the message queue is exiting. Note that a result of true does not mean the message will be processed — if the looper is quit before the delivery time of the message occurs then the message will be dropped.

uptimeMillesをベースとした時刻にHander#handleMessage(Message)を呼び出すと説明しています。
ここで最大の注意事項です。「ディープスリープ中に過ぎた時間は、実行時間に加える」と書いてあります。
ディープスリープした分だけHander#handleMessage(Message)を呼び出すタイミングがずれるということです。

ディープスリープとは

明確な定義は分かっていませんが、端末がスリープしAndroidが全く動作しない状態のことを指していると考えています。
スリープしていてもバックグラウンドで動作しているような場合はディープスリープではありません。

解決策1:ディープスリープさせない

CountDownTimer.javaを使ったアプリを使うときには、常にスリープさせずに画面を表示した状態を維持するということです。これを人為的に行うのは大変なので、WakeLockを取得してカウントダウンが終了するまでスリープさせないということをします。
ただし、WakeLockを取得するとバッテリーを非常に消耗するのであまりよい解決策ではないでしょう。

解決策2:ディープスリープしてもよい実装にする

クラスを自作し、ディープスリープしてもカウントダウンが正確に行われるようにします。
具体的にどうすればよいのかということですが、求めていることによって実装は変わってきます。

最低限必要になる要素は次の2つでしょう。

  • ディープスリープしてもカウントダウン終了時刻や間隔がずれないこと
  • ディープスリープを起こす必要があるということ

ディープスリープしてもカウントダウン終了時刻や間隔がずれないこと

Androidの時間の概念には3種類あります。

  1. OSが起動してからの時間。ディープスリープの時間を除く。
  2. OSが起動してからの時間。ディープスリープの時間を含む。
  3. 一般的な日時。日時を変更すると、それに伴って変更するもの。

Handlerが使用していたのは「1」です。「2」を使うことでディープスリープしても時間はずれません。

ディープスリープの状態から端末を動作することができるもの

それは「AlarmManager」を使用することです。
アラームを使用して指定した時刻にディープスリープしていても起動することができます。

さいごに

長々と説明しましたが、タイマーアプリを作るときの肝の部分です。
他の方法もあるかもしれませんが、これらのことに気をつけてタイマーを作成すると正確な時間でカウントダウンし終了するということができるのではないでしょうか。