NotificationListenerService with RemoteController.

2019-04-15 00:44发布

问题:

The Problem

I want to implement the new RemoteController API, which has been introduced with API19 (Kitkat,4.4). The API requires me to implement a class which extends from NotificationListenerService (introduced with API18) and implements the RemoteController.OnClientUpdateListener interface. (Further information on how to implement all this can be found here and a very helpful example project can be found here)

Everything's working as it should on my API19 (Kitkat,4.4) device and every other API<18 device. However the problem is, that the app crashes right away on API18 (Jelly bean, 4.3), because the "android.service.notification.NotificationListenerService" is being sent and starts my RemoteService, which then crashes the app instantly with the following Stacktrace:

05-24 09:32:48.945      893-893/com.example E/AndroidRuntime﹕ FATAL EXCEPTION: main
java.lang.RuntimeException: Unable to instantiate service com.example.RemoteService: java.lang.ClassNotFoundException: Didn't find class "com.example.RemoteService" on path: DexPathList[[zip file "/data/app/com.example-1.apk"],nativeLibraryDirectories=[/data/app-lib/com.example-1, /system/lib]]
        at android.app.ActivityThread.handleCreateService(ActivityThread.java:2561)
        at android.app.ActivityThread.access$1600(ActivityThread.java:141)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1338)
        at android.os.Handler.dispatchMessage(Handler.java:99)
        at android.os.Looper.loop(Looper.java:137)
        at android.app.ActivityThread.main(ActivityThread.java:5103)
        at java.lang.reflect.Method.invokeNative(Native Method)
        at java.lang.reflect.Method.invoke(Method.java:525)
        at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:737)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:553)
        at dalvik.system.NativeStart.main(Native Method)
 Caused by: java.lang.ClassNotFoundException: Didn't find class "com.example.RemoteService" on path: DexPathList[[zip file "/data/app/com.example-1.apk"],nativeLibraryDirectories=[/data/app-lib/com.example-1, /system/lib]]
        at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:53)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:501)
        at java.lang.ClassLoader.loadClass(ClassLoader.java:461)
        at android.app.ActivityThread.handleCreateService(ActivityThread.java:2558)
        at android.app.ActivityThread.access$1600(ActivityThread.java:141
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1338)
...

This is caused by the simple fact, that RemoteController and therefore the implemented interface RemoteController.OnClientUpdateListener is not available on API18, but NotificationListenerService is. The intent is not being sent on every device

What I've tried so far

  • Put another layer of abstraction on it and build a simple "if (API>=19)" check. I tried to do this by using a second empty NotificationListenerService which would just work as a kind of callthrough Service. I would put that second Service in my AndroidManifest, so that it receives the intent instead, and then I'd just put the "if (API>=19)" check in its onCreate and start my actual RemoteService from there. The result is, that my App doesn't crash any more (yay). Unfortunately it also doesn't work in API19 Kitkat any more :(. I think that's because I have to register my RemoteService as a listener in my AndroidManifest directly, or otherwise it won't receive anything.

Code snippets

RemoteService.java:

public class RemoteService extends NotificationListenerService
    implements RemoteController.OnClientUpdateListener {
...
}

AndroidManifest.xml:

<manifest ...>
    <application ...>
        <service android:name="com.example.RemoteService"
             android:label="@string/app_name"
             android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
            <intent-filter>
                <action android:name="android.service.notification.NotificationListenerService" />
            </intent-filter>
        </service>
    </application>
</manifest>

回答1:

The solution

Put a bool in an xml file in values and values-v19. Set it to false in values and to true in values-v19. Now set the NotificationListenerService's "android:enabled" tag to this bool.

./values/bool_switches.xml:

<resources>
    <bool name="remotecontrollerservice_enabled">false</bool>
</resources>

./values-v19/bool_switches.xml:

<resources>
    <bool name="remotecontrollerservice_enabled">true</bool>
</resources>

./AndroidManifest.xml:

...
<service android:name=".services.RemoteControllerService"
         android:label="@string/scrobblingservice_string"
         android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
         android:enabled="@bool/remotecontrollerservice_enabled">
    <intent-filter>
        <action android:name="android.service.notification.NotificationListenerService"/>
    </intent-filter>
</service>
...


回答2:

Just disable the service with android:enabled="false", then in Application.onCreate enable it if the device is running KitKat or newer.