Android's GcmReceiver/GcmListenerService Force

2019-03-06 00:33发布

问题:

I'm running into an issue with my implementation of GCM on Android, min SDK 15. I am perfectly able to receive push notifications from GCM, and my GcmListenerService implementation has a functioning onMessageReceived(...) callback. My issue, though, is that whenever one or the other performs a receive, it forces the application into the foreground!

My expected behavior is that I am able to receive these push notifications as they come, whether or not the application is in the background, and then to create a notification in the tray that starts an activity should a payload require it.

The flow that causes an issue is (physical device, Motorola Razr M xt907):

1. Launch application. Device key is verified from GCM instanceID.
2. Press Home on Android.
3. Press Recent Apps key; swipe away the application; wait a few seconds
4. Manually send a push notification to the device via GCM
5. Application receives the notification, and the application forces itself back into the foreground

If it helps, I threw the main application and receivers into separate processes to get an idea of what was actually happening in the background, and a truncated version of this is below:

NotificationsProcess I/MultiDex﹕ VM with version 1.6.0 does not have multidex support
NotificationsProcess I/MultiDex﹕ install
NotificationsProcess I/MultiDex﹕ MultiDexExtractor.load(/data/app/com.MyApp.MyAppmobile.alpha-1.apk, false)
NotificationsProcess I/MultiDex﹕ loading existing secondary dex files
NotificationsProcess I/MultiDex﹕ load found 1 secondary dex files
NotificationsProcess I/MultiDex﹕ install done
...
[... Main Application Code ... ]
...
PrivateProcess I/MultiDex﹕ VM with version 1.6.0 does not have multidex support
PrivateProcess I/MultiDex﹕ install
PrivateProcess I/MultiDex﹕ MultiDexExtractor.load(/data/app/com.MyApp.MyAppmobile.alpha-1.apk, false)
PrivateProcess I/MultiDex﹕ loading existing secondary dex files
PrivateProcess I/MultiDex﹕ load found 1 secondary dex files
PrivateProcess I/MultiDex﹕ install done
NotificationsProcess D/MyApp﹕ Network is connected via WIFI
NotificationsProcess D/MyApp﹕ Executing networkWentOnline() callbacks
NotificationsProcess D/MyApp﹕ Successfully connected to GoogleApiServices for Location Awareness.

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GcmListenerService finally comes alive
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
NotificationsProcess D/GCMMessageHandler﹕ From: 149994375514
NotificationsProcess D/GCMMessageHandler﹕ Message: null
NotificationsProcess D/GCMMessageHandler﹕ Data: Bundle[{gcm.notification.body=Hey there dude, I like text =), collapse_key=com.MyApp.MyAppmobile.alpha}]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
GcmListenerService is finished
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

NotificationsProcess D/MyApp﹕ [... Main Application Code ... ]
NotificationsProcess D/MyApp﹕ [... Main Application Code ... ]
PrivateProcess D/MyApp﹕ [... Main Application Code ... ]
PrivateProcess D/MyApp﹕ [... Main Application Code ... ]
PrivateProcess D/MyApp﹕ [... Main Application Code ... ]
PrivateProcess D/MyApp﹕ [... Main Application Code ... ]
PrivateProcess D/MyApp﹕ [... Main Application Code ... ]
PrivateProcess V/TDCollateJSON﹕ SQLite3 handle is 1417181168
PrivateProcess D/MyApp﹕ [... Main Application Code ... ]
PrivateProcess D/MyApp﹕ [... Main Application Code ... ]
PrivateProcess D/MyApp﹕ [... Main Application Code ... ]
PrivateProcess D/MyApp﹕ [... Main Application Code ... ]
PrivateProcess D/MyApp﹕ [... Main Application Code ... ]

My Android manifest is setup as such, with the following items found under the main <application> tag:

<receiver
    android:name="com.google.android.gms.gcm.GcmReceiver"
    android:process="NotificationsProcess"
    android:exported="true"
    android:permission="com.google.android.c2dm.permission.SEND" >
    <intent-filter
        android:process="NotificationsProcess">
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
        <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
    </intent-filter>
</receiver>
<service
    android:name=".GCMMessageHandler"
    android:process="NotificationsProcess"
    android:exported="false">
    <intent-filter
        android:process="NotificationsProcess">
        <action android:name="com.google.android.c2dm.intent.RECEIVE" />
    </intent-filter>
</service>
<service
    android:name=".GCMInstanceIDListenerService"
    android:process="NotificationsProcess"
    android:exported="false">
    <intent-filter
        android:process="NotificationsProcess">
        <action android:name="com.google.android.gms.iid.InstanceID"/>
    </intent-filter>
</service>
<service
    android:name=".RegistrationIntentService"
    android:process="NotificationsProcess"
    android:exported="false">
</service>

[... Other app activities ...] 

<activity
    android:name=".activities.SyncActivity"
    android:label="@string/app_name"
    android:theme="@style/Theme.AppCompat.NoActionBar"
    android:screenOrientation="portrait">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
</activity> 

I have the distinct feeling like this is a simple manifest issue, but I wouldn't be surprised if this is instead a much more subtle issue. If I can offer any more application output, please let me know!

Any experience, wisdom, or paths to try for this are greatly appreciated!

EDIT:

I am including the GcmListenerService implementation. Notice the only thing I do is actually create a notification and post it. With no interaction from the user, the application simply comes to the foreground - no tap, no swipe, just poof foreground.

@Override
public void onMessageReceived(String from, Bundle data) {

    String message = data.getString("gcm.notification.body");
    Log.d("GCMMessageHandler", "From: " + from);
    Log.d("GCMMessageHandler", "Message: " + message);

    MyApp.WAS_STARTED_FROM_NOTIFICATION = true;

    NotificationCompat.Builder mBuilder =
            new NotificationCompat.Builder(this)
                    .setSmallIcon(R.drawable.ic_launcher)
                    .setContentTitle("Hello from MyApp")
                    .setContentText(message);

    NotificationManager mNotificationManager =
            (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
    mNotificationManager.notify(MY_APP_NOTIFICATION_ID, mBuilder.build());

}

回答1:

Thanks to @petey in the question's comment history, I went down the right path to figure out what was going on in the application.

His comment posed looking through the Application launch to make sure something else in the flow wasn't forcing an Activity intent to fire. Something I did not realize about Android (as a fledgling developer) was the full lifecycle of the Application class, and its relation to Intent receivers and filters.

My issue, in a nutshell, was that the MyApplication class (extended from Application) contained logic that fired an Intent based on a few other saved states of the app - there was nothing guarding against this should a filter have forced the app open, and these receivers were the first time the application was to be started in any way other than the device's launcher.

To anyone else experiencing similar issues: Intent filters, in order to function, will always fire your manifested Application, as per standard android behavior. Make SURE that your core logic flow is able to handle the fact that your launch may come from places other than a user tapping on you!

P.S.: To any developers to whom this is intensely obvious, be gentle, and help explain the big-overview Android app lifecycle concepts to your peers ;)