Skip to content
This repository has been archived by the owner on Jun 20, 2023. It is now read-only.

2.04. メッセージングと通知

KeithYokoma edited this page Apr 25, 2013 · 40 revisions

この章では、アプリ内や、アプリ外との遣り取りをする仕組みであるメッセージングについて解説します。

参考:Intent | Android Developers

参考:Intents and Intent Filters | Android Developers

参考:BroadcastReceiver | Android Developers

参考:LocalBroadcastManager | Android Developers

目次

Intent

Android のメッセージングで、最も頻繁に使われるオブジェクトに Intent があります。
Intent は、メッセージングでやり取りするメッセージそのものを取り扱う Entity の役割を持っています。

実際に Intent をメッセージとして送る仕組みは、Context が持っており、
Intent を受け取ることが出来るのは、Activity、Service、BroadcastReceiver の 3 つのコンポーネントです。

Intent オブジェクト

Intent オブジェクトは、メッセージを送信する相手に実行してもらいたい処理を記述したデータです。
Intent オブジェクトには、以下に挙げる情報が含まれています。

Action

どのようなアクション(処理)を実行してほしいか、を示します。
Activity に向けた Action と、Broadcast 用の Action の 2 種類があります。

Activity に向けた Action の代表的なものを以下に示します。

  • ACTION_VIEW
  • ACTION_MAIN
  • ACTION_SEND
  • ACITON_SENDTO
  • ACTION_EDIT
  • ACTION_PICK
  • ACTION_DELETE
  • ACTION_INSERT
  • ACTION_SEARCH
  • ACTION_CALL

Broadcast 用の Action の代表的なものを以下に示します。

  • ACTION_BOOT_COMPLETED
  • ACTION_SHUTDOWN
  • ACTION_PACKAGE_ADDED
  • ACTION_PACKAGE_REPLACE
  • ACTION_PACKAGE_REMOVED
Category
Intent を処理するべき対象が、どのような属性を持っていることを期待しているかを示すものです。
  • CATEGORY_DEFAULT
  • CATEGORY_LAUNCHER
  • CATEGORY_HOME
  • CATEGORY_PREFERENCE
  • CATEGORY_BROWSABLE
Data
Action の対象となるデータを指し示す URI です。
例えば、ACTION_VIEW と対にして Data を渡すということは、対象のデータを表示するよう指示することを意味します。
Type
Data の種類を表す MIME タイプです。
Component

Action を実行することを期待するコンポーネントです。
Intent を送りつける対象のコンポーネントとも読み替えることができます。

この、Component を明示している(Intent を送る対象が明らかな)Intent のことを、明示的 Intent と呼んでいます。
一方、Component を明示していない(Action をハンドリング出来るもの全てを対象とする)Intent のことを、 暗黙的 Intent と呼びます。

Extras
Intent を送りつける対象に渡す追加情報です。
Key と Value のペアを Bundle オブジェクトに詰めて渡します。
Flag
Activity の起動方法をシステムに通知するための情報です。

Intent Filter

暗黙的 Intent を受け取るコンポーネントが、その Intent をハンドリング可能かどうかを宣言するための仕組みです。
Android フレームワークは、この Intent Filter の宣言を元にして、暗黙的 Intent の対象となるコンポーネントをリストアップします。

ActionDataCategoryの 3 つの情報を基準に暗黙的 Intent がハンドリング可能なものを選択するように設計されているので、Intent Filter にもこの 3 つの情報について宣言をしておきます。

暗黙的 Intent がハンドリング可能かどうかの判定に、Flag や Extras は使われません。

AndroidManifest での IntentFilter の宣言の例を以下に示します。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="jp.mixi.sample"
    android:versionCode="1"
    android:versionName="1.0" >

    <uses-sdk
        android:minSdkVersion="7"
        android:targetSdkVersion="17" />

    <application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >

        <activity
            android:name="jp.mixi.sample.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <!-- アプリのメイン(入り口)となる Activity を起動するときの Action を受け取る -->
                <!-- Intent クラスに定義されている ACTION_MAIN 定数の実態は "android.intent.action.MAIN" という文字列 -->
                <action android:name="android.intent.action.MAIN" />

                <!-- ランチャーからの起動のものを受け取る -->
                <!-- Intent クラスに定義されている CATEGORY_LAUNCHER 定数の実態は "android.intent.category.LAUNCHER" という文字列 -->
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity
            android:name="jp.mixi.sample.AnotherActivity"
            android:label="@string/app_name" >
            <!-- 画像を共有する Action のための Intent Filter 宣言 -->
            <intent-filter>
                <!-- ACTION_SEND または ACTION_SEND_MULTIPLE のいずれかを受け取る -->
                <!-- Intent に設定可能な Action は 1 つだけなので、<intent-filter> に Action を複数宣言すると -->
                <!-- その中からいずれかに該当するものを受け取る、という意味になる -->
                <action android:name="android.intent.action.SEND" />
                <action android:name="android.intent.action.SEND_MULTIPLE" />

                <!-- 暗黙的 Intent を扱う際に必須のカテゴリ -->
                <!-- システムは、Activity の起動に暗黙的 Intent を発行すると、 -->
                <!-- このカテゴリが付与されているものとして扱うため、Activity で暗黙的 Intent を受け取りたい場合は -->
                <!-- 必ずこのカテゴリを <intent-filter> に宣言しておく -->
                <!-- 複数のカテゴリを <intent-filter> に宣言した場合は、 -->
                <!-- 全てのカテゴリにマッチするもののみを受け取る、という意味になる -->
                <category android:name="android.intent.category.DEFAULT" />

                <!-- Data の種類の制限 -->
                <!-- MIME タイプのほか、URI のスキームを制限することもできる -->
                <data android:mimeType="image/jpeg" />
            </intent-filter>
        </activity>

    </application>

</manifest>

Intent を用いたメッセージング

Activity を起動する Intent

新しい Activity を呼び出すときに使用します。

public class MyActivity extends Activity {
    @Overrride
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // メッセージのオブジェクトとして Intent を作る
        // どの Context から、どのクラスに対してメッセージを送るかを指定する
        Intent intent = new Intent(this, NextActivity.class);
        // Intent を Context に渡して、メッセージを送る
        // この場合、NextActivity クラスにメッセージが送られ、NextActivity が立ち上がる
        startActivity(intent);
    }
}

Activity を起動し、処理結果を期待するメッセージング

新しい Activity を呼び出し、その Activity から元の Activity へと Intent を戻すことで、何らかの処理の結果を呼び出し元に通知することができるものです。

// 呼び出し元の Activity
public class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 新しい Activit を呼び出す
        // 戻りの Intent を期待する場合は、Activity#startActivityForResult(Intent, int) を使用する
        startActivityForResult(new Intent(this, SubActivity.class), SubActivity.REQUEST_CODE_HOGE);
    }

    // 新しい Activity から戻ってくる Intent を受け取るコールバック
    // 新しい Activity が全面に出る場合は、onRestart()の前、ダイアログのように一部を覆って表示される場合は、onResume()の前で呼ばれる
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        // super.onActivityResult(int, int, Intent) の呼び出しは、条件に関係なくすること
        // Fragment から startActivityForResult(Intent, int) した場合の戻りの判定ができなくなってしまう
        super.onActivityResult(requestCode, resultCode, data);

        // requestCode には、startActivityForResult(Intent, int) の第 2 引数で指定したものが来る
        // resultCode には、呼び出し先で setResult(int, Intent) をコールした時の第 1 引数が来る
        // data には、呼び出し先で setResult(int, Intent) をコールした時の第 2 引数が来る 
        switch (requestCode) {
            case SubActivity.REQEUST_CODE_HOGE:
                 // REQUEST_CODE_HOGE の戻りが来た時の処理
                 return;
            default:
                 // 知らない requestCode の戻りが来た時の処理
                 return;
        }
    }
}
// 呼び出し先の Activity
public class SubActivity extends Activity {
    public static final int REQUEST_CODE_HOGE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 何かする

        // 呼び出し元に返す Intent オブジェクトをセットする
        // 第 1 引数には、RESULT_OK または RESULT_CANCELLED、あるいは、RESULT_FIRST_USER を起点にした独自の int 型定数を使う
        setResult(RESULT_OK, new Intent());
        finish();
    }
}

Service を起動する Intent

Service を呼び出します。Service そのものについては、別の章で解説します。

public class MyActivity extends Activity {
    private ServiceConnection mServiceConnection;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // サービスのバインド要求
        bindService(new Intent(this, MyService.class), mServiceConnection, Context.BIND_AUTO_CREATE);

        // サービスの開始要求
        startService(new Intent(this, MyIntentService.class));
    }
}

Intent の Broadcast

アプリ内、アプリ外問わず、Intent を全体へ投げかける仕組みのことを、ブロードキャストと言います。
Android のシステムでは、端末の状態を全アプリに通知するための仕組みとして利用しています。

BroadcastReceiver

ブロードキャストされる Intent を受信するためのコンポーネントです。

その宣言の仕方に依って、使われ方が異なることに注意します。

BroadcastReceiver のライフサイクル

BroadcastReceiver には、ブロードキャストされた Intent を受け取るためのコールバックメソッドが用意されています。

public class MyBroadcastReceiver extends BroadcastReceiver {
    // Broadcast された Intent を受け取るコールバック
    @Override
    public void onReceive(Context context, Intent intent) {

    }
}

BroadcastReceiver のライフサイクルは、このonReceive(Context, Intent)メソッドが実行されている間となります。
つまり、onReceive(Context, Intent)メソッドの処理が終わると、BroadcastReceiver のライフサイクルが終わることになります。

また、BroadcastReceiver が動作しているプロセスは、最も優先順位の高いフォアグラウンドプロセスとして扱われます。
つまり、BroadcastReceiver のライフサイクルが終了したプロセスは、そのプロセスで動作している他のコンポーネントが持つ優先順位になります。
もし、BroadcastReceiver のみがプロセス上で動作していた場合、BroadcastReceiver のライフサイクルが終了すると、そのプロセスは空プロセスとして扱われ、システムが優先してプロセスを kill するようになります。

よって、この 2 つの特徴から、BroadcastReceiver の中で非同期処理(Service を除く)を実行することは良くない実装となります。
なぜなら、非同期処理が終了する以前に、プロセスが終了してしまう可能性があるからです。

Static BroadcastReceiver

AndroidManifest に宣言される BroadcastReceiver です。

アプリがインストールされている間はずっと、BroadcastReceiver が動作し、ブロードキャストされる Intent の監視を続けます。
ただし、Honeycomb 以後の Android では、アプリのインストール直後は、アプリの状態が待機状態として扱われるようになったため、アプリを一度でも起動しないと、ブロードキャストされる Intent が受信できなくなります。

Dynamic BroadcastReceiver

Activity などの Context で動的に登録/解除が行われる BroadcastReceiver です。

Intent を Broadcast する

LocalBroadcastManager

特に、アプリ内全体へのブロードキャストを行う仕組みとして、LocalBroadcastManagerがあります。
これによって、他のアプリには知られたくない Broadcast が実現出来ます。

これとは別に、ブロードキャスト時にパーミッションを設定し、そのパーミッションを得ているアプリのみがブロードキャストを受信できるようにする方法もあります。この場合、パーミッションを持っていれば、他のアプリでもブロードキャストの受信が可能となります。

今回は、より軽量に利用可能な LocalBroadcastManager を使って解説します。

注:LocalBroadcastManager では、マルチプロセスをサポートしていないため、プロセスの異なるコンポーネントへのブロードキャストは上手く動作しない。

// LocalBroadcastManager は Support Package に含まれている
import android.support.v4.content.LocalBroadcastManager;

public class MyActivity extends Activity {
    @Override
    protected void onStart() {
        super.onStart();

        
    }
}

Notification

Android のステータスバー(画面上部の、時間や電波状況を表示している領域)に、アプリからのお知らせを表示する仕組みを Notification と言います。

Notification の仕組み

PendingIntent

NotificationBuilder

GitHub Pagesへ移行しましたmixi-inc.github.ioへお願いします。

Clone this wiki locally