T

TitaniumでAndroidのServiceを

マニュアル通りにやっても動かなかったので、メモしておきます。

AndroidのServiceについては別の資料をご覧頂くとして(常駐アプリみたいなものです)、その起動や停止をTitanium Mobileから制御する方法について説明します。1.8.2で動作確認しています。

Serviceを利用するにはJSに記述する以外にも、tieapp.xmlに追記する必要があります。

<android xmlns:android="http://schemas.android.com/apk/res/android">
        <services>
            <service type="interval" url="myservice.js"/>
        </services>
</android>

これは、myservice.jsというファイルに記述されたServiceを指定された間隔(interval)で起動する、という意味になります。tieapp.xmlにこれを指定すると、build/android/gen/APP_ID/MyServiceService.javaというファイルが生成されます。中身はTiJSIntervalServiceを継承したMyserviceServiceクラスの実装で、build/android/AndroidManifest.xmlに

<service  android:name="APP_ID.MyserviceService" />

が追記されていれば成功です。APP_IDのところは実際には先ほどの生成されたJavaのファイルにも記述されているパッケージ名になります。

ところが、いろいろと作業する内にtieapp.xmlを編集するだけでは上のようにはならず、

<service  android:name=".MyserviceService" />

のようになったことがありました。困ったことに、一度こうなるとそのまま変更されなくなってしまうようです。これでは当然動作しないので、一度ビルドしてbuild/android/AndroidManifest.xmlが作成されたら、プロジェクトディレクトリ直下にplatform/androidディレクトリを作成し、AndroidManifest.xmlをコピーします。

$ mkdir -p PROJECT_HOME/platform/android
$ cp PROJECT_HOME/build/android/AndroidManifest.xml PROJECT_HOME/platform/android/

platform/android/AndroidManifest.xmlに記述された内容はtieapp.xmlの情報とマージされて、build/android/AndroidManifest.xml.genとなります。platform以下のXMLの方が優先されるみたいです。なので、これを編集して

<service  android:name="APP_ID.MyserviceService" />

という行を追加します(しつこいようですが、APP_IDはパッケージ名です)。

次に、myservice.jsを用意するわけですが、XMLの方には記述されていませんが設置するのはResources直下ではなく、Resources/android以下になります。もし起動時にデータを受け渡しする場合はintentにputExtraで指定することができます。Serviceは即時実行されますが、intervalを指定すると二度目以降は指定されたintervalの間隔(ミリ秒)で実行されます。

// app.js
var activity = Titanium.Android.currentActivity;
var service_intent = Ti.Android.createServiceIntent({
  url: 'myservice.js'
});
service_intent.putExtra('interval', 5 * 60 * 1000); //5分後に実行
service_intent.putExtra('my_data', 'this is my data');
service = Ti.Android.createService(service_intent);
service.start();
// android/myservice.js

var service = Ti.Android.currentService;
var service_intent = service.getIntent();
if(!Ti.App.Properties.hasProperty('notificationCount')){
  Ti.App.Properties.setInt('notificationCount', 0);//初回起動時は無視
}else{
  //5分後にresumeしたときの動作
  Ti.App.Properties.removeProperty('notificationCount');
  var my_data = service_intent.getStringExtra('my_data');
  Ti.API.info(my_data);//データの引き渡しができた
  service.stop();
}

これでログに文字列を出力するだけのServiceを作ることができました!最高のアプリです。バカ売れ間違い無しですね。

Serviceの活躍する場面ですが、例えばここから起動中のアプリにfireEventで何か実行させたりすることができます。setIntervalとかsetTimeoutで動作を制御しようとすると、Androidの場合は画面スリープの制御が出来なかったりしていまいち信頼性が低いので、Serviceを起動してそこからコントロールするのがいいと思います。あるいは、バックグランドで実行する位置情報の更新にも便利ですね。

そうそう、iOSのlocal notificationの変わりにもなりそうです。

また、常駐しているサービスから何かのきっかけでAlarm Managerを呼び出して停止中のアプリを起動させたりすることができるともっと面白いことができるかもしれません。が、Titanium MobileにはAndroidのAlarm Managerを扱う仕組みがないので、これは別途モジュールで作成する必要があります。

続く

* update

ところが、Titanium製アプリでバックグランドでServiceを実行すると、Androidでホームボタンを押してアプリを閉じた場合は問題ないのですが、backボタンで閉じた場合はServiceがresumeしなくなることが判明しました。

そこで、backボタンを押したときにホームボタンと同じ動作をするよう変更してみたところ、Serviceは無事動き続けました。

  win.addEventListener('android:back', function(){
    var intent = Ti.Android.createIntent({
      action: Ti.Android.ACTION_MAIN
    });
    intent.addCategory(Ti.Android.CATEGORY_HOME);
    Ti.Android.currentActivity.startActivity(intent);
  });

backボタンでアプリが閉じてしまうrootウィンドウにこんなイベントリスナを追加するとうまくいきました。

Posted by on 4月 17, 2012 in Android, Titanium

コメントを残す