Android (Kotlin) でブロードキャスト送受信の仕方を理解する

## きっかけ

最近、Android 開発を仕事で行っていますが、複数のアプリ間連携の方法を今まで知りませんでした。
最近知ったのですが、`Intent`と`BroadcastReceiver`を使用することで簡単にアプリ間連携が可能でした。

しかし、まったくの初心者であった自分にとっては？？？でした。。
そこで今回は 2 つのアプリを連携させる`Intent`の使い方について学ぶことができたので、共有します。

## 環境

- Android Studio 3.6.1
- Android 7.0 以降

## 概要

1 つの Android 端末に 2 つのアプリケーションをインストールし、その 2 つのアプリ間の連携方法について記述します。
今回作成するアプリは以下になります。

#### IntentBroadcast アプリ
- `Intent`を Android 端末アプリケーション内の全てのアプリにブロードキャストするアプリケーション

#### IntentReceiver アプリ
- ブロードキャストされた`Intent`を受信するアプリケーション

## 1. IntentBroadcast アプリについて

Activity 画面にボタンを配置し、そのボタンをタップすることで`Intent`を送信します。
ブロードキャストの方法は、以下のように`Intent`インスタンスを作成し、`sendBroadcast(intent)`を実行することで可能です。

```MainActivity.kt
import android.content.Intent
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // ボタン1配置
        button1.setOnClickListener {
            // インテントをブロードキャスト
            val intent = Intent("com.example.broadcast.MY_NOTIFICATION1")
            intent.putExtra("data", "Notice me senpai!")
            sendBroadcast(intent)
        }

        // ボタン2配置
        button2.setOnClickListener {
            // インテントをブロードキャスト
            val intent = Intent("com.example.broadcast.MY_NOTIFICATION2")
            intent.putExtra("data", "Hello, world!")
            sendBroadcast(intent)
        }
    }
}
```

ここで、`Intent`のインスタンス作成時に以下のように Action 文字列をセットしています。
`Intent`を受信するレシーバはこの`com.example.broadcast.MY_NOTIFICATION1`の部分を確認し、受け取るべき情報なのかを判断します。

そのため一意であればどんな文字列でも構いませんが、作法として「パッケージ名 + 任意の文字列」とするのがベターです。
(Android 端末内のアプリケーションのパッケージ名は基本的に一意であるためです。)

また、`Intent`は`putExtra()`を利用することで情報を追加登録することができます。
今回は`data`という名前でそれぞれ文字列をセットしました。

<br>
ちなみに以下の`activity_main.xml`、`strings.xml`は画面にボタンを設置するために記述しているだけで、ブロードキャスト処理には関係ありません。
上記のソースを動かす際のサンプル用として参考してください。

```activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center"
    android:orientation="vertical"
    android:padding="50dp"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/button1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button1" />

    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/button2" />
</LinearLayout>
```

```strings.xml
<resources>
    <string name="app_name">IntentBroadcast</string>
    <string name="button1">Button1</string>
    <string name="button2">Button2</string>
</resources>

```

## 2. IntentReceiver アプリについて

こちらは Android 端末内にブロードキャストされている`Intent`を受信します。
受信の方法はまず、`BroadcastReceiver`クラスを継承した、ブロードキャストを実際に受信するクラス(`MyBroadcastReceiver`クラス)を作成します。

そしてメイン画面に、先ほど作成した`MyBroadcastReceiver`クラスを利用するレシーバを準備することで`Intent`受信が可能となります。

```MyBroadcastReceiver.kt
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.widget.Toast

class MyBroadcastReceiver : BroadcastReceiver() {
    override fun onReceive(context: Context?, intent: Intent?) {
        // インテントに登録されている、名前"data"に対応する文字列をトーストで表示する
        Toast.makeText(context, intent?.getStringExtra("data"), Toast.LENGTH_SHORT).show()
    }
}
```

```MainActivity.kt
import android.content.IntentFilter
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // BroadcastReceiverを継承したクラス(MyBroadcastReceiver)を指定する
        // IntentFilterは何を呼び出すかを振り分けるもの
        // 2020/03/20: links_2_3_4 さん指摘反映
        // 画面回転時にメモリリークに関する警告表示を阻止
        this.applicationContext.registerReceiver(
            MyBroadcastReceiver(),
            IntentFilter("com.example.broadcast.MY_NOTIFICATION1")
        )
        this.applicationContext.registerReceiver(
            MyBroadcastReceiver(),
            IntentFilter("com.example.broadcast.MY_NOTIFICATION2")
        )
    }
}
```

ここで`MainActivity.kt`では、`Intent`の Action 文字列によってどのブロードキャストレシーバクラスを利用するかを設定しています。
(今回は同じブロードキャストレシーバクラス`MyBroadcastReceiver()`を設定しています。)

そして`Intent`の Action 文字列(`com.example.broadcast.MY_NOTIFICATION1`)が、`IntentFilter()`内でで記述した文字列と同じであれば、設定したブロードキャストクラス内の処理が実行されます。

ブロードキャストレシーバクラス`MyBroadcastReceiver.kt`では、`Intent`に登録された名前`data`の文字列をトーストで表示するようにしました。

## 挙動確認

1. IntentBroadcast アプリと IntentReceiver アプリを Android 端末にインストールします。
1. IntentReceiver アプリを起動し、ホームボタンを選択するなどしてバックグラウンドに移動させます。
1. IntentBroadcast アプリを起動し、画面上に設置したボタンをタップします。

うまくブロードキャスト送信・受信ができれば`Intent`に登録した`data`の文字列がトースト表示されます。
![スライド1.JPG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407150/831f1c75-19f8-6faf-4253-1072efd6bc0a.jpeg)

これで、IntentBroadcast アプリで操作した情報が IntentReceiver アプリに受け渡され、2 つのアプリ間連携ができました。

## 最後に

今回は、1 つのアプリケーションで`Intent`をブロードキャストを行い、もう 1 つのアプリケーションで`Intent`を受け取ってトースト表示する仕組みを紹介しました。
本記事ではとても単純なケースを紹介しましたが、この`Intent`を使えば、複数の Android アプリケーションでの連携が可能になります。
今後余裕があれば、この`Intent`のやり取りを利用した他のサンプルも実装・紹介できたらなと思います。

最後まで閲覧いただき、ありがとうございました。