Hatena::ブログ(Diary)

TECH LOG RSSフィード

2009-02-20

[]アプリケーションの基礎

[2009/2/27]サービスとブロードキャストレシーバ、コンテントプロバイダを追加

[2009/3/2]インテントコンポーネントのシャットダウンを追加

[2009/3/3]マニフェストインテントフィルタタスクアクティビティを追加

原文:http://developer.android.com/guide/topics/fundamentals.html

--------------------------------------------------------

AndroidアプリケーションはJava言語記述します。コンパイルされたJavaコードは、アプリケーションで必要なデータリソースファイルも含めて、aaptツールでAndroidパッケージ(.apt拡張子のアーカイブファイル)にバンドルされます。このファイルユーザが端末にダウンロードするファイルであり、モバイル端末にインストールしてアプリケーションを描画する際にはこのファイル形式でなければなりません。同一の.apkファイル内にあるコードは全て、1つのアプリケーションであるとみなされます。

Androidアプリケーションは、さまざまな方法で世界中に存在しています。

  • デフォルトでは、各アプリケーションはLinuxプロセス上で動作します。アプリケーションのコードを実行する際、Androidはプロセスを開始します。アプリケーションがシステムリソースを必要としなくなった際には、プロセスをシャットダウンします。
  • プロセスは各々でJavaバーチャルマシン(VM)を保持しており、他のアプリケーションからは独立した形で、アプリケーションのコードを実行します。
  • デフォルトでは、各アプリケーションには一意のLinuxユーザIDが割り当てられます。パーミッションが設定されるため、基本的には割り当てられたユーザやアプリケーション自身からしかアプリケーションファイルを参照することができません。ただし、他アプリケーションに公開する方法も用意されています。

2つのアプリケーションで同一のユーザIDを共有することも可能で、その場合は互いのファイルを参照することができるようになります。同一IDシステムリソースやアプリケーションを保存するということは、同一のLinuxプロセス上で動作することを意味し、同一のVMを共有することになります。

アプリケーションコンポーネント

Androidの主な特徴として、あるアプリケーションが他のアプリケーションの要素を利用できる点が挙げられます(アプリケーションが利用を許可していることが前提です)。例えば、アプリケーションで画像スクロールリストを表示する場合を考えてみましょう。既に別のアプリケーションがちょうどいいスクローラーを開発していて、他アプリケーションで利用できるようにしてあったとします。その場合、自分自身で開発するのではなく、既に出来上がっているスクローラーを呼び出すことができます。他アプリケーションのコード統合するのではなく、それにリンクするのです。必要な時に、簡単に他アプリケーションの一部を実行することができます。

これを実現させるため、システムは必要な時にいつでも、アプリケーションプロセスを開始し、Javaオブジェクトのインスタンスを生成できなければなりません。それゆえ、たいていのシステムにあるようなアプリケーションと違って、Androidアプリケーションはアプリケーション内で(例えばmain()ファンクションのような)エントリーポイントを1つも持ちません。そのため、システムは必要な時に必要なコンポーネントのインスタンスを作成し、実行することができます。


アクティビティ (Activity)

アクティビティでは、主にユーザ操作に対応するビジュアルユーザインターフェースを提供します。具体例として、アクティビティを利用して、ユーザが選択可能なメニュー項目リストを提供したり、キャプションと共に写真を表示したりすることができます。テキストメッセージングアプリケーションの場合では、あるアクティビティがメッセージの送信アドレスリストを表示し、別のアクティビティが選択されたアドレスへメッセージを書き、また別のアクティビティが古いメッセージを参照し設定を変更したりします。それらのアクティビティは包括した1つのユーザインターフェースとして動作するにもかかわらず、各アクティビティは他とは独立して存在しています。各アクティビティはActivityクラスのサブクラスとして実装されます。

アプリケーションが1つのアクティビティで構成されている場合もあれば、テキストメッセージングアプリケーションのように複数含まれている場合もあります。どのぐらいの数のアクティビティが含まれているかは、もちろん、アプリケーションとその設計依存します。一般的に、複数のアクティビティのうち1つは、アプリケーション起動時にユーザに最初に提供されるものとなっています。現在アクティビティが次のアクティビティを開始すると、あるアクティビティから別のアクティビティへ遷移します。

アクティビティには描画用のデフォルトウィンドウが規定されています。一般的に、ウィンドウは画面全体に表示されますが、画面よりも小さく他のウィンドウの前面に表示されるウィンドウもあります。例えば、アクティビティの途中でユーザレスポンスを促すポップアップダイアログや、画面上の特定項目を選択した際に、重要情報をユーザに提供するウィンドウなどが挙げられます。アクティビティではそのようなウィンドウ活用することもできます。

ウィンドウで表示される内容は階層化されたビュー(Viewクラスから派生したオブジェクト群)によって提供されます。各ビューはウィンドウ内のある長方形の領域を制御します。親ビューは自分子供たちのレイアウトも保持しています。枝葉ビュー(階層構造の底辺にあるビュー)は自分達が制御する長方形を描画し、その領域に対して指定されたユーザアクションに対応します。ビューはユーザアクティビティとの相互のやりとりが発生する場所なのです。例えば、ビューがある小さめの画像を表示したとして、ユーザがその画像をトンとたたいたら、アクションを開始するかもしれません。Androidでは、ボタンテキストフィールドスクロールバー、メニュー項目、チェックボックスなど、数多くの既製のビューを提供しています。

階層化されたビューはActivity.setContetnView()メソッドによって、アクティビティのウィンドウ内に置かれます。”content view”とは階層構造ルートにあるViewオブジェクトのことです(Viewや階層構造の詳細情報はユーザインターフェースドキュメントを参照してください)。


サービス (Service)

サービスビジュアルインターフェースを持っておらず、不特定期間バックグラウンドで実行されます。例えば、ユーザが他の事をしている間に、サービスがバックグラウンドでミュージックを再生しているかもしれません。ネットワーク越しにデータを検索し、何かを計算して適切なアクティビティに結果を返しているかもしれません。各サービスはServiceクラスを継承します。

例として、プレイリストの曲を再生するメディアプレイヤーを取り上げてみます。メディアプレイヤーアプリケーションは、ユーザに曲を選択・再生させるアクティビティを1つ以上は持っているはずです。しかしながら、ミュージック再生自体は、アクティビティによって処理されません。ユーザメディアプレイヤーを離れて何か違うことをしたとしても、再生を続けるためです。ミュージックを鳴らし続けるために、メディアプレイヤーアクティビティは、バックグラウンドで実行されるサービスを開始します。システムは、たとえアクティビティが画面から離れたとしても、ミュージック再生サービスを実行し続けます。

実行中のサービスに接続(バインド)することも可能です。(もし実行していなければサービスを開始することもできます)。接続中、サービスが公開するインターフェースを通してサービスと通信することができます。例えば音楽サービスの場合は、インターフェースを通して、ユーザが一時停止、巻戻し、停止、再生を実行することができるでしょう。

アクティビティや他のコンポーネントのように、サービスはアプリケーションプロセスのメインスレッドで実行されます。そのため、他のコンポーネントユーザインターフェースをブロックしたりはしません。しばしば(ミュージック再生のような)時間のかかるタスクのために別のスレッドを生成することはあります。詳細は「プロセスとスレッド」を参照してください。


ブロードキャストレシーバ (Broadcast receiver)

ブロードキャストレシーバはブロードキャストアナウンスを受け取り対処する以外は何もしないコンポーネントです。多くのブロードキャストはシステムコードから開始されます。例えば、タイムゾーンが変わったり、バッテリーがローになったり、写真が撮られたり、ユーザ言語設定を変更したりした場合などです。また、アプリケーションがブロードキャストを開始することもできます。例えばデータが端末にダウンロードされ利用可能になったことを他のアプリケーションに知らせたりする場合などです。

アプリケーションは、重要と判断したアナウンスに対応するため、制限なくブロードキャストレシーバを保持することができます。全てのレシーバはBoradcastReceiverクラスを継承します。

ブロードキャストレシーバユーザインターフェースを表示しません。受け取った情報に応じてアクティビティを開始するかもしれませんし、ユーザに通知する通知マネージャ (NotificationManager)を使うかもしれません。ユーザの注意を引くために様々な通知方法が提供されています。バックライトの点滅、端末のバイブレーション、音を鳴らすなどです。一般的に、ステータスバーに配置された永続的なアイコンを開くことで、ユーザメッセージを取得します。


コンテントプロバイダ (Content Provider)

コンテントプロバイダは、他のアプリケーションが利用可能なアプリケーションデータを作成します。データファイルシステムやSQLiteデータベースなどに格納されます。コンテントプロバイダはContentProviderクラスを継承しており、他のアプリケーションがデータを退避したり格納したりできるような標準的なメソッド群を実装しています。しかしながら、アプリケーションは直接これらのメソッドを呼ばず、代わりにコンテントリゾルバ(ContentResolver)オブジェクトを使って、そのメソッドを呼びます。コンテントリゾルバはコンテントプロバイダと通信することができます。プロセス間通信を管理するプロバイダと協調するということです。

コンテントプロバイダの詳細情報は、「コンテントプロバイダ」ドキュメントを参照してください。

特定のコンポーネントにより処理されるべきリスクエストが発生した際、Androidは常にコンポーネントのアプリケーションプロセスが実行中かを確認し、必要に応じてプロセスを開始します。また、コンポーネントの適切なインスタンスが利用可能かも確認し、必要に応じてインスタンスを生成します。


アクティベート用コンポーネント:インテント (intent)

コンテントプロバイダはコンテントリゾルバからのリクエストによってアクティベートされます。他の3つのコンポーネントである、アクティビティ、サービス、ブロードキャストレシーバに関しては、インテントと呼ばれる非同期メッセージによってアクティベートされます。インテントとは、メッセージ内容を保持するIntentオブジェクトのことです。アクティビティやサービスにとって、インテントリクエストされているアクションを伝え、とりわけ、実行するデータのURIを特定する役割を担います。例えば、ユーザ画像を提供したり、ユーザテキスト編集させるために、アクティビティに対してリクエストを運んだりします。ブロードキャストレシーバにとって、インテントオブジェクトはアナウンスされているアクションを伝える存在です。例えば、カメラボタンが押されたことを関連する関係者アナウンスするなどが考えられます。

コンポーネントアクティベートするために、それぞれ別々のメソッドが用意されています。

  • Context.startActivity()やActivity.startActivityForResult()にインテントオブジェクトが渡されることで、アクティビティが起動します(もしくは実行すべき新規の何かが与えられます)。起動したアクティビティは、getIntent()メソッドを呼んで起動するきっかけとなった最初のインテントを参照します。Androidは、アクティビティのonNewIntent()メソッドを呼んで、以降のインテントアクティビティに渡します。
    • あるアクティビティが次のアクティビティを開始するケースはよくあることです。開始したアクティビティから結果を受け取ることを想定している場合には、startActivity()の代わりにstartActivityForResult()を呼び出します。例えば、ユーザ写真を選ばせるアクティビティを開始した場合には、選択された写真を受け取ることを想定しているはずです。結果は、呼び出し中のアクティビティのonActivityResult()メソッドに渡されたインテントオブジェクトとして返ってきます。
    • 以降のセクションである、「リモートプロシージャコール」に、サービスのバインドに関する詳細が記述されています。
  • アプリケーションはContext.sendBroadcast()、Context.sendOrderedBroadcast()、Context.sendStickyBroadcast()など様々な方法によりインテントオブジェクトを渡すことで、ブロードキャストを開始することができます。onReceive()メソッドを呼び出すと、Androidは関連する全てのブロードキャストレシーバインテントを運びます。

インテントメッセージに関する詳細は、「インテントインテントフィルタ」を参照してください。


コンポーネントのシャットダウン

コンテントプロバイダはコンテントリゾルバからのリクエストを処理している間だけアクティブです。ブロードキャストレシーバも、ブロードキャストメッセージを処理している間だけアクティブです。そのため、これらのコンポーネントを明示的にシャットダウンする必要はありません。

一方、アクティビティはユーザインターフェースを持っており、ユーザと長期間に渡る通信を行います。たとえアイドル中であったとしても、通信が続く限り、アクティビティはアクティブのままです。同様に、サービスも長期間実行したままの可能性があります。そのため、Androidはアクティビティとサービスをシャットダウンするメソッドを提供しています。

  • finish()メソッドが呼び出されると、アクティビティはシャットダウンされます。finishActivity()を呼び出すことで、あるアクティビティが別のアクティビティ(startActivityForResult()で開始されたもの)をシャットダウンすることも可能です。
  • サービスの場合は、stopSelf()メソッドが呼び出されるか、Context.stopService()が呼び出されることで、停止します。

コンポーネントが既に使われていない場合や、アクティブコンポーネントのためにAndroidがメモリを回収しなければならない場合に、コンポーネントシステムによってシャットダウンされる場合があります。後述のセクション、「コンポーネントライフサイクル」で、その可能性と効果について詳細に記述しています。


マニフェストファイル

Androidでは、コンポーネントを開始する前に、そのコンポーネント存在するかを確認しなければならないため、アプリケーション内のマニフェストファイルコンポーネントを宣言する必要があります。マニフェストファイルは、Androidパッケージ(.apkファイル)にバンドルされており、アプリケーションのコードファイルリソース情報を保持します。

マニフェストはXMLファイル構造となっており、どんなアプリケーションであっても必ずAndroidManifest.xmlという名前が付けられています。マニフェストはアプリケーションコンポーネントの宣言だけではなく、デフォルトのAndroidライブラリにリンクするライブラリの指定や、アプリケーションに付与するパーミッションの指定など、たくさんの情報が記述されます。

しかし、マニフェスト重要な役割はやはり、Androidに対してアプリケーションのコンポーネントを知らせることです。例えば、アクティビティが以下のように宣言されているとしましょう。


<manifest . . . >
    <application . . . >
        <activity android:name="com.example.project.FreneticActivity"
                  android:icon="@drawable/small_pic.png"
                  android:label="@string/freneticLabel" 
                  . . .  >
        </activity>
        . . .
    </application>
</manifest>

<activity>タグのname属性には、アクティビティを実装するActivityのサブクラスを指定します。iconとlabel属性には、ユーザに表示されるアクティビティのアイコンとラベルを格納したリソース記述します。

他のコンポーネントも同様に宣言します。サービスの場合は<service>、ブロードキャストレシーバの場合は<receiver>、コンテントプロバイダの場合は<provider>です。マニフェストで宣言されていないアクティビティ、サービス、コンテントプロバイダはシステムに表示されず、結果的に実行されません。しかしながら、ブロードキャストレシーバマニフェストで宣言するか、(ブロードキャストレシーバオブジェクトとして)コードで直接生成するか、Context.registerReceiver()を呼んでシステムに登録するかのいずれでも構いません。

アプリケーションのマニフェストファイルを構築する方法の詳細は、「AndroidManifest.xmlファイル」を参照してください。


インテントフィルタ

インテントオブジェクトは明示的に対象コンポーネントを指定することができます。仮にそうした場合、Androidは(マニフェストでの宣言をベースにして)コンポーネントを検索し、それをアクティブにします。しかし、もし対象を明示的に指定しない場合、Androidはインテントを処理するのに適したコンポーネントを探さなければなりません。その場合、Androidはインテントオブジェクトと、候補となり得る対象コンポーネントインテントフィルタを比較します。コンポーネントインテントフィルタは、Androidにコンポーネントが処理できるインテントの種類を通知します。コンポーネントに関する他の重要な情報と同様、それらはマニフェストファイルで宣言されます。前回の例を拡張し、アクティビティに2つのインテントフィルタを追加したのが、以下の例です。


<manifest . . . >
    <application . . . >
        <activity android:name="com.example.project.FreneticActivity"
                  android:icon="@drawable/small_pic.png"
                  android:label="@string/freneticLabel" 
                  . . .  >
            <intent-filter . . . >
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
            <intent-filter . . . >
                <action android:name="com.example.project.BOUNCE" />
                <data android:type="image/jpeg" />
                <category android:name="android.intent.category.DEFAULT" />
            </intent-filter>
        </activity>
        . . .
    </application>
</manifest>

この例の最初のフィルタにある、アクション”android.intent.action.MAIN”とカテゴリ”android.intent.category.LAUNCHER”は、よくある組み合わせの1つです。アプリケーションランチャー(端末上でユーザが起動するアプリケーションリストが表示された画面)で表示するアクティビティを登録しています。すなわち、そのアクティビティはアプリケーションのエントリーポイントとなり、ランチャーでアプリケーションを選択した際にユーザが目にする最初のものということになります。

2番目のフィルタは、アクティビティが特定のデータタイプを処理することを宣言するものです。

コンポーネントは無制限にインテントフィルタを保持でき、それぞれが異なった働きをします。フィルタがないアクティビティは、明示的に対象コンポーネントを指定してあるインテントによってのみアクティベートすることができます。

ブロードキャストレシーバコード内で作成し登録した場合には、インテントフィルタを直接IntentFilterオブジェクトとしてインスタンス化します。他のフィルタは全てマニフェスト定義します。

インテントフィルタに関する詳細は、「インテントインテントフィルタ」を参照してください。


アクティビティとタスク

前述した通り、アクティビティは異なるアプリケーションで定義された他のアクティビティを開始することができます。仮に例えば、ある箇所のストリートマップユーザに表示させたいとしましょう。その処理を行うアクティビティが既に存在する場合、あなたのアクティビティがしなければいけないことは、インテントオブジェクトに必要な情報を結びつけ、startActivity()メソッドに渡すだけです。これだけで、マップViewerは地図を表示するはずです。ユーザが戻るキーを押した際には、あなたのアクティビティが画面に再表示されるでしょう。

たとえ、別アプリケーションで定義され別アプリケーションプロセスで実行されていたとしても、ユーザにとってはまるで、マップViewerがあたかもあなたのアクティビティと同じアプリケーション内にあるかのように見えることでしょう。Androidでは、両方のアクティビティを同じタスク内に保持し続けることにより、このようなユーザ操作を実現しています。簡潔に言えば、タスクとはユーザがアプリケーションとして経験するもの、そのものということになるでしょう。スタックに配置された関連するアクティビティのグループと言い換えてもいいでしょう。スタックのルートアクティビティはタスクを開始するアクティビティで、一般的にアプリケーションランチャーでユーザが選択したアクティビティである場合が多いです。スタックのトップにあるアクティビティは実行中のものであり、ユーザが起こすアクションフォーカスしています。あるアクティビティが別のアクティビティを開始する時は、新規のアクティビティがスタックにプッシュされ、実行中のアクティビティとなります。以前のアクティビティはスタックに残ったままです。ユーザが戻るキーを押すと、現在アクティビティがスタックからポップアップされ、以前のアクティビティが実行中のアクティビティとして再開します。

スタックはオブジェクトを格納するため、仮にタスクが同一のActivityのサブクラスを複数オープンしてしまった場合には、先ほどの例で言うと、複数のマップViewerが起動することになります。スタックはそれぞれのインスタンスごとに別々のエントリを持てるということです。スタックのアクティビティは決して再配置されることはなく、プッシュとポップアップのみです。

タスクアクティビティのスタックであり、マニフェストファイルクラスやエレメントではありません。そのため、アクティビティと独立してタスクの値を設定する方法はありません。一般的にタスクの値はルートアクティビティで設定されます。例えば、次のセクションで「タスクのアフィニティ(affinity)」について論じますが、その値はタスクルートアクティビティで設定されるアフィニティから読み出されます。

同一タスクにある全てのアクティビティは1つのユニットとして動作します。タスク全体(スタック内の全てのアクティビティ)を、フォアグラウンドに持って来ることもできますし、バックグラウンドに送ることもできます。仮に例えば、現在実行中のタスクがスタックに4つのアクティビティを保持しているとしましょう。現在実行中のアクティビティの下に3つアクティビティがある状態です。ユーザがHOMEキーを押して、アプリケーションランチャーへ移動し、新規のアプリケーション(実際には新規のタスク)を選択した場合、現在タスクはバックグラウンドへ移動し、新規タスクルートアクティビティが表示されます。その後、ユーザがホーム画面へ戻り、再度以前のアプリケーション(以前のタスク)を選択したとします。そのタスクは、スタックにある4つのアクティビティとともに、フォワードに来ます。その後ユーザが戻るキーを押しても、画面にはユーザが先ほどまで実行していたアクティビティ(以前のタスクルートアクティビティ)は表示されません。スタックのトップのアクティビティは削除され、同一タスク内にある1つ前のアクティビティが表示されることになります。

今説明したこの振る舞いは、アクティビティとタスクのデフォルトの振る舞いですが、その振る舞いを変更する方法もあります。アクティビティとタスク関係、ならびにタスク内のアクティビティの振る舞いは、 アクティビティを開始したインテントオブジェクトに設定されたフラグと、マニフェストファイルの<activity>で設定されたアクティビティの属性との相互関係によって決められます。リクエストを送信するほうと受信するほうの両方が、処理内容を決める権利を持っているのです。

以下は、主要なインテントのフラグです。

    • FLAG_ACTIVITY_NEW_TASK
    • FLAG_ACTIVITY_CLEAR_TOP
    • FLAG_ACTIVITY_RESET_TASK_IF_NEEDED
    • FLAG_ACTIVITY_SINGLE_TOP

主要な<activity>属性です。

    • taskAffinity
    • launchMode
    • allowTaskReparenting
    • clearTaskOnLaunch
    • alwaysRetainTaskState
    • finishOnTaskLaunch

次のセクションでは、上記のフラグと属性が何を意味するのか、どのようにお互いに作用するのか、使用する上での考慮点は何かについて解説します。


訳注)この章はやたら長いので、一旦ここでアップします。原文ではまだまだ続きがあります。

--------------------------------------------------------

仕事が忙しくなってきたため、翻訳のペースが落ちてきてしまっています。そんなとき心の支えになっているのが、「あくせく翻訳」の左上のカウンターだったりします。アクセス数は多くないかもしれませんが、もしかしたら間違って迷いこんだだけなのかもしれませんが、確実に見にきてくださっている人はいる・・・そう思って頑張るようにしています。



Android翻訳目次へ

はてなユーザーのみコメントできます。はてなへログインもしくは新規登録をおこなってください。

トラックバック - http://d.hatena.ne.jp/coolblue/20090220/p1