質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

90.50%

  • Java

    14049questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • Android

    6618questions

    Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Android のService+Receiver が端末スリープして数秒後に止まる

受付中

回答 1

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 1,409

peishun

score 19

AndroidでService+Receiverを実装しています。なぜかというとバックグランドでタイマー処理を実現したいからです。

Secerviceが1秒ごとに時間をカウントダウンして、Receiverがその値を取得してActivityに通知し、カウントダウンの様子を画面に表示するという処理をしています。

しかし、この時の電源ボタンを押してスリープ状態にすると30秒ぐらいでカウントダウンが止まってしまいます。
ちなみに、Android studioを起動したPCに接続してUSBデバッグモードで同じことをするとなぜかカウントダウンが止まりません。

Nortificationを作成し、startForgroundの実行等も行ってみましたが、解決しませんでした。

この現象に、どんな原因もしくはどんな対処法が考えられるでしょうか?

サービスのソースコードは以下のようになっています。

package jp.wings.nikkeiibp.napalerm.Process;

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.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;

    //非同期処理
    android.os.Handler mHandler = new android.os.Handler();

    /** 設定した秒数 */
    private int mTimerSeconds;

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

    @Override
    public void onCreate () {
        super.onCreate ();
    }

    @Override
    public int onStartCommand ( Intent intent, int flags, int startId ) {
        super.onStartCommand(intent, flags, 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 (true);
        mTimer.schedule ( new TimerTask () {
            @Override
            public void run () {
                mHandler.post ( new Runnable () {
                    public void run () {
                        //カウントダウン
                        mTimerSeconds--;
                        //レシーバーに通知
                        sendBroadCast ();
                    }
                } );
            }
        }, 1000, 1000 );



        return START_STICKY;

    }

    @Override
    public void onDestroy () {
        super.onDestroy ();
        // タイマー停止
        if( mTimer != null ){
            mTimer.cancel();
            mTimer = null;
        }
    }

    /**
     * レシーバーに値を渡す
     */
    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);
    }
}

レーシーバは以下のようになっています

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;
    }
}

カウントダウンを表示するアクティビティは以下の通りになっています

package jp.wings.nikkeiibp.napalerm.Activity;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.Service;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.graphics.Typeface;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.annotation.RawRes;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.NotificationCompat;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;

import jp.wings.nikkeiibp.napalerm.Common.IntentKeyWord;
import jp.wings.nikkeiibp.napalerm.Process.TimerReceiver;
import jp.wings.nikkeiibp.napalerm.Process.TimerService;
import jp.wings.nikkeiibp.napalerm.R;

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);

        //サービスに秒数をセット
        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);

                //カウントダウンの文字を00:00:00にする
                setCountText(0,0,0);

                //文字の色を変える
                mCountText.setTextColor(Color.RED);

                //文字を太くする
                mCountText.setTypeface(Typeface.DEFAULT_BOLD);

                //次のアクティビティへ
                goNotificaionAlert();

            }else{

                final int hour = time / 3600;
                final int minute = ( time % 3600 ) / 60;
                final int second = ( time % 3600 ) % 60;

                //カウントダウン表示更新
                setCountText(hour,minute,second);

            }
        }
    };
}

ちなみにマニュフェストファイルには以下の記述をしております。

<application

    ・・・・・・・・・・・

  <service android:name=".Process.TimerService" />
  <receiver android:name=".Process.TimerReceiver" />


  ・・・・・・・・・・
</application>
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • aja

    2016/04/21 12:39

    カウントダウンをどのようにしているのでしょうか? コードを出してもらわないと...

    キャンセル

  • peishun

    2016/04/21 14:57

    大変失礼いたしました。コードの追記をいたしました

    キャンセル

回答 1

+1

スリープの次にサスペンド(ディープスリープ)と呼ばれる状態があります。

スリープでは、画面表示は消えますが、まだCPUが完全に動いている状態で、
サスペンドでは、CPUの動作も制限し、最低限の機能だけ動かしている状態です。

USBデバッグ中は、このサスペンド状態に入ることがないみたいです。
(LogCatとか、デバッガーとか動かしているので、
CPUの動作を制限することが出来ないんでしょうかね?詳しくは分かりません)

USBデバッグを接続していない状態で、サスペンドにさせたくない場合は、
アプリ側から、WakeLockをPARTIAL_WAKE_LOCKで取得しておけば、
電源ボタンが押されても、サスペンドに移行させないことが出来たと思います。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2016/04/21 15:31 編集

    解決したかと思ったのですが、まだ上手くいかなかったです。しかしサスペンドという状態が存在することが初耳だったので勉強になりました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 90.50%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

  • 受付中

    取得した画像をgridViewで表示する際に、スレッドを使って高速化したい

    端末内に保存されている画像を取得して、gridViewを使ってサムネイル表示するコードを書きましたが、画像が多くなると、取得に時間がかかり、表示までかなり待たされる状態です。 ス

  • 解決済

    facebookログインする際にCallbackされない

    frangmentにてログインボタンを表示し、 それをクリックすることでfacebookログインをしようとしているのですが、callbackされません。 最初、facebook

  • 受付中

    Android Lolipop(5.0)以上の時のNotificationのAutoCancelにつ...

    お世話になっております。 Push通知を受信した後、通知センターからのタップでアプリ起動の後、通知センターから該当通知を削除したいのですが、 Lolipop(5.0)以上だ

  • 解決済

    Java Android SeekBarの値を渡す

    SeekBarを使ってMainclassから違うクラスに値を渡したいです。 違うクラスというのが、Serviceを使っているので、Intentを使ってやるやり方がよく分かりません

  • 解決済

    バックグランドのActivityを強制起動する方法

    Androidアプリ開発をしております。 一定時間経過後、たとえAndroid端末がスリープ状態だったとしてもActivityを起動する処理を実装しようとしています。

  • 解決済

    カウントダウン

    ・ソースコード func twelve(num11:Int){     while num11 >= 0{         print(num11)         num

  • 解決済

    Androidで現在地を取ることについて

    やりたいことはANDROIDで定期的に現在地を取得したい(画面なしで)。 さらにそれを常駐させたい。 ということなのです。 まず常駐は http://language-an

  • 解決済

    Android Firebaseでの通知受信について

    FirebaseでPush通知を処理するプログラムを作っています。 端末がスリープ状態にある時、通知を受けた時点でスリープから復帰したいのですが、 どうしても出来ません。

同じタグがついた質問を見る

  • Java

    14049questions

    Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

  • Android

    6618questions

    Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。