Titanium Mobileで作る! iPhone/Androidアプリ

第18回 ServiceとNotification

この記事を読むのに必要な時間:およそ 4 分

Titanium Mobile SDK 1.7.2

前回の記事公開とほぼ同時期に,Titanium Mobile SDKの1.7.2がリリースされました。バグフィックスが主のようですが,Motorola XOOMなどのタブレット型端末への対応が強化されるなど,Android版の活発な開発が継続されているようです。

さて前回に引き続いて,今回もAndroid特有の機能について解説します。今回はServiceとNotificationについて解説します。

Serviceとは

iPhoneアプリはホームボタンを押して画面を消してしまうと,基本的にはバックグラウンドで動作することがありません(終了後の数分間と,VoIPやGPS,オーディオ関係のいくつかの機能を除きます)⁠しかし,Androidアプリは戻るボタンを押してホーム画面に戻ったとしても,そのアプリは終了せず動き続けています。なので,定期的にTwitterの更新をチェックするなどユーザーの操作を必要としない動作をバックグラウンドで行うことができます。実際にそのような処理を行ってくれるのがServiceの機能です。

Serviceに必要なもの

Serviceを動かすためには,tiapp.xmlへの設定の記述と,定期的に実行される処理を記載したJavascriptのファイルが必要になります。また,アプリからServiceを起動するためのコードも必要です。ではまず,Serviceの起動を行うところから解説していきます。

var win = Ti.UI.currentWindow;

var button1 = Ti.UI.createButton({
    width: '100dp',
    heigth: '50dp',
    top: '210dp',
    left: '10dp',
    title: 'Service Start'
})

button1.addEventListener(
    'click',
    function(){
        Ti.API.debug('create intent');
        var intent = Titanium.Android.createServiceIntent( { url: 'testservice.js' } );
        intent.putExtra('interval', 10000);
        intent.putExtra('twitter_id', 'kurain');
        var service = Titanium.Android.createService(intent);
        service.start();
    }
);
win.add(button1);

前回の記事と同様に新しいプロジェクトを作成し,あるWindowの中にボタンを設置している状況だと考えてください。ボタンをクリックされた時の処理がbutton1.addEventListenerの第二引数の関数の中で定義されています。Serviceを起動するときは前回解説したIntentの一種として起動するべきServiceのIntentを作成します。Intentの作成を行うのがTitanium.Android.createServiceIntentです。引数のオブジェクトのurlパラメタに指定されているファイルが実際にこのServiceが行う処理を記述しているファイルになります。このファイルはプロジェクトのあるディレクトリに存在するandroidディレクトリに入れておくようにしましょう。続いてこのIntentに対してputExtraメソッドで実行間隔を指定しているのが

 intent.putExtra('interval', 10000);

の部分です。ミリ秒で指定するので,この例では10秒間隔でこのServiceが実行されます。またputExtraを使うと任意のキーでデータをServiceに渡すことができるので,この例ではTwitterの更新をチェックする対象としてtwitter_idをServiceに渡すようにしています。そして作成したIntentをTitanium.Android.createServiceに渡すとTitanium.Android.Serviceオブジェクトが作成されます。このオブジェクトのstartメソッドを呼び出すとServiceが起動します。

Serviceの中身

これで10秒に一回何かしらの動作が行われるServiceができました。実際に何をするかは,testservice.jsの実装次第ということになります。それでは実装のほうを確認してみましょう。

var service = Titanium.Android.currentService;
var intent = service.intent;
var twitter_id  = intent.getStringExtra("twitter_id");
var url = 'http://api.twitter.com/1/statuses/user_timeline.json?screen_name=' + twitter_id;
Ti.API.debug("Try Download: " + url);

var xhr = Ti.Network.createHTTPClient();

xhr.onload = function () {
    var json = this.responseText;
    var data = JSON.parse(json);
    var tweet_id = data[0].id_str;
    
    Ti.API.info('Last Tweet:' + tweet_id);
};
xhr.open('GET', url);
xhr.send();

まずは簡単な動作だけの実装にしてあります。IntentにputExtraメソッドで指定したTwitterユーザーのツイートを取得し,最新のツイートのidをデバッグコンソールに出力するだけのコードです。HTTPClientの動作については何度か連載で解説していますので問題ないと思います。重要なのは,先頭の3行で,Titanium.Android.currentSerciceでこのServiceを表すオブジェクトが取得できる点と,そのServiceを表すオブジェクトからIntentを取得し,Serviceを起動するときにputExtraで指定した値がgetStringExtraで取得できているという点です。このメソッドを使うことで,このtestservice.jsに対して,呼び出し側からデータを渡すことができるようになっているわけです。

tiapp.xmlへの記載

Serviceを使うにはもうひとつ,tiapp.xmlへの記述が必要です。

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

androidタグは最初から存在しているので,<services>タグと,<service>タグを新たに追加してください。serviceのurlに指定するファイル名はServiceの動作を実装しているファイル名にします。

ここまでの実装が終わったら,実際にアプリを起動して確かめてみましょう。デバッグコンソールに10秒ごとに最新のツイートのidが表示されます。ツイートを新たに追加すれば,表示されるidも変化するはずです。

Servieからの通知

さてServiceの実装を試すことはできましたが,デバッグコンソールにidが表示されるだけでは実用性がありません。ServiceからTwitterの更新があったかどうかをユーザーに通知する機能が必要です。前述の通りServiceはアプリが画面上になくても動作していることがあります。アプリを利用中でもないのに,Twitterに更新があったからといってアプリが急に起動するようだと,ユーザーに不快な思いをさせますね。そこで,AndroidにはNotificationという仕組みが用意されており,バックグラウンドで起きた動作を画面上部のステータスバーに表示させることができます。普段⁠アプリのダウンロード中⁠⁠メールが着信しました⁠といった表示が出ているあの部分です。

では実際に先ほどのtestservice.jsへコードを追加して,Notificationを表示させてみましょう。

function showNotification(opt){
    Ti.API.info('start notification');
    var modified = opt.modified;
    var twitter_url = 'http://twitter.com/' + twitter_id;
    var intent = Ti.Android.createIntent({ //①
        action: Ti.Android.ACTION_VIEW, 
        data: twitter_url 
    });
    var pending = Ti.Android.createPendingIntent({  //②
        'intent' : intent,
    });
    var notification = Ti.Android.createNotification({ //③
        contentIntent : pending,
        contentTitle: 'Twitter をチェックしました',
        contentText:   modified ? '更新されていました' : '更新されていませんでした',
    });
    Ti.Android.NotificationManager.notify(1, notification);
}

xhr.onload = function () {
    var json = this.responseText;
    var data = JSON.parse(json);
    var tweet_id = data[0].id_str;
    
    var last_id = Ti.App.Properties.getString('last_tweet_id');
    Ti.API.info([tweet_id, last_id]);
    if (last_id && last_id == tweet_id ) {
        showNotification({modified:false});
    } else {
        Ti.App.Properties.setString('last_tweet_id',tweet_id);
        showNotification({modified:true});
    }
};

先のコードをこのように変更します。showNotificationがNotificationを実際に表示している関数になっています。xhr.onloadで指定されている関数では最新ツイートのidを調べて,前回チェックした時と変更があるかを確認しNotificationに表示する内容を変化させるようにしています。

showNotification関数では①の部分でまず,Intentを作成しています。このIntentは表示されたNotificationがユーザーによってタップされたときに発行されるIntentになります。前回の復習になりますが,この例でのIntentはdataに指定されたurlを,開くことのできるアプリで表示するというものになります。urlが開けるということですから当然,ブラウザアプリのいずれかが起動されるということになります。

Intentは基本的には即時に発行するものなので,Ti.Android.createPendintIntentというメソッドに渡してpendingされた,つまり即時には発行されないIntentにします。こうしてできたのがpending変数です。

そして,③の部分でやっとNotificationを作成することができます。Ti.Android.createNotificationに渡しているcontentIntentには前述のとおりNotificationの表示がタップされたときに発行されるIntentを,contentTitle,contentTextには表示される時のタイトルと文章をそれぞれ指定します。こうしてできたTi.Android.NotificationオブジェクトをTi.Android.NotificationManager.notifyメソッドに渡せば,Notificationが表示されます。第一引数に渡している数値はNotificationのidで,表示したNotificationを削除したいときなどに指定します。

実際にこのコードでNotificationを表示するとこのようになります。

ステータスバーに三角形のビックリマークが表示される

ステータスバーに三角形のビックリマークが表示される

ステータスバーをドラッグすると,Notificationが表示される

ステータスバーをドラッグすると,Notificationが表示される

今回の記事のサンプルコードは,こちらのUrlから取得できますのであわせてご利用ください(前回記事のリポジトリのブランチになっています)⁠

まとめ

今回はAndroid版でのみ使える,ServiceとNotificationの機能について解説しました。アプリを終了させずにバックグラウンド処理を続けさせることのできる点はAndroidの大きな魅力の一つですので是非お試しください。ただし,アプリにメモリを消費させ続けることを嫌って定期的にバックグラウンドのアプリを終了させるようなアプリを利用している場合や,そもそもメモリが足らずに,バックグラウンドのアプリが自動的にkillされることも考えられます。なのでServiceでの処理は必ず実行されるものとは考えないようにする必要があるのを忘れないでください。

著者プロフィール

倉井龍太郎(くらいりゅうたろう)

株式会社はてな アプリケーションエンジニア。

スマートフォンアプリからSQLチューニングまで幅広く格闘中。好きな言語はRuby。

URLhttp://d.hatena.ne.jp/r_kurain/
Twitter@kurain

コメント

  • 質問:「interval」ではなく「指定時刻」でサービスの実行

    倉井さん

    Titanium mobileアプリ開発にあたり、大変参考にさせていただいております。ありがとうございます。

    さて、以下の記事につきまして1点質問があります。
    「第18回 ServiceとNotification」
    http://gihyo.jp/dev/serial/01/titanium/0018

    記事の中では、「interval」でのサービス実行の方法が記載されておりますが、これを「指定時刻」で実行するようにすることはできるでしょうか?

    お忙しいところ恐縮ですが、ご教示いただけると幸いです。
    よろしくお願い致します。

    KY

    Commented : #1  KY (2013/08/19, 13:36)

コメントの記入