AlarmManager は、アラームを作成するためのクラスです。アラームを使うと、将来の特定の時間に特定のコードが実行されるようにアプリをスケジュールすることができます。

public class AlarmManager extends Object{}

タイマーのようなものを使用するよりも、AlarmManagerクラスを使用してアラームを作成し、スケジューリングする方がより良く、より効率的です。

AlarmManager は、システムアラームサービスへのアクセスを提供してくれるので、スケジューリングアルゴリズムを発明するようなことはありません。

AlarmManager は主に BroadcastReceivers` と一緒に使用されます。その仕組みは以下の通りです。

*最初のアラームが鳴る、または鳴らない。

  • システムは intent をブロードキャストします。これは登録されていた intent です。
  • ターゲットアプリケーションがまだ実行されていない場合は、自動的に起動します。
  • デバイスがスリープしても、すでに登録されているアラームは保持されます。
  • デバイスがスリープしているときにアラームが鳴った場合は、デバイスが起こされます。これはオプションです。
  • ユーザーがデバイスの電源を切ったり、再起動した場合、アラームはクリアされます。

ブロードキャストの処理が終わるまで、電話がスリープしないことが保証されます。ブロードキャストの処理は、android.content.BroadcastReceiveronReceive()メソッドで行います。これは、このクラスを派生させた後にオーバーライドするメソッドです。

onReceive()メソッドが実行されている間は、AlarmManager`はCPUのウェイクロックを保持します。そのため、デバイスはスリープしません。

そして、onReceive()の実行が終了して戻ると、AlarmManagerはウェイクロックを解除します。

しかし、onReceive()メソッドが終了すると同時に、端末がすぐにスリープしてしまうことがあります。端末がすぐにスリープしてしまったため、Context.startService()を使ってサービスをリクエストしていても、そのサービスは開始されません。これは、呼び出される前にデバイスがスリープしてしまったためです。しかし、最初のウェイクロックはもはや行われていません。これはonReceive()が返ってきた瞬間に解除されたものです。では、どうすればいいのでしょうか?まず、BroadcastReceiverServiceに別のウェイクロックを実装します。このウェイクロックにより、サービスが利用可能になるまでデバイスが動作することが保証されます。

では、AlarmManagerはいつ使用し、いつ使用しないのでしょうか?AlarmManagerはスケジューリングのために使います。タイミングやティッキングの操作には使用しないでください。また、スケジューリング操作にタイマーを使用しないでください。AlarmManagerを使ったスケジュールコードは、アプリケーションが常に実行されている必要はありません。タイマーを使った場合には、常に実行していなければなりません。これはメモリと処理時間の無駄です。

Android OSでは、アラームをシフトさせることで、ウェイクアップやバッテリー消費を最小限に抑えています。これはAndroid API 19(KitKat)からです。このアラームは厳密には正確ではないかもしれません。厳密に行う必要がある場合は、setExact()メソッドを使用してください。

AlarmManager "は直接インスタンス化されません。代わりに、Contextクラスの静的なgetSystemService()を使用します。その際、Context.ALARM_SERVICE` フラグを渡します。

Context.getSystemService(Context.ALARM_SERVICE

例題 1: Kotlin Android で目覚まし時計を作る

この例題は、アラームとBroadcastreceiverの使い方を学ぶためのものです。その過程で、簡単な目覚まし時計を作ります。この例では、これらの技術を実践的に学ぶことができます。

ステップ 1: Kotlin プロジェクトの作成

まず、Android Studio で空の Kotlin プロジェクトを作成します。

Step 2: 依存関係

特別な依存関係は必要ありません。ただし、コルーチンを使用しますので、必ず Kotlin を使用してください。

ステップ 3: パーミッション

このプロジェクトにはパーミッションは必要ありません。ただし、この目覚まし時計では BroadcastReceiver を使用しているので、AndroidManifest.xmlreceiver を登録してください。

        <receiver
            android:name=".AlarmReceiver"
            android:exported="false" />

Step 4: レイアウトの設計

必要なレイアウトは、MainActivityレイアウトの1つだけです。TextViewやボタンを追加して、以下のようにConstraintLayoutを使って制約をかけるだけです。

activity_main.xmlを参照してください。

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/background_black"
    tools:context=".MainActivity">

    <View
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:layout_marginHorizontal="50dp"
        android:background="@drawable/background_white_ring"
        app:layout_constraintBottom_toTopOf="@id/onOffButton"
        app:layout_constraintDimensionRatio="H,1:1"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/timeTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="09:30"
        android:textColor="@color/white"
        android:textSize="50sp"
        app:layout_constraintBottom_toTopOf="@id/ampmTextView"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_chainStyle="packed" />

    <TextView
        android:id="@+id/ampmTextView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="AM"
        android:textColor="@color/white"
        android:textSize="25sp"
        app:layout_constraintBottom_toTopOf="@id/onOffButton"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@id/timeTextView" />

    <Button
        android:id="@+id/onOffButton"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="@string/on_alarm"
        app:layout_constraintBottom_toTopOf="@id/changeAlarmTimeButton"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <Button
        android:id="@+id/changeAlarmTimeButton"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:text="@string/change_time"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

ステップ 5: モデルクラスの作成

モデルクラスを作成します。Kotlinのデータクラスで、2つの整数とブール値をconstructorで受け取ります。整数は「時」と「分」で、ブーリアンは「オンオフ」です。これらは、目覚まし時計に表示されるタイマービューを構築するために使用されます。

AlarmDisplayModel.kt

package com.example.alarmclock

data class AlarmDisplayModel(
    val hour: Int,
    val minute: Int,
    var onOff: Boolean
) {

    val timeText: String
        get() {
            val h = "%02d".format(if (hour < 12) hour else hour - 12)
            val m = "%02d".format(minute)

            return "$h:$m"
        }

    val ampmText: String
        get() {
            return if (hour < 12) "AM" else "PM"
        }

    val onOffText: String
        get() {
            return if (onOff) "알람 끄기" else "알람 켜기"
        }

    fun makeDataForDB(): String {
        return "$hour:$minute"
    }

}

Step 5: アラームレシーバーの作成

これは、broadcast receiverを拡張して、onReceive()メソッドをオーバーライドすることで行います。onReceive()`の中では、通知チャンネルを作成して、通知を作成します。

以下にそのコードを示します。

AlarmReceiver.kt

package com.example.alarmclock

import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Build
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat

class AlarmReceiver: BroadcastReceiver() {

    companion object {
        const val NOTIFICATION_ID = 100
        const val NOTIFICATION_CHANNEL_ID = "1000"
    }

    override fun onReceive(context: Context, intent: Intent) {
        createNotificationChannel(context)
        notifyNotification(context)
    }

    private fun createNotificationChannel(context: Context) {

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
            val notificationChannel = NotificationChannel(
                NOTIFICATION_CHANNEL_ID,
                "기상 알람",
                NotificationManager.IMPORTANCE_HIGH
            )

            NotificationManagerCompat.from(context).createNotificationChannel(notificationChannel)
        }
    }

    private fun notifyNotification(context: Context) {
        with(NotificationManagerCompat.from(context)) {
            val build = NotificationCompat.Builder(context, NOTIFICATION_CHANNEL_ID)
                .setContentTitle("알람")
                .setContentText("일어날 시간입니다.")
                .setSmallIcon(R.drawable.ic_launcher_foreground)
                .setPriority(NotificationCompat.PRIORITY_HIGH)

            notify(NOTIFICATION_ID, build.build())

        }

    }

}

Step 6: メインの Activity の作成

最後に、以下のようにMainActivityを作成します。

MainActivity.ktを作成します。

package com.example.alarmclock

import android.app.AlarmManager
import android.app.PendingIntent
import android.app.TimePickerDialog
import android.content.Context
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import android.widget.TextView
import java.util.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        initOnOffButton()
        initChangeAlarmTimeButton()

        val model = fetchDataFromSharedPreferences()
        renderView(model)

    }

    private fun initOnOffButton() {
        val onOffButton = findViewById<Button>(R.id.onOffButton)
        onOffButton.setOnClickListener {

            val model = it.tag as? AlarmDisplayModel ?: return@setOnClickListener
            val newModel = saveAlarmModel(model.hour, model.minute, model.onOff.not())
            renderView(newModel)

            if (newModel.onOff) {
                // 켜진 경우 -> 알람을 등록
                val calendar = Calendar.getInstance().apply {
                    set(Calendar.HOUR_OF_DAY, newModel.hour)
                    set(Calendar.MINUTE, newModel.minute)

                    if (before(Calendar.getInstance())) {
                        add(Calendar.DATE, 1)
                    }
                }

                val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
                val intent = Intent(this, AlarmReceiver::class.java)
                val pendingIntent = PendingIntent.getBroadcast(this, ALARM_REQUEST_CODE,
                    intent, PendingIntent.FLAG_UPDATE_CURRENT)

                alarmManager.setInexactRepeating(
                    AlarmManager.RTC_WAKEUP,
                    calendar.timeInMillis,
                    AlarmManager.INTERVAL_DAY,
                    pendingIntent
                )

            } else {
                cancelAlarm()
            }

        }
    }

    private fun initChangeAlarmTimeButton() {
        val changeAlarmButton = findViewById<Button>(R.id.changeAlarmTimeButton)
        changeAlarmButton.setOnClickListener {

            val calendar = Calendar.getInstance()
            TimePickerDialog(this, { picker, hour, minute ->

                val model = saveAlarmModel(hour, minute, false)
                renderView(model)
                cancelAlarm()

            }, calendar.get(Calendar.HOUR_OF_DAY), calendar.get(Calendar.MINUTE), false).show()

        }

    }

    private fun saveAlarmModel(
        hour: Int,
        minute: Int,
        onOff: Boolean
    ): AlarmDisplayModel {
        val model = AlarmDisplayModel(
            hour = hour,
            minute = minute,
            onOff = onOff
        )

        val sharedPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)

        with(sharedPreferences.edit()) {
            putString(ALARM_KEY, model.makeDataForDB())
            putBoolean(ONOFF_KEY, model.onOff)
            commit()
        }

        return model
    }

    private fun fetchDataFromSharedPreferences():AlarmDisplayModel {
        val sharedPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE)

        val timeDBValue = sharedPreferences.getString(ALARM_KEY, "9:30") ?: "9:30"
        val onOffDBValue = sharedPreferences.getBoolean(ONOFF_KEY, false)
        val alarmData = timeDBValue.split(":")

        val alarmModel = AlarmDisplayModel(
            hour = alarmData[0].toInt(),
            minute = alarmData[1].toInt(),
            onOff = onOffDBValue
        )

        // 보정 보정 예외처리

        val pendingIntent = PendingIntent.getBroadcast(this, ALARM_REQUEST_CODE, Intent(this, AlarmReceiver::class.java), PendingIntent.FLAG_NO_CREATE)

        if ((pendingIntent == null) and alarmModel.onOff) {
            // 알람은 꺼져있는데, 데이터는 켜저있는 경우
            alarmModel.onOff = false

        } else if ((pendingIntent != null) and alarmModel.onOff.not()){
            // 알람은 켜져있는데, 데이터는 꺼져있는 경우
            // 알람을 취소함
            pendingIntent.cancel()
        }

        return alarmModel

    }

    private fun renderView(model: AlarmDisplayModel) {
        findViewById<TextView>(R.id.ampmTextView).apply {
            text = model.ampmText
        }

        findViewById<TextView>(R.id.timeTextView).apply {
            text = model.timeText
        }

        findViewById<Button>(R.id.onOffButton).apply {
            text = model.onOffText
            tag = model
        }

    }

    private fun cancelAlarm() {
        val pendingIntent = PendingIntent.getBroadcast(this, ALARM_REQUEST_CODE, Intent(this, AlarmReceiver::class.java), PendingIntent.FLAG_NO_CREATE)
        pendingIntent?.cancel()
    }

    companion object {
        private const val SHARED_PREFERENCES_NAME = "time"
        private const val ALARM_KEY = "alarm"
        private const val ONOFF_KEY = "onOff"
        private const val ALARM_REQUEST_CODE = 1000

    }
}

ステップ 7: 実行

最後にプロジェクトを実行します。

Reference

コードの参考リンクを紹介します。

番号 リンク
1.|コードのダウンロード

例2: アラームの起動方法

そのような携帯電話のようなソフトウェアアプリケーションの一つが「アラーム」です。あるいは、未来に何かが起こることをスケジュールできるアプリです。これは、デスクトップアプリケーションよりもモバイル機器ではさらに重要です。

なぜなら、私たちはモバイル機器を放置したり、電源を切ったりすることはありません。モバイル機器は、私たちのパーソナルアシスタントです。そのため、デスクトップアプリケーションよりもパーソナルな使い方をします。

そこでAndroidでは、AlarmManagerというリッチなクラスを提供しています。これは、システムサービスにアクセスするためのクラスです。このクラスは明らかにパブリックで、java.lang.Objectから派生しています。

これがその定義です。

    public class AlarmManager extends Object{}

何を作るの?

それでは、簡単なアンドロイドの AlarmManager の例を見てみましょう。アンドロイドでアラームを開始したり、キャンセルしたりする方法を見てみましょう。基本的なエディットテキストがあります。ユーザーはアラームが鳴るまでの時間をミリ秒単位で入力します。アラームが鳴ると、シンプルな Toast メッセージが表示されます。

プロジェクトの構成

プロジェクトの構成は以下の通りです。

Step 1 - Android プロジェクトの作成

  • アンドロイドスタジオで、File -- New -- New Project を選択します。
  • プロジェクト名を入力します。
  • 最小限のSDKを選択します。
  • テンプレートから空のアクティビティまたはブラを選択します。

Step 2. - 2. build.gradle を修正しましょう。

これが2つ目のステップです。android studioで作成したAndroidプロジェクトには、2つのbuil.gradleファイルがあります。ここでは、アプリレベルの build.gradle を対象とします。

dependenciesセクションに以下のコードを追加します。

        implementation 'com.android.support:appcompat-v7:24.2.1'
        implementation 'com.android.support:design:24.2.1'

サポートライブラリから2つの依存関係を追加しています。AppCompat "と "design "です。

ステップ3. リソースを準備しましょう。

ここでは、レイアウトだけを用意します。私はテンプレートとしてベーシックなアクティビティを選びました。そこで、2つのレイアウトを用意しました。

  • activity_main.xm : テンプレートレイアウト
  • content_main.xml : これを修正します。

必要なのは、1つのエディットテキストと1つのボタンを追加することだけです。エディットテキストには、ユーザーが時間を秒単位で入力し、スタートボタンでアラームを開始します。

     <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout

        android_layout_width="match_parent"
        android_layout_height="match_parent"
        android_paddingBottom="@dimen/activity_vertical_margin"
        android_paddingLeft="@dimen/activity_horizontal_margin"
        android_paddingRight="@dimen/activity_horizontal_margin"
        android_paddingTop="@dimen/activity_vertical_margin"
        app_layout_behavior="@string/appbar_scrolling_view_behavior"
        tools_context="com.tutorials.hp.alarmmanagerstarter.MainActivity"
        tools_showIn="@layout/activity_main">

        <EditText
            android_id="@+id/timeTxt"
            android_layout_width="wrap_content"
            android_layout_height="wrap_content"
            android_layout_alignParentLeft="true"
            android_layout_alignParentTop="true"
            android_layout_marginTop="28dp"
            android_ems="10"
            android_hint="Number of seconds"
            android_inputType="numberDecimal" />

        <Button
            android_id="@+id/startBtn"
            android_layout_width="wrap_content"
            android_layout_height="wrap_content"
            android_layout_alignRight="@+id/timeTxt"
            android_layout_below="@+id/timeTxt"
            android_layout_marginRight="60dp"
            android_layout_marginTop="120dp"
            android_text="Start" />
    </RelativeLayout>

ステップ4. それでは、BroadcastReceiverクラスを作成しましょう。

BroadcastReceiverは、アンドロイドのコンポーネントのひとつです。BroadcastReceiverはアンドロイドコンポーネントの一つで、他には Activity, Service, ContentProvider があります。
BroadcastReceiver`はシステムのイベントを受信します。

これは実際には抽象クラスで、明らかに公開されています。これは java.lang.Object から派生したものです。

    public abstract class BroadcastReceiver extends Object{}

sendBroadcast()`によって送信されたインテントは、この基本クラスによって受信されます。

このクラスは抽象クラスなので、onReceive()メソッドをオーバーライドします。

まず、javaクラスを作成します。

    public class MyReceiver{}

次のようなインポートを追加します。

    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.widget.Toast;

このクラスは android.content.BroadcastReceiver から派生しています。

    public class MyReceiver extends BroadcastReceiver {}

これで強制的に onReceive() メソッドをオーバーライドすることになります。

        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(context, "Alarm Ringing...", Toast.LENGTH_SHORT).show();
        }

Step 5. それでは、MainActivityクラスの説明をします。

アクティビティは、ユーザーインターフェースを表すアンドロイドのコンポーネントです。アクティビティから派生してアクティビティを作成します。より多くのデバイスをサポートするために、AppCompatActivityを使用します。

それでは、アクティビティを作ってみましょう。

    public class MainActivity extends AppCompatActivity {}

アクティビティの上に以下のインポートを追加します。

    import android.app.AlarmManager;
    import android.app.PendingIntent;
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.design.widget.FloatingActionButton;
    import android.support.design.widget.Snackbar;
    import android.support.v7.app.AppCompatActivity;
    import android.support.v7.widget.Toolbar;
    import android.view.View;
    import android.widget.Button;
    import android.widget.EditText;
    import android.widget.Toast;

アクティビティには3つのメソッドと2つのフィールドがあります。

まず、2つのフィールドを定義します。基本的には、ボタンとエディットテキストです。それらを MainActivity クラスの中に追加します。

        Button startBtn;
        EditText timeTxt;

次に、メソッド go() を作成します。このメソッドは、AlarmManagerの初期化とアラームの開始を担当します。

        private void go()
        {
            //GET TIME IN SECONDS AND INITIALIZE INTENT
            int time=Integer.parseInt(timeTxt.getText().toString());
            Intent i=new Intent(this,MyReceiver.class);

            //PASS CONTEXT,YOUR PRIVATE REQUEST CODE,INTENT OBJECT AND FLAG
            PendingIntent pi=PendingIntent.getBroadcast(this,0,i,0);

            //INITIALIZE ALARM MANAGER
            AlarmManager alarmManager= (AlarmManager) getSystemService(ALARM_SERVICE);

            //SET THE ALARM
            alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+(time*1000),pi);
            Toast.makeText(MainActivity.this, "Alarm set in "+time+" seconds", Toast.LENGTH_SHORT).show();
        }

次に、ボタンとedittextを初期化し、ボタンのonclickリスナーを処理するメソッドを作成します。

        private void initializeViews()
        {
            timeTxt= (EditText) findViewById(R.id.timeTxt);
            startBtn= (Button) findViewById(R.id.startBtn);

            startBtn.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                   go();
                }
            });
        }

Step 6. AndroidManifest`をチェックしよう

androidmanifest.xmlを確認します。マニフェストに BroadcastReceiver` クラスが登録されていることを確認しましょう。

broadcastreceiver`クラスが登録されていることがわかります。

            <receiver android_name="MyReceiver" >
            </receiver>

私の場合は次のようになっています。

    <?xml version="1.0" encoding="utf-8"?>
    <manifest
        package="com.tutorials.hp.alarmmanagerstarter">
        <application
            android_allowBackup="true"
            android_icon="@mipmap/ic_launcher"
            android_label="@string/app_name"
            android_supportsRtl="true"
            android_theme="@style/AppTheme">
            <activity
                android_name=".MainActivity"
                android_label="@string/app_name"
                android_theme="@style/AppTheme.NoActionBar">
                <intent-filter>
                    <action android_name="android.intent.action.MAIN" />

                    <category android_name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
            <receiver android_name="MyReceiver" >
            </receiver>
        </application>

    </manifest>

Example: Android AlarmManager - Toast の表示をスケジュールする

Androidエンジニアは、APIレベル1でAlarmManagerクラスを追加しましたので、アンドロイドの初期から存在しています。このクラスを使うと、将来行われるであろう操作をスケジューリングすることができます。AlarmManager`では、将来実行されるコードを設定することができます。

このコードが実行されるためにアプリが起動している必要がないというのは素晴らしいことです。もちろん、アプリは起動されますが、登録された時間にのみ実行されます。Alarm Maanagerは、android.appパッケージに属し、java.lang.Object.Alarmを継承しています。

端末がスリープ状態であっても、登録されている限りアラームは保持されます。AlarmManagerについての詳細はこちらをご覧ください。

スクリーンショット
  • 以下はプロジェクトのスクリーンショットです。

この例でよくある質問を説明します。

  • アンドロイドの AlarmManager の使い方。
  • AlarmManager とは何ですか?
  • アンドロイドの AlarmManager とは?
  • Toast を使った簡単な AlarmManager の例です。

使用したツール

このサンプルは、以下のツールを使って作成しました。

  • Windows 8
  • アンドロイドスタジオ IDE
  • Genymotion エミュレータ

ソースコード

それでは早速、ソースコードを見てみましょう。

Build.Gradle

  • 通常、アンドロイドプロジェクトでは、2つの build.gradle ファイルがあります。1つはアプリレベルの build.gradle で、もう1つはプロジェクトレベルの build.gradle です。appレベルはappフォルダ内にあり、通常はここに依存関係を追加し、コンパイルとターゲットのsdkを指定します。
  • また、AppCompatやデザインサポートライブラリの依存関係も追加します。
  • MainActivity "は "AppCompatActivity "から派生したもので、デザインサポートライブラリの "FloatingActionButton "も使用しています。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 24
    buildToolsVersion "25.0.1"

    defaultConfig {
        applicationId "com.tutorials.hp.alarmmanagerstarter"
        minSdkVersion 15
        targetSdkVersion 24
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    testImplementation 'junit:junit:4.12'
    implementation 'com.android.support:appcompat-v7:24.2.1'
    implementation 'com.android.support:design:24.2.1'
}

MyReceiver.java"

  • 私たちの Broadcast Receiver クラスです。
  • このクラスは、android.app.content.BroadCastReceiverを継承しています。
  • そして、OnReceive()メソッドをオーバーライドします。ここには、アラームが鳴ったときに実行されるコードを書きます。
  • ここでは、単純に Toast メッセージを表示します。

package com.tutorials.hp.alarmmanagerstarter;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class MyReceiver extends BroadcastReceiver {

    /*
    RING ALARM WHEN IN WHEN WE RECEIVE OUR BROADCAST
     */
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Alarm Ringing...", Toast.LENGTH_SHORT).show();
    }
}

MainActivity.java

  • ランチャーアクティビティです。
  • ActivityMain.xml をこのアクティビティのコンテンツビューとしてインフレーションします。
  • このアクティビティ内のビューとウィジェットを初期化します。
  • また、AlarmManagerオブジェクトを使用して、このアクティビティ内でアラームの初期化と開始を行います。

package com.tutorials.hp.alarmmanagerstarter;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    Button startBtn;
    EditText timeTxt;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

         initializeViews();

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });

    }

    /*
    INITIALIZE VIEWS
     */
    private void initializeViews()
    {
        timeTxt= (EditText) findViewById(R.id.timeTxt);
        startBtn= (Button) findViewById(R.id.startBtn);

        startBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
               go();
            }
        });
    }

    /*
    INITIALIZE AND START OUR ALARM
     */
    private void go()
    {
        //GET TIME IN SECONDS AND INITIALIZE INTENT
        int time=Integer.parseInt(timeTxt.getText().toString());
        Intent i=new Intent(this,MyReceiver.class);

        //PASS CONTEXT,YOUR PRIVATE REQUEST CODE,INTENT OBJECT AND FLAG
        PendingIntent pi=PendingIntent.getBroadcast(this,0,i,0);

        //INITIALIZE ALARM MANAGER
        AlarmManager alarmManager= (AlarmManager) getSystemService(ALARM_SERVICE);

        //SET THE ALARM
        alarmManager.set(AlarmManager.RTC_WAKEUP, System.currentTimeMillis()+(time*1000),pi);
        Toast.makeText(MainActivity.this, "Alarm set in "+time+" seconds", Toast.LENGTH_SHORT).show();
    }

}

アクティビティメイン.xml

  • テンプレートレイアウトです。
  • ContentMain.xmlを含みます。
  • また、appbarlayout、ツールバー、floatingaction butttonも定義しています。

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout

    android_layout_width="match_parent"
    android_layout_height="match_parent"
    android_fitsSystemWindows="true"
    tools_context="com.tutorials.hp.alarmmanagerstarter.MainActivity">

    <android.support.design.widget.AppBarLayout
        android_layout_width="match_parent"
        android_layout_height="wrap_content"
        android_theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android_id="@+id/toolbar"
            android_layout_width="match_parent"
            android_layout_height="?attr/actionBarSize"
            android_background="?attr/colorPrimary"
            app_popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton
        android_id="@+id/fab"
        android_layout_width="wrap_content"
        android_layout_height="wrap_content"
        android_layout_gravity="bottom|end"
        android_layout_margin="@dimen/fab_margin"
        android_src="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

ContentMain.xml

  • コンテンツのレイアウトです。
  • MainActivity の内部に表示されるビューやウィジェットを定義します。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout

    android_layout_width="match_parent"
    android_layout_height="match_parent"
    android_paddingBottom="@dimen/activity_vertical_margin"
    android_paddingLeft="@dimen/activity_horizontal_margin"
    android_paddingRight="@dimen/activity_horizontal_margin"
    android_paddingTop="@dimen/activity_vertical_margin"
    app_layout_behavior="@string/appbar_scrolling_view_behavior"
    tools_context="com.tutorials.hp.alarmmanagerstarter.MainActivity"
    tools_showIn="@layout/activity_main">

    <EditText
        android_id="@+id/timeTxt"
        android_layout_width="wrap_content"
        android_layout_height="wrap_content"
        android_layout_alignParentLeft="true"
        android_layout_alignParentTop="true"
        android_layout_marginTop="28dp"
        android_ems="10"
        android_hint="Number of seconds"
        android_inputType="numberDecimal" />

    <Button
        android_id="@+id/startBtn"
        android_layout_width="wrap_content"
        android_layout_height="wrap_content"
        android_layout_alignRight="@+id/timeTxt"
        android_layout_below="@+id/timeTxt"
        android_layout_marginRight="60dp"
        android_layout_marginTop="120dp"
        android_text="Start" />
</RelativeLayout>

実行するには

  1. 上記プロジェクトをダウンロードします。
  2. ZIPファイルが出てきますので、それを解凍します。
  3. Android Studioを開きます。
  4. すでに開いているプロジェクトを閉じます。
  5. メニューバーの「ファイル」→「新規作成」→「プロジェクトのインポート」をクリックします。
  6. プロジェクトをインポートする先のフォルダを選択します。
  7. Androidプロジェクトを選択します。
  8. OK "をクリックします。
  9. これで、プロジェクトのインポートが完了しました。

おわりに

今回は、簡単なアンドロイドの AlarmManager の例を見ました。

Example: アンドロイドの繰り返し/反復アラームのスケジュール

AlarmManager "はAndroidの初期から存在しています。これにより、将来のタスクをスケジュールすることができます。

AlarmManager`を使うと、将来実行されるコードを設定することができます。

AlarmManagerは、android.appパッケージに属し、java.lang.Objectを継承しています。デバイスがスリープ状態であっても、アラームは登録されている限り保持されます。今回の例では,周期的に繰り返されるアラームを扱う方法を見てみましょう。

与えられた秒数の後にToastメッセージの表示をスケジュールします。例えば、ユーザが edittexts に 5 を入力して start ボタンをクリックすると、5 秒ごとに Toast メッセージが表示され、ユーザが cancel ボタンをクリックしてアラームをキャンセルするまで、表示されます。このようにして、アラームの繰り返しを開始し、それをキャンセルする方法を見ることができます。AlarmManagerの詳細はこちらを参照してください。

この例でよくある質問

  • アンドロイドの AlarmManager の使い方。
  • アンドロイドで繰り返し/反復するアラームを設定し、それらをキャンセルする方法。
  • アンドロイドでアラームを開始したり、キャンセルしたりする方法。
  • アンドロイドで将来行われる作業をスケジュールするには?
  • 簡単な繰り返しの AlarmManager の例として、Toast があります。

使用したツール

このサンプルは、以下のツールを使って作成しました。

  • Windows 8
  • AndroidStudio IDE
  • Genymotion エミュレータ
  • 言語 : Java
  • Topic : Android Recurring Alarms, AlarmManager, Start Cancel Repeating alarms
  • 使用したライブラリ
  • サードパーティ製のライブラリは一切使用していません。

ソースコード

ソースコードを見てみましょう。

Build.Gradle

通常、アンドロイドプロジェクトでは、2つの build.gradle ファイルがあります。1つはアプリレベルの build.gradle で、もう1つはプロジェクトレベルの build.gradle です。appレベルはappフォルダ内にあり、通常はここに依存関係を追加したり、コンパイルおよびターゲットSDKを指定したりします。

また、AppCompatやデザインサポートライブラリの依存関係も追加します。
MainActivity "は "AppCompatActivity "から派生したもので、デザインサポートライブラリの "FloatingActionButton "も使用します。

apply plugin: 'com.android.application'

android {
    compileSdkVersion 26
    buildToolsVersion "26.0.0"
    defaultConfig {
        applicationId "com.tutorials.hp.repeatingalarm"
        minSdkVersion 15
        targetSdkVersion 26
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    androidtestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    implementation 'com.android.support:appcompat-v7:26.+'
    implementation 'com.android.support.constraint:constraint-layout:1.0.0-alpha7'
    implementation 'com.android.support:design:26.+'
    testImplementation 'junit:junit:4.12'
}

MyReceiver.java

  • 私たちの MyReceiver クラスです。
  • android.content.BroadcastReceiver から派生しています。
  • onReceive()メソッドをオーバーライドし、アラームが鳴ったときに行う処理をここで行います。

package com.tutorials.hp.repeatingalarm;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.widget.Toast;

public class MyReceiver extends BroadcastReceiver {
    /*
   RING ALARM WHEN IN WHEN WE RECEIVE OUR BROADCAST
    */
    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "Alarm Ringing...", Toast.LENGTH_SHORT).show();
    }
}

MainActivity.java

  • 私たちの MainActivity クラスです。
  • サポートライブラリのアクションバー機能を使用するアクティビティのベースクラスである AppCompatActivity から派生しています。
  • メソッド:onCreate(), initializeViews(), initializeAlarmManager(), go().
  • setContentView()メソッドを使用して、activity_main.xmlからインフレーションを行います。
  • 使用しているビューは、EditTextsbuttonsです。
  • findViewById()を使用して、レイアウト仕様からこれらを参照します。
  • AlarmManager を初期化します。
  • setInExactRepeating()`でアラームを開始します。

package com.tutorials.hp.repeatingalarm;

import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

    Button startBtn,cancelBtn;
    EditText timeTxt;
    AlarmManager alarmManager;
    PendingIntent pi;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
        setSupportActionBar(toolbar);

     initializeViews();
     initializeAlarmManager();

        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                go();
            }
        });

    }

    /*
    INITIALIZE VIEWS
     */
    private void initializeViews()
    {
        timeTxt= (EditText) findViewById(R.id.timeTxt);
        startBtn= (Button) findViewById(R.id.startBtn);
        cancelBtn= (Button) findViewById(R.id.cancelBtn);

        startBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                go();
            }
        });
        cancelBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                if(alarmManager != null)
                {
                    alarmManager.cancel(pi);
                }
            }
        });
    }
  /*
  INITIALIZE AND START OUR ALARM
  */
  private void initializeAlarmManager()
  {
    // INITIALIZE INTENT
    Intent intent=new Intent(this,MyReceiver.class);

        //PASS CONTEXT,YOUR PRIVATE REQUEST CODE,INTENT OBJECT AND FLAG
        pi= PendingIntent.getBroadcast(this,0,intent,0);

        //INITIALIZE ALARM MANAGER
        alarmManager= (AlarmManager) this.getSystemService(ALARM_SERVICE);
  }

    /*
    START OUR ALARM
     */
    private void go()
    {
        //GET TIME IN SECONDS
        int time=Integer.parseInt(timeTxt.getText().toString());

        //SET THE ALARM
       if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.CUPCAKE) {
            alarmManager.setInexactRepeating(AlarmManager.RTC_WAKEUP,System.currentTimeMillis()+(time*1000),time*1000,pi);
            Toast.makeText(MainActivity.this, "Alarm set in "+time+" seconds", Toast.LENGTH_SHORT).show();

        }else
        {
            alarmManager.setRepeating(AlarmManager.ELAPSED_REALTIME,System.currentTimeMillis()+(time*1000),time*1000,pi);
            Toast.makeText(MainActivity.this, "Yes Alarm set in "+time+" seconds", Toast.LENGTH_SHORT).show();

        }
    }

}

ActivityMain.xml を参照してください。

これは、MainActivityのテンプレート・レイアウトです。ルートとなるレイアウトタグはCoordinatorLayoutです。CoordinatorLayout`は、framelayoutをさらにパワーアップさせたビューグループです。

  • CoordinatorLayout は主に2つのユースケースを想定しています。トップレベルのアプリケーションの装飾またはクロームレイアウトとして 1つまたは複数の子ビューとの特定のインタラクションのためのコンテナとして。
  • CoordinatorLayout の中には、AppBarLayout、FloatingActionButton を追加し、content_main.xml をインクルードしています。
  • AppBarLayout は、マテリアルデザインコンセプトのスクロール機能を実装した、縦型の LinearLayout です。
  • CoordinatorLayout の直接の子でなければならず、そうでなければ多くの機能が動作しません。
  • AppBarLayout の中に、青い色をした toolbar を追加します。
  • これはテンプレートレイアウトなので、ここではなく、content_main.xmlにウィジェットを追加します。
  • これは、android.support.design.widget.VisibilityAwareImageButtonから派生したクラスです。これは、ユーザーインターフェイスで見られる丸いボタンです。

<?xml version="1.0" encoding="utf-8"?>

<android.support.design.widget.CoordinatorLayout

    android_layout_width="match_parent"
    android_layout_height="match_parent"
    tools_context="com.tutorials.hp.repeatingalarm.MainActivity">

    <android.support.design.widget.AppBarLayout
        android_layout_width="match_parent"
        android_layout_height="wrap_content"
        android_theme="@style/AppTheme.AppBarOverlay">

        <android.support.v7.widget.Toolbar
            android_id="@+id/toolbar"
            android_layout_width="match_parent"
            android_layout_height="?attr/actionBarSize"
            android_background="?attr/colorPrimary"
            app_popupTheme="@style/AppTheme.PopupOverlay" />

    </android.support.design.widget.AppBarLayout>

    <include layout="@layout/content_main" />

    <android.support.design.widget.FloatingActionButton
        android_id="@+id/fab"
        android_layout_width="wrap_content"
        android_layout_height="wrap_content"
        android_layout_gravity="bottom|end"
        android_layout_margin="@dimen/fab_margin"
        app_srcCompat="@android:drawable/ic_dialog_email" />

</android.support.design.widget.CoordinatorLayout>

ContentMain.xml

  • ContentMain.xml ファイルを作成します。
  • MainActivity にインフレーションされるものとします。
  • ルートタグは ConstraintLayout です。
  • EditTexts と2つのボタンが含まれています。
  • ユーザーは、アラームが鳴ってからの秒数をエディットテキストに入力します。
  • アラームを開始するには、スタートボタンをクリックします。
  • アラームをキャンセルするにはCancelボタンをクリックします。

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout

    android_layout_width="match_parent"
    android_layout_height="match_parent"
    app_layout_behavior="@string/appbar_scrolling_view_behavior"
    tools_context="com.tutorials.hp.repeatingalarm.MainActivity"
    tools_showIn="@layout/activity_main">

    <LinearLayout
        android_layout_width="368dp"
        android_layout_height="327dp"
        android_orientation="vertical"
        app_layout_constraintTop_toTopOf="parent"
        android_layout_marginTop="8dp">
        <EditText
            android_id="@+id/timeTxt"
            android_layout_width="wrap_content"
            android_layout_height="wrap_content"
            android_layout_alignParentLeft="true"
            android_layout_alignParentTop="true"
            android_ems="10"
            android_hint="Number of seconds"
            android_inputType="numberDecimal"
            />

        <Button
            android_id="@+id/startBtn"
            android_layout_width="match_parent"
            android_layout_height="wrap_content"
            android_layout_alignRight="@+id/timeTxt"
            android_layout_below="@+id/timeTxt"
            android_text="Start"
            />
        <Button
            android_id="@+id/cancelBtn"
            android_layout_width="match_parent"
            android_layout_height="wrap_content"
            android_layout_alignRight="@+id/timeTxt"
            android_layout_below="@+id/timeTxt"
            android_text="Cancel"
            />

    </LinearLayout>

</android.support.constraint.ConstraintLayout>

Video/Preview

  • YouTubeチャンネルには、約1000のチュートリアルがあり、以下のものはその一つです。

https://www.youtube.com/watch?v=23Gw-11JFqc

ダウンロード

  • 以下からプロジェクトの全容をダウンロードできます。ソースコードはしっかりとコメントされています。

ダウンロード