I recently implemented an hybrid cordova based app for one of our customers and added push notifications to it as per MFP documentation.
Almost everything works fine, registration for broadcast push events, sending and receiving them via MFP server and processing them in Javascript. If the app is not active, the push notification is shown on the notification bar, which is ok.
However, tapping on it does not start the app and thus my javascript code never receives the notification.
Instead Logcat shows this exception:
03-10 15:47:37.489: E/Parcel(3190): Class not found when unmarshalling: com.worklight.wlclient.push.GCMIntentService$Message
03-10 15:47:37.489: E/Parcel(3190): java.lang.ClassNotFoundException: com.worklight.wlclient.push.GCMIntentService$Message
03-10 15:47:37.489: E/Parcel(3190): at java.lang.Class.classForName(Native Method)
03-10 15:47:37.489: E/Parcel(3190): at java.lang.Class.forName(Class.java:308)
03-10 15:47:37.489: E/Parcel(3190): at java.lang.Class.forName(Class.java:272)
03-10 15:47:37.489: E/Parcel(3190): at android.os.Parcel.readParcelableCreator(Parcel.java:2275)
03-10 15:47:37.489: E/Parcel(3190): at android.os.Parcel.readParcelable(Parcel.java:2239)
03-10 15:47:37.489: E/Parcel(3190): at android.os.Parcel.readValue(Parcel.java:2146)
03-10 15:47:37.489: E/Parcel(3190): at android.os.Parcel.readArrayMapInternal(Parcel.java:2479)
03-10 15:47:37.489: E/Parcel(3190): at android.os.BaseBundle.unparcel(BaseBundle.java:221)
03-10 15:47:37.489: E/Parcel(3190): at android.os.BaseBundle.getString(BaseBundle.java:918)
03-10 15:47:37.489: E/Parcel(3190): at android.content.Intent.getStringExtra(Intent.java:5440)
03-10 15:47:37.489: E/Parcel(3190): at com.android.server.am.ActivityStackSupervisor.startActivityLocked(ActivityStackSupervisor.java:1871)
03-10 15:47:37.489: E/Parcel(3190): at com.android.server.am.ActivityStackSupervisor.startActivityMayWait(ActivityStackSupervisor.java:1409)
03-10 15:47:37.489: E/Parcel(3190): at com.android.server.am.ActivityManagerService.startActivityInPackage(ActivityManagerService.java:5565)
03-10 15:47:37.489: E/Parcel(3190): at com.android.server.am.PendingIntentRecord.sendInner(PendingIntentRecord.java:257)
03-10 15:47:37.489: E/Parcel(3190): at com.android.server.am.PendingIntentRecord.send(PendingIntentRecord.java:197)
03-10 15:47:37.489: E/Parcel(3190): at android.content.IIntentSender$Stub.onTransact(IIntentSender.java:64)
03-10 15:47:37.489: E/Parcel(3190): at android.os.Binder.execTransact(Binder.java:446)
03-10 15:47:37.489: E/Parcel(3190): Caused by: java.lang.ClassNotFoundException: Didn't find class "com.worklight.wlclient.push.GCMIntentService$Message" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
03-10 15:47:37.489: E/Parcel(3190): at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
03-10 15:47:37.489: E/Parcel(3190): at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
03-10 15:47:37.489: E/Parcel(3190): at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
03-10 15:47:37.489: E/Parcel(3190): ... 17 more
03-10 15:47:37.489: E/Parcel(3190): Suppressed: java.lang.ClassNotFoundException: com.worklight.wlclient.push.GCMIntentService$Message
03-10 15:47:37.489: E/Parcel(3190): at java.lang.Class.classForName(Native Method)
03-10 15:47:37.489: E/Parcel(3190): at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
03-10 15:47:37.489: E/Parcel(3190): at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
03-10 15:47:37.489: E/Parcel(3190): at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
03-10 15:47:37.489: E/Parcel(3190): ... 18 more
03-10 15:47:37.489: E/Parcel(3190): Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack available
I checked the dexed classes and the inner classes are present. Nonetheless the dex classloader refuses to find/load them. I also double checked the activity name, permissions and everything else I dug up on Stack Overflow and the like, but to no avail. The Intent is received ok by the app which just can't handle it because that inner class is missing on the dex classloader.
This is with the debug build without ProGuard. I also migrated the project from Eclipse/ADT/ANT to Android Studio/Gradle only to face the same issue, so I don't think this is related to the GCM library or the build process itself. It's probably something with the dex path, but I have no clue on how to analyse or fix this.
Due to the size of the Libraries involved (Google Play and Mobile First) I had to set dex.force.jumbo=true in the project.properties.
Here's my AndroidManifest:
<?xml version="1.0" encoding="UTF-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ag.entega.enetz.promt" android:versionCode="15" android:versionName="1.12">
<uses-sdk android:minSdkVersion="16" android:targetSdkVersion="22"/>
<supports-screens android:smallScreens="false" android:normalScreens="true" android:largeScreens="true"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<permission android:name="ag.entega.enetz.promt.permission.C2D_MESSAGE" android:protectionLevel="signature"/>
<uses-permission android:name="ag.entega.enetz.promt.permission.C2D_MESSAGE"/>
<uses-permission android:name="com.google.android.c2dm.permission.RECEIVE"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS"/>
<uses-permission android:name="android.permission.USE_CREDENTIALS"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.CAMERA"/>
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
<uses-feature android:name="android.hardware.wifi"/>
<application android:label="@string/app_label" android:icon="@drawable/icon" android:allowBackup="false" android:largeHeap="true">
<meta-data android:name="com.google.android.gms.version" android:value="@integer/google_play_services_version"/>
<activity android:name="promt" android:label="@string/app_label" android:configChanges="orientation|keyboardHidden|screenSize" android:launchMode="singleTask" android:theme="@android:style/Theme.Translucent.NoTitleBar" android:screenOrientation="sensor">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter>
<action android:name="ag.entega.enetz.promt.NOTIFICATION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<!-- Preference Activity -->
<activity android:name="com.worklight.common.WLSettingActivity" android:label="Worklight Settings"/>
<!-- UI Activity for displaying native dialogs -->
<activity android:name="com.worklight.wlclient.ui.UIActivity"/>
<!-- Push service -->
<!-- In order to use the c2dm library, an application must declare a class with the name C2DMReceiver, in its own package, extending com.google.android.c2dm.C2DMBaseReceiver
It must also include this section in the manifest, replacing "com.google.android.apps.chrometophone" with its package name. -->
<service android:name="GCMIntentService"/>
<service android:name="ForegroundService"/>
<!-- Only google service can send data messages for the app. If permission is not set - any other app can generate it -->
<receiver android:name="com.worklight.androidgap.push.WLBroadcastReceiver" android:permission="com.google.android.c2dm.permission.SEND">
<!-- Receive the actual message -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.RECEIVE"/>
<category android:name="ag.entega.enetz.promt"/>
</intent-filter>
<!-- Receive the registration id -->
<intent-filter>
<action android:name="com.google.android.c2dm.intent.REGISTRATION"/>
<category android:name="ag.entega.enetz.promt"/>
</intent-filter>
</receiver>
<activity android:name="com.google.zxing.client.android.CaptureActivity" android:screenOrientation="landscape" android:configChanges="orientation|keyboardHidden" android:theme="@android:style/Theme.NoTitleBar.Fullscreen" android:windowSoftInputMode="stateAlwaysHidden">
<intent-filter>
<action android:name="com.phonegap.plugins.barcodescanner.SCAN"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name="com.google.zxing.client.android.encode.EncodeActivity" android:label="@string/share_name">
<intent-filter>
<action android:name="com.phonegap.plugins.barcodescanner.ENCODE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
</application>
My question is: How to solve this inner class not found exception? Any help is very much appreciated.
Ok, I got it. Thanks for all your suggestions!
First of all, I unnecessarely included the google play libs into my project. It's recommended by Google and usually required, but not with MFP. The Mobile First Platform brings everything to the game to use GCM push notifications. But after removing the lib the error still came up.
So I checked out the Sample Project from IBM, set it up and run it and voilá, the App opens when tapping on the notification. So I double checked the AndroidManifest and found two differences:
First of all I was missing a dot in front of the app name "promt". So correctly it's ".promt".
Second the intent filter for notifications was missing the activity name:
<intent-filter>
<action android:name="ag.entega.enetz.promt.NOTIFICATION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
must be
<intent-filter>
<action android:name="ag.entega.enetz.promt.promt.NOTIFICATION"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
After fixing this no more errors and the app opens when tapping on the notification.
Thus @IdanAdar and @Vikin K were correct to point at the AndroidManifest.xml. Thanks again, guys!
Beats me why the exception does not point to the issue at hand, though.