unittest
Andoird

BroadcastReceiverのUnitTest時にonReceiveが走らない問題(解決したが根本原因分からず)

More than 1 year has passed since last update.

Unit Test時にBroadcastReceiverのonReceiveが走らない

BroadcastReceiverのUnit Testをしようと以下のようなコードを書いたが、テスト対象のonReceiveが走らないという問題が発生した。

Test.java
    @Test
    public void onReceiveTest() throws Exception {
        Intent intent = new Intent();
        intent.setAction("com.example.subaru44k.ORIGINAL_INTENT");
        mContext.sendBroadcast(intent);

        Thread.sleep(50000);
    }
AndroidManifest.xml
        <receiver
            android:name=".SampleReceiver"
            android:enabled="true"
            android:exported="true">
            <intent-filter>
                <action android:name="com.example.subaru44k.ORIGINAL_INTENT" />
            </intent-filter>
        </receiver>
SampleReceiver.java
public class SampleReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Toast.makeText(context, "sample broadcast received", Toast.LENGTH_SHORT).show();
    }
}

うまくいく方法

以下のような形で、テスト対象にActivityを作成して、ActivityTestRuleにて起動してやれば受信できるようになる。

Test.java
    @Rule
    public ActivityTestRule<MainActivity> mActivityTestRule = new ActivityTestRule<MainActivity>(MainActivity.class);

    @Test
    public void onReceiveTest() throws Exception {
        Intent intent = new Intent();
        intent.setAction("com.example.subaru44k.ORIGINAL_INTENT");
        mContext.sendBroadcast(intent);

        Thread.sleep(50000);
    }

原因の考察(間違っていた)

これはおそらく、テスト実行直後は対象のアプリがストップ状態になっていて、Intentが受けられないのだと思う。
しかし、一度アプリを立ち上げておけばストップ状態は解除されるはずだし、
その状態からテスト書いたならばターゲットアプリの更新だから、ストップ状態にならないはずだが、なぜだろうと思ったら・・・

Android Studioにおける更新は以下のコマンドではなく、

$ adb install -r TARGET_APP

以下のコマンドのようで、

$ adb shell am force-stop com.example.subaru44k
$ adb push TARGET_APP /data/local/tmp/com.example.subaru44k.test
$ adb shell pm install -r "/data/local/tmp/com.example.subaru44k.test"

am force-stopには、アプリの強制停止&ストップ状態に遷移という効果があり、その影響でストップ状態に戻されていたようだ。
だからActivityTestRuleで明示的にアプリを起動してやれば受けられるようになる。

Serviceの場合はServiceTestRuleを使えばよいだろう。

違った

以下を見る限り上記認識は間違っている。

https://developer.android.com/about/versions/android-3.1.html

The platform defines two new intent flags that let a sender specify whether the Intent should be allowed to activate components in stopped application.

FLAG_INCLUDE_STOPPED_PACKAGES — Include intent filters of stopped applications in the list of potential targets to resolve against.
FLAG_EXCLUDE_STOPPED_PACKAGES — Exclude intent filters of stopped applications from the list of potential targets.
When neither or both of these flags is defined in an intent, the default behavior is to include filters of stopped applications in the list of potential targets.
Note that the system adds FLAG_EXCLUDE_STOPPED_PACKAGES to all broadcast intents.

SystemアプリはFLAG_EXCLUDE_STOPPED_PACKAGESをつけてintentを送るが、デフォルトではFLAG_INCLUDE_STOPPED_PACKAGESがついているので、stop状態でも受けることができる。
実際に上記テストアプリにて

intent.addFlags(Intent.FLAG_INCLUDE_STOPPED_PACKAGES);

をしてsendBroadcast(intent)してもターゲットアプリはIntent受けられてないので、原因は別のようだ。

根本原因分からないが、なんかまた混乱しそうなのでひとまず調査内容を投稿しておく。