無料ブログはココログ

« 画面遷移(他のアクティビティの起動) | トップページ | アラームマネージャとCalendar »

2013年7月12日 (金)

インテントサービスで効果音を再生

 

PhotoPhoto_3
 サービスの一つである「インテントサービス」を実装した。
 インテントサービスは「サービス」という名前を冠しているため、サービスと同様に当初はWindowsのサービスのようなものだろうと想像していたのだが、実はそのようなものとはかなり異なると後になって分かった。
 要するにAndroidの「インテントサービス」は多少時間がかかる処理をバックグラウンドで実行することが目的らしい。そのため処理が終わると共にサービスは終了する(当初は「なぜサービスなのに勝手に終了するのか?」と疑問に思ったものだった)。つまり常駐することが目的なのではなく、時間のかかる処理をバックグラウンドで行うことが目的なのである。

 実は当面の開発テーマとして「薬の飲み忘れアプリ」を考えている(歳がバレるが‥)。アラームマネージャから定期的にプログラムを起動し、アラームを鳴らすべきタイミングの判定や音を鳴らす処理をインテントサービスにしようと思っている。そのため、インテントサービスの処理内容としては音を鳴らしてみることにした。

 まず元となるアクティビティを普通に作成する。アクティビティ側での起動処理は前回投稿の「画面遷移」での子のアクティビティの起動と良く似て、Mindで以下のように記述する。(サービスへのデータ渡しはまだ実装しておらず単純起動とした)

 <パッケージ名>と <クラス名>で インテントサービスを起動

 ちなみに子アクティビティの起動の時は以下であった。

 <パッケージ名>と <クラス名>で インテントを実行

後者では「・・を実行」だったものが前者では「・・を起動」となっており、呼び出し元は子の終わりを待たないことが想像できるようにした。

 処理単語「インテントサービスを起動」が実行されると Mind→C→Java へと制御が移行し最終的に以下のJavaコードが動作する。(これまた子アクティビティの起動と似たような記述となっている)

   intent = new Intent();
   intent.setClassName( pkgname, classname );
   this.startService(intent);

 1つのパッケージに主(アクティビティ)・副(インテントサービス)の二つを含めることになるのも画面遷移の時と類似であり、C記述の共有ライライブラリ(DLL)のAndroid環境下での構成は以下のようになる。

        (パッケージ化する前の元ファイル状態のうち主要なものだけを抜粋)

  assets
  |-- bin
  |   |-- euc-uni-encode.bin          ←Mindが使うUnicode変換テーブル
  |   `-- uni-euc-decode.bin          ←Mindが使うUnicode変換テーブル
  `-- mco
      |-- MyIntentService.mco         ←インテントサービスのMindオブジェクトコード
      `-- TestSoundService2.mco       ←メインアクティビティのMindオブジェクトコード

  bin
  |-- classes
  |   `-- jp
          `-- co
              `-- scriptslab
                  `-- TestSoundService2       ←パッケージ名
                      |-- MyIntentService.class   ←インテントサービスのJavaクラス
                      `-- TestSoundService2.class ←メインアクティビティのJavaクラス

  libs
  |-- armeabi
      |-- libdllker_MyIntentService.so    ←C記述DLL (Mindランタイムとディスパッチャ)
      `-- libdllker_TestSoundService2.so  ←C記述DLL (Mindランタイムとディスパッチャ)

  res
  |-- raw
  |   `-- se_maoudamashii_onepoint23.ogg    ←効果音を収めたファイル
                 ↑
                 著作:音楽素材/魔王魂 http://maoudamashii.jokersounds.com/

 JavaからC→Mindへと処理を移行する仕組み自体は前投稿の「画面遷移」と同じなのだが、作成したインテントサービスを実行してみたところ問題が発生した。2度目のサービス起動で落ちてしまうのである。
 原因は Java→C に渡される環境情報 env, thiz の2つの扱いだった。たとえば一番最初にCが呼び出される箇所は以下のようになっていた。

                 Mindカーネルの "dllstartup.c" (まずかったときの記述)

  static JNIEnv*      env_save;            ←単一の大域変数への退避はまずかった
  static jobject     thiz_save;
 
  jstring
  dllStartup_common( JNIEnv* env, jobject thiz, jstring pkgnamej,
                             jstring classnamej, jbyteArray mcobufferj )
  {
        ~略~
    env_save = env;
    thiz_save = thiz;
        ~略~

  JavaからCが呼び出されたとき、上記のように第1/第2引数で環境情報 env, thiz の2つが渡される。このあと延々とMindの処理に入るため、これらを一旦大域変数に格納し、Mind→Java の逆呼び出し時には、この大域変数を参照していたのだが、ボタン押下やサービス要求などのイベント発生時にこれを共用してはいけなかったのだ。
 JNIの本「JNI Java native Interface プログラミング(ロブ・ゴードン著、林秀幸訳、ピアゾン・エデュケーション)」によれば
     「JNIEnvポインタをstatic変数に入れてグローバルとして使用
      したくなるかもしれないが、そんな誘惑に負けてはいけない」
と書かれているのだが、まさにこれをやってしまっていた。
 対策として、この情報はJavaからC側に制御が移るごとにスタックに入れることで解決した(Cランタイムで隠蔽するのでMindからは見えない)。

 次にインテントサービス内でアラームを鳴らす方法だが、クラス SoundPool または MediaPlayer を使う方法がよく取り上げられている。効果音のような短いものは前者が使われるようだが、試しに MediaPlayer を使ってみた。
 MediaPlayerはそのコンストラクタで音源を指定する方法がよく書かれているが、Mindでの記述統一(たとえばレイアウトやビューと同じような割り当てを使いたかった)のため、コンストラクタでは音源は指定せず、MediaPlayer#setDataSource のメソッドで明に指定するようにしたが、この方法では音を出すのに都合3段階が必要となる。
 Mindでの記述は以下のようになる。

  インテントサービス処理とは 本定義 (・ → ・)
                  _音源パスは 文字列定数 「raw/se_maoudamashii_onepoint23」
                  _プレイヤー1は プレイヤー
          メディアプレイヤーを生成し 偽?
                  ならば <~略~>
                          終り
                  つぎに
          _プレイヤー1に 入れ
          _音源パスと _プレイヤー1で メディアプレイヤーにロードし 偽?
                  ならば <~略~>
                          終り
                  つぎに
          100と 0を _プレイヤー1で メディアプレイヤーを演奏し 偽?
                  ならば <~略~>
                          終り
                  つぎに。

 MediaPlayerにローカルファイル(たとえば raw/ 配下のファイル)で音源を指定する時のURIは

        android.resource://raw/se_maoudamashii_onepoint23

のようになるのだが、記述が煩雑であるためMindのライブラリ側で補完を行うことにより、アプリケーション側では短形式として

        raw/se_maoudamashii_onepoint23

で済むようにした。

      ( 注:試した音源は 著作:音楽素材/魔王魂 http://maoudamashii.jokersounds.com/ )





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

« 画面遷移(他のアクティビティの起動) | トップページ | アラームマネージャとCalendar »

Android」カテゴリの記事

コメント

今回は自分が使いこなすのに苦労したインテントなので、大変興味深く読みました。

Androidの手法がMindの世界に飲み込まれていく様子がよくわかって、またまた次回も楽しみです(プレッシャー?)。

かのさん
コメントありがとうございます。
前回の投稿から日が経ちましたがサービス周りで結構ハマっていました。
JNIEnvの扱いは書いたとおりなのですが、そもそもインテントサービスとサービスの使い分けがよくわからず、あちこち調べていたらあっと言うまに日が経ちました(^^;

残暑お見舞い申し上げます。

Linux版Mindを新しいマシンにインストールを試みましたが、エラーがでます。ツール類がインストールされていない可能性もありますが、コンパイルがうまく行きません(下記)。
OSは、CentOS6.3 64ビット版です。Mindユーザを代表して、よろしくお願い申し上げます。

cc -m32 -DLINUX -D_GNU_SOURCE -DFTIME_SUPPORTED -Wall -O6 -funsigned-char -ansi \
kernel.o \
startup.o osfunc.o \
-o kernel -lm -ldl
/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-redhat-linux/4.4.7/libgcc_s.so when searching for -lgcc_s
/usr/bin/ld: skipping incompatible /usr/lib/gcc/x86_64-redhat-linux/4.4.7/libgcc_s.so when searching for -lgcc_s
/usr/bin/ld: cannot find -lgcc_s
collect2: ld はステータス 1 で終了しました
make: *** [kernel] エラー 1

おぐさん、こんにちは。
Linux版Mindのご質問ですね、
せっかくご質問いただいたのにお手数をおかけしますが、

Mind掲示板
http://www.scripts-lab.co.jp/mind/enterbbs.html

のほうで改めて同文の質問を上げていただけないでしょうか。
掲示板のほうはここ何年か開店休業状態でして大変申し訳ありません。
これを機に今後はきちんと対応していきたいと思います。

では。

コメントを書く

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

トラックバック

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

この記事へのトラックバック一覧です: インテントサービスで効果音を再生:

« 画面遷移(他のアクティビティの起動) | トップページ | アラームマネージャとCalendar »