androidbroadcastreceiveraccessibilityandroid-broadcast

How to have a Manifest-declared broadcast receiver communicate with an activity or service?


This should be obvious but I can't find the solution. Many people have asked it and the reply is usually to just switch to a context-registered receiver instead. I have an accessibility service which needs to be notified when the Manifest-declared receiver picks up something. Obviously this can't be done with an interface since I can't find a way to get the instance of the receiver. Here's some code:

Manifest:

<service
    android:name=".MyAccessibilityService"
    android:exported="true"
    android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"
    android:process=":listenerProcess4">
    <intent-filter>
        <action android:name="android.accessibilityservice.AccessibilityService" />
    </intent-filter>

    <meta-data
        android:name="android.accessibilityservice"
        android:resource="@xml/accessibilityservice" />
</service>

<receiver
    android:name=".MyBroadcastReceiver"
    android:exported="false"
    android:process=":listenerProcess4">
    <intent-filter>
        <action android:name="roastie_toastie" />
    </intent-filter>
</receiver>

Receiver:

public class MyBroadcastReceiver extends BroadcastReceiver {
    BroadcastReceivedListener mBroadcastReceivedListener;

    public void setBroadcastReceivedListener(BroadcastReceivedListener broadcastReceivedListener) {
        mBroadcastReceivedListener = broadcastReceivedListener;
    }

    @Override
    public void onReceive(Context context, Intent intent) {
        mBroadcastReceivedListener.broadcastReceived();
    }

    public interface BroadcastReceivedListener {
        void broadcastReceived();
    }
}

MyAccessibilityService:

public class MyAccessibilityService extends AccessibilityService implements MyBroadcastReceiver.BroadcastReceivedListener {

    boolean lightsAreOn = true;

    @Override
    public void broadcastReceived() {
        lightsAreOn = false;
    }
}

Solution

  • I asked a couple of times for concrete scenarios, and you did not really provide any, so I am going to answer with an abstract scenario: suppose that your broadcast is designed to send a command to the service.

    In that case, you could:

    1. Implement a singleton (here called CommandBus, for lack of a better idea of a name). Ideally, that singleton would be set up by a dependency inversion framework (e.g., Dagger/Hilt).

    2. Have CommandBus expose some reactive way to get commands to the service. If you were using Kotlin, that could be a SharedFlow. Since you are using Java, that could be an RxJava Observable, or perhaps a simple callback.

    3. Have MyAccessibilityService get access to the CommandBus singleton and use whatever you set up in step #2 to find out about commands.

    4. Have CommandBus expose an API to hand it commands to deliver to the service (e.g., a sendCommand() method).

    5. Have MyBroadcastReceiver get access to the CommandBus singleton and call the method from step #4 in onReceive().

    At this point, when MyBroadcastReceiver receives a broadcast, it tells CommandBus to send the command, which then flows over to MyAccessibilityService, which goes and does something.