TitaniumでAndroidのバックグラウンド処理

  • 6
    いいね
  • 1
    コメント
この記事は最終更新日から1年以上が経過しています。

Titanium Advent Calendar 2014の6日目です。
岩手でTitaniumを推進しているチイキットの伊藤です。Titanium界隈の巨人達がいる中で大変恐縮なのですが、初歩的なAndroidのバッググラウンド処理について書いていきます。

やりたいこと

とてもシンプルで何秒、何分ごとに起動し、
条件をチェックして、条件が満たされている場合処理する、しない場合処理しない感じにします。
例えば、ニュースサイトからバックグラウンドで自動でデータを取得し、バックグラウンドに1時間立っていたら、ニュースを取得し、更新された記事を保存するような感じで使えると思います。
※今回はデータの保存まではやりません。。。

  • Androidでバックグラウンド処理
  • 時間+αな条件での起動
  • ボタンを押したらバックグラウンド処理が停止・再開

こんな感じの作ります

screenshot_2014-12-06-21-31-48_1024.png

ログがこんな感じ

スクリーンショット 2014-12-06 21.10.52.png

ソースはこちらになります

環境はTitanium SDK 3.4.1.GAでAlloyつかってます。

https://github.com/ganezasan/AlloyAndroidBackground.git

バックグラウンド処理するには

serviceを使います。詳しいことを書くとボロがでるので、
こちらの公式ドキュメントを御覧ください。

http://docs.appcelerator.com/titanium/3.0/#!/api/Titanium.Android.Service

こちらの順番で
* tiapp.xmlにserviceを使うことを追記
* service.jsにバックグラウンドで処理することを記載
* index.jsにサービスの起動・バックグラウンドの検知を書く

tiapp.xmlにserviceを使用する設定を追加する

service.jsというファイルをServiceとして実行し、指定された間隔(interval)で起動するように設定します。
service.jsファイルはapp/assets配下に置く設定になります。

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

service.jsにバックグラウンドで処理することを記載

今回はAlloy.Globalsに以下の変数を用意し、
「サービスが処理中じゃない」 かつ 「バックグラウンドに入っている」 かつ 「以前処理した時間より10秒たっている」場合に処理をするように記載しました。

alloy.js
Alloy.Globals.updateNow = false;
Alloy.Globals.activeFlg = true;
Alloy.Globals.updateOften = 10000;

処理中はAlloy.Globals.updateNowの変数をtrueにして処理が重複しないようにしています。
今回処理は記載していませんが、処理が終わったタイミングで、終了時刻を登録し、
Alloy.Globals.updateNowもfalseに変更しています。

service.js
Ti.API.info("service is running");
Ti.API.info(to_hhmi(Alloy.Globals.lastClosed));
Ti.API.info("service autoUpdate:" + Alloy.Globals.autoUpdate);
Ti.API.info("service activeFlg :" + Alloy.Globals.activeFlg);

// 現在時刻
var now = new Date();

if (!Alloy.Globals.updateNow
  && !Alloy.Globals.activeFlg 
  && (now.getTime() - Alloy.Globals.lastClosed.getTime() > Alloy.Globals.updateOften)) {
  Ti.API.info("============================");
  Ti.API.info("service Start!!!!");
  Ti.API.info("============================");
  Alloy.Globals.updateNow = true;


  //データ取得等の処理を記載する


  //処理終了の時刻を登録
  Alloy.Globals.lastClosed = now;   // データ取得時刻を保存
  //処理中に再度サービスの処理が起動されないようにするため
  Alloy.Globals.updateNow = false;
}

index.jsにサービスの起動・バックグラウンドの検知を書く

アプリが起動したタイミングでserviceを起動し、
activityにアプリがバックグラウンドにいるのか、起動している状態なのかを検知するイベントをしこんでいきます。

index.js
//Activityのライフサイクルに応じてイベントを設定
$.index.addEventListener("open", function () {
  Ti.API.info("open");
  Alloy.Globals.activity = $.index.activity;
  Alloy.Globals.page = "index";
  Alloy.Globals.activeFlg = true;

  Alloy.Globals.activity.addEventListener("resume", function(d) {
    Ti.API.info("************************ Activity was resumed(再開したよ) ************************");
    Ti.API.info(Alloy.Globals.page);

    Alloy.Globals.activeFlg = true;
    Ti.API.info("activeFlg: true");
    Ti.API.info(Alloy.Globals.activeFlg);
  });
  Alloy.Globals.activity.addEventListener("pause", function(d) {
    Ti.API.info("************************ Activity was paused(バックグラウンドにはいったよ) ************************");
    Ti.API.info(Alloy.Globals.page);
    Ti.API.info(Alloy.Globals.activeFlg);

    if(Alloy.Globals.page == "index" && Alloy.Globals.activeFlg ){
      Alloy.Globals.activeFlg = false;
      // アプリが閉じた時の時間を保存
      Alloy.Globals.lastClosed = new Date();
      Ti.API.info("lastClosed: " + to_hhmi(Alloy.Globals.lastClosed));
    }
  });
  Alloy.Globals.activity.addEventListener("destroy", function() {
    Ti.API.info("Activity was destroied");
    Ti.API.info(Alloy.Globals.service);
    Ti.API.info(Alloy.Globals.serviceIntent);
  });
  // サービスを起動(Androidのみ)
  if (OS_ANDROID && Alloy.Globals.service == null) {
    Ti.API.info("************************ Service Start ************************");
    var intent = Titanium.Android.createServiceIntent({url: "service.js"});
    intent.putExtra("interval", Alloy.Globals.serviceOften);
    var service = Titanium.Android.createService(intent);
    service.start();
    Alloy.Globals.serviceStatus = true;
    Alloy.Globals.service = service;
    Alloy.Globals.serviceIntent = intent;
    $.index.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);
    });
  }
});

注意する事として、Android特有のbackボタンを押したときにserviceがdestroyされてしまうため、backボタンを押したときにホームボタンと同じ動作をするよう変更しています。

こちらの記事を参考にさせて頂きました。

http://selfkleptomaniac.org/archives/2128

もう1点たしか画面遷移したタイミングで、activityがpauseされるため、
バックグラウンドにいるのか、画面遷移したのかわからない状態になります。
その場合は、Alloy.Globals.pageで画面遷移する前にpageを変更するのを前回思いつきましたが、もっと良いやり方があるかと思いますので探してみてください。

完成

ログGif

まとめ

バックグラウンド処理はたまに使用することがあるので、
いままでやったことをまとめることができてよかったです。

ほんとは、yagi さんの言っていたfirebaseで何かやってみたかったのですが、、

次は

明日は同じくチイキットの @s4shiki さんです。いやー岩手熱いですね〜!
よろしくお願いします!