無料ブログはココログ

« インテントサービスで効果音を再生 | トップページ | ピッカーダイアログによる日時の入力 »

2013年8月21日 (水)

アラームマネージャとCalendar

Alarmmgrtop_2

 Android APIにはアラームマネージャ(AlarmManager)というクラスがあり、指定した時刻や指定した時間間隔で他のアクティビティやサービスを起動することができる。
 アラームマネージャでは時刻を表すオブジェクトを扱うことになるが、それは別途、Calendarクラスの機能であるため、まず先にCalendarの機能を実装した。

 以下はJavaレベルでCalendarクラスの使い方の例である。

    1. Calendarクラスのインスタンスを生成
       →例: Calendar cal = Calendar.getInstance();

    2. カレンダー要素を取り出すメソッドを使って値を得る
       →例: int year = cal.get(Calendar.YEAR);

  Mindから利用する場合、1と2は一体処理するのが良い。1で得られたJavaのインスタンスをMindに返しても使いように困るからである。さらに、2において年だけ、月だけ・・をMind→Javaの呼び出し毎に返す仕様ではやはりCalendarインスタンスの維持の問題が生じる。

  注:レイアウト/ビュー/メディアプレイヤーなどではJavaのインスタンスを
        Mindもかかわって維持管理している。これらは駆動が複雑なので一体処理
    することができないという理由からである。

 これを解決するのに、前の1と2を一体処理しかつJava側で年月日時分秒をまとめて得てからC(Mind)側に返す方法があり、Mind→Javaの呼び出し回数も1度で済む。しかし「Java側で年月日時分秒をまとめたオブジェクト」とC(およびMind)での構造体との間でフォーマット変換が必要で、これがオーバーヘッドになるのが気になった。
 今回はもう1つの方法として、年月日時分秒をパックして整数値(Javaで言うlong)を1つ返し、Mindのライブラリ内で分解して日時構造体に格納することにした。整数はJava←→Cでダイレクト引渡しのためフォーマット変換が不要になる。それに比べたらJavaでのパックとMindでのアンパックの処理時間などわずかであろう。

 余談になるが、Javaも含め関数型言語はこのようなとき不便だ。MindやForthといったスタック型言語であれば単純に年月日時分秒を表す6個の整数を積んで返せば済むのだが、関数型言語では戻り値は1つなので、6個のデータを返すのに(オーバーヘッドの少ない)整数などプリミティブ要素を使って返すことができない。結果的に「戻したい値の数」が1個なのかあるいはそれより多いのかで文法が大きく変わってしまう。

 さて日時情報を得る手段は次のものが標準Mindに元から備わっていてAndroid環境でも動作する。

                      (Mind標準仕様のもの)

        <日時構造体>をつかい 日時を得る → ・

          (上の余談と矛盾するがMind仕様で日時情報は構造体に格納し返す。
                もちろん6個のデータをスタック積みで返す方法も考えられる)

 上を実行すると指定した構造体の各要素に年月日が格納される。
 Android API(Calendarクラス)を使う場合も最終目的は同じなので、Mindでの仕様は標準仕様と似させて以下のようにした。。

                      (Android仕様のもの)

        <日時構造体>をつかい システム日時をカレンダーで得る → ・


 上記両者共通に使う日時構造体(型紙)は標準Mindに元からあるものを少し訂正し以下になった。

        日時型は   型紙
                        年は   変数
                        月は   変数
                        日は   変数
                        曜日は  変数
                日付情報は 年と 月と 日と 曜日
                        時は   変数
                        分は   変数
                        秒は   変数          ←この次にミリ秒を含めるか要検討
                時刻情報は 時と 分と 秒
          全体は 日付情報と 時刻情報。

 以上のように、Android API(Calendarクラス)を使った日時の参照と、LinuxのAPIによる日時の参照は機能として類似のものとなる。細かなことを言えば、前者はミリ秒の情報が得られるなど違いは有るのだが、おおむね似た機能と言える。
 今回の実装でこの二つの似た機能をどのように扱うか考えたが結局、両手段をともに設置することにした。当然だが Linux API版であってもAndroid API版であっても同じ値が得られる。

 日時を年月日時分秒の要素で得る方法ではなく、単一値として得る手段もある。こちらも標準Mindで(つまり Linux API)元から有り、以下のようなものでもちろんAndroid環境でも動作する。

        日時を値で得る → <秒>
        日時とミリ秒を値で得る → <秒>、<ミリ秒>      ←これは最近の拡張

 AndroidネイティブAPIを使う機能は以下の文法とした。

        システム日時を値で得る → <通算ミリ秒>

 上記両者は目的は同じようなものだが計算手段が異なるので、返されるミリ秒の値については互換性は無い。
 なお年月日時分秒を個別に取得するほうの「システム日時をカレンダーで得る」であるが、Android APIで本来得られるミリ秒値をMind側の都合で無視してしまった。秒未満の値については上記で代替できるのではないかと思っているのだが今後の開発で変更する可能性もある。

 Calendarクラスにはこの他、日時要素への加算・減算や全体として整合をとる機能などがある。たとえば「日」3だけ増加させたとき31日を超えてしまうことがあるが、次に新しい要素値を参照したとき自動的に正規化(ここでは「月」への繰上げ)してくれる。
 しかしこの機能もまた標準Mind(Linux API)にほぼ類似のものが有る。以下のものである。

        <日時構造体>を 日時を正規化 → <通算ミリ秒>

 上記により日時構造体に格納された要素が正しい値に補正されるので、手順は異なるもののAndroid APIとほぼ同じ機能と言える。これがあるため、Android APIが提供する要素の加算・減算(いずれも正規化を伴う)は今回はMind側に実装しなかった。当面様子を見ることにする。
 ちなみに、Android APIでは日時要素への加算・減算をおこなうメソッドが多数定義されているが、標準Mind(Linux API)では日時要素が構造体メンバーであり直接アクセス可能のため、加算・減算をおこなうための手段は必要無い。(正規化をおこなう処理単語のみが提供される)

        注:ここまでローカルタイムの話である。タイムゾーンは追って実装する必要がある。

 次にアラームマネージャについて。
 Mindレベルでは以下の処理単語を用意した。単語名はあちこちにあるAndroidの日本語解説で登場する呼称そのままなので、これが何なのかは理解しやすいと思う。

        <パッケージ名>、<クラス名>、
          <ユニークID>、<開始ミリ秒>
                電源ONからの経過時間でアラーム開始・スリープ解除 → 真偽

        <パッケージ名>、<クラス名>、
          <ユニークID>、<開始ミリ秒>
                時刻指定でアラーム開始 → 真偽

        <パッケージ名>、<クラス名>、
          <ユニークID>、<開始ミリ秒>
                時刻指定でアラーム開始・スリープ解除 → 真偽

        <パッケージ名>、<クラス名>、
          <ユニークID>、<開始ミリ秒>、<間隔ミリ秒>
                正確な一定間隔でアラーム開始 → 真偽

        <パッケージ名>、<クラス名>、
          <ユニークID>、<開始ミリ秒>、<間隔ミリ秒>
                一定間隔でアラーム開始 → 真偽

        <パッケージ名>、<クラス名>、
          <ユニークID>
                アラーム停止 → 真偽

  指定した時間になったらアクティビティやサービスを起動するのだが、これら外部プログラムの起動周りのことは記事「画面遷移(他のアクティビティの起動)」と同様の考えなのでそちらも参考にして欲しい。

 アラームマネージャの単語群のうちひとつの利用例は以下の通り。(一定間隔でインテントサービスを起動し、そちらで音を鳴らすというもの)

                _開始ミリ秒は 数値変数

        ~略~

        システム日時を値で得て
                 10000ミリを ミリ秒を加え _開始ミリ秒に 入れ

        私のパッケージ名と "MyIntentService"と 0をつみ (=UniqueID)
         _開始ミリ秒をつみ (=開始時刻)
          10000ミリをつみ (=間隔)
            一定間隔でアラーム開始し






参考URL: 「日本語プログラミング言語Mind」 (スクリプツ・ラボ)

« インテントサービスで効果音を再生 | トップページ | ピッカーダイアログによる日時の入力 »

Android」カテゴリの記事

コメント

戻り値が1個という制限ってCやJavaを使っている人には当たり前で、制限という感覚すらないでしょうね。

「をつかい」という送り仮名、リギーにお世話になっていたころに構造体の周りを設計していたときのことを懐かしく思い出しました。(^^)

かのさん、
コメントありがとうございます。
詳しく読んでいただいたようで恐縮です。
そうですね、確かに昔、「関数型言語は1つしか返せない」という制限を人に話して「なるほど」と言われたことはあまり無かったような気がします。
「をつかい」は傑作な表記でしたね(^^)

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/100109/58035212

この記事へのトラックバック一覧です: アラームマネージャとCalendar:

« インテントサービスで効果音を再生 | トップページ | ピッカーダイアログによる日時の入力 »