AndroidでService内で発行したTimerが端末スリープ状態で勝手に止まる
解決済
回答 1
投稿
- 評価
- クリップ 0
- VIEW 8,707
AndroidでServiceを用いて秒単位でカウントダウン処理を行い、Receiverを用いてその値を受け、Activity側でカウントダウン値を表示しています。
端末がスリープ状態でも動作するようにServiceを用いているのですが、端末がスリープするとなぜかカウントダウンが数秒〜数十秒後に止まってしまします。
Nortificationを作成してstartForegroundをしてシステムからキルされづらくしたり、WakeLockを取得してCPUを常に起動させたりといった対策はしたのですが解決しません。
以下にソースコードを記載します。どなたか解決策を提示していただけないでしょうか?
「Service」のソースコードは以下です
package jp.wings.nikkeiibp.napalerm.Process;
import android.app.IntentService;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.os.PowerManager;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import java.util.Timer;
import java.util.TimerTask;
import jp.wings.nikkeiibp.napalerm.Activity.CountDownActivity;
import jp.wings.nikkeiibp.napalerm.Common.IntentKeyWord;
import jp.wings.nikkeiibp.napalerm.R;
public class TimerService extends Service {
//タイマー処理を行うオブジェクト
private Timer mTimer = null;
/** スリープ時のスクリーン及びCPUの制御を行うオブジェクト */
private PowerManager.WakeLock mWakeLock;
/** 設定した秒数 */
private int mTimerSeconds;
@Override
public void onCreate() {
super.onCreate();
//CPUやスリープ画面の制御オブジェクト取得
PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Cowntdown_Start");
//CPUを常にON状態にするのを開始(releaseするまで終了しない)
mWakeLock.acquire();
}
@Nullable
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public int onStartCommand ( Intent intent, int flags, int startId ) {
//カウントダウンする時間を取得
mTimerSeconds = intent.getIntExtra ( IntentKeyWord.SET_SECONDS, 0 );
Log.i ( "TimerService getSecond", "" + mTimerSeconds );
//通知を作成
Notification notification = new NotificationCompat.Builder(this)
.setContentTitle(getString(R.string.nortification_title))
.setContentText(getString(R.string.nortification_text))
.setSmallIcon(R.mipmap.ic_launcher)
.build();
//通知削除不可にする
notification.flags = Notification.FLAG_ONGOING_EVENT;
//通知マネージャー取得
NotificationManager manager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
//通知識別ID
final int NUM = 1;
//通知発行
manager.notify(NUM, notification);
//システムから殺されにくくする
startForeground(NUM,notification);
// タイマーの設定 1秒毎にループ
mTimer = new Timer ();
mTimer.schedule ( new TimerTask () {
@Override
public void run () {
//カウントダウン
mTimerSeconds--;
//レシーバーに通知
sendBroadCast();
}
}, 1000, 1000 );
return START_STICKY;
}
@Override
public void onDestroy () {
mWakeLock.release();
// タイマー停止
if( mTimer != null ){
mTimer.cancel();
mTimer = null;
}
super.onDestroy ();
}
/**
* レシーバーに値を渡す
*/
protected void sendBroadCast() {
Intent broadcastIntent = new Intent();
broadcastIntent.putExtra(IntentKeyWord.SET_SECONDS, mTimerSeconds);
Log.i ( "sendBroadCast setSecond", "" + mTimerSeconds );
broadcastIntent.setAction("UPDATE_ACTION");
getBaseContext().sendBroadcast(broadcastIntent);
}
}
「Receiver」のソースコードは以下です
package jp.wings.nikkeiibp.napalerm.Process;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import jp.wings.nikkeiibp.napalerm.Common.IntentKeyWord;
public class TimerReceiver extends BroadcastReceiver{
/** サービスから値を受け取ったときに動かす非同期処理 */
public static Handler handler;
@Override
public void onReceive ( Context context, Intent intent ) {
Bundle getbundle = intent.getExtras ();
int seconds = getbundle.getInt ( IntentKeyWord.SET_SECONDS );
Log.i ( "TimerReceiver getSecond", "" + seconds );
if(handler !=null){
Message msg = new Message();
Bundle setbundle = new Bundle();
setbundle.putInt ( IntentKeyWord.SET_SECONDS, seconds);
Log.i ( "TimerReceiver setSecond", "" + seconds );
msg.setData(setbundle);
handler.sendMessage(msg);
}
}
/**
* 値受け取り時に実行する非同期処理の登録
*/
public void registerHandler(Handler handler) {
this.handler = handler;
}
}
カウントダウンを表示するActivityのソースコードは以下
public class CountDownActivity extends Activity {
/** サービスから値を受け取るレシーバー */
private TimerReceiver mTimerReceiver;
/** 暗黙的インテントの値を受け取る際のフィルター */
private IntentFilter mIntentFilter;
/** カウントダウン表示用テキスト */
private TextView mCountText;
/** 時間をカウントダウンするサービス */
private Intent mService;
@Override
protected void onCreate ( Bundle savedInstanceState ) {
super.onCreate ( savedInstanceState );
setContentView ( R.layout.activity_count_down );
mCountText = (TextView )findViewById( R.id.countDownText);
//インテントから値を取得
mService = new Intent(this , TimerService.class);
int time = getIntent ().getIntExtra ( IntentKeyWord.SET_SECONDS, 0 );
//時分秒を計算
final int hour = time / 3600;
final int minute = ( time % 3600 ) / 60;
final int second = ( time % 3600 ) % 60;
//初期秒数をテキストに表示
setCountText(hour,minute,second);
//0時間0分の場合+1秒(マイナスにカウントし始めるのを防ぐ)
if(time == 0)
time = 1;
//サービスに秒数をセット
mService.putExtra ( IntentKeyWord.SET_SECONDS, time );
Log.i ( "service setSecond", "" + time );
//タイマーサービス開始
startService ( mService );
//サービスのレシーバーをセット
setReceiver ();
}
@Override
protected void onDestroy() {
super.onDestroy();
stopService(mService);
}
/**
* レシーバーをセット
* レシーバーはサービスから値を受け取るもの
*/
private void setReceiver(){
mTimerReceiver = new TimerReceiver ();
mIntentFilter = new IntentFilter ();
mIntentFilter.addAction ( "UPDATE_ACTION" );
registerReceiver ( mTimerReceiver, mIntentFilter );
mTimerReceiver.registerHandler ( updateHandler );
}
/**
* サービスから値を受け取った際の更新処理
*/
private Handler updateHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
Bundle bundle = msg.getData ();
int time = bundle.getInt ( IntentKeyWord.SET_SECONDS );
//カウントダウンが完了した
if(time == 0){
//サービス終了
stopService(mService);
//次のアクティビティへ
goNotificaionAlert();
}else{
final int hour = time / 3600;
final int minute = ( time % 3600 ) / 60;
final int second = ( time % 3600 ) % 60;
//カウントダウン表示更新
setCountText(hour,minute,second);
}
}
};
/**
* カウントダウンテキストをセットする
* @param hour 時間
* @param minute 分
* @param second 秒
*/
private void setCountText(int hour,int minute,int second){
mCountText.setText ( String.format ( "%02d",hour ) +
":" + String.format ( "%02d",minute ) +
":" + String.format ( "%02d",second ));
}
}
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
0
https://groups.google.com/forum/#!topic/android-group-japan/OMGp8_9A5HE
上記に同じ現象で解決案も載ってます
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.37%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる