GCM and Parse notification conflict

2019-02-03 19:54发布

问题:

I need my android app to work with two push services, GCM and parse. My problem is that I can't find a way for register correctly to Parse, get parse notification and GCM notification too. I can reach all these things individually, but never together. My current implementation looks this way:

<!-- GCM BradcastReceiver & Service -->
    <service android:name=".GcmIntentService"
        android:enabled="true"/>
    <meta-data android:name="com.google.android.gms.version"
        android:value="@integer/google_play_services_version" />
    <receiver
        android:name=".GcmBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter
            android:priority="100"> <!-- higher priority to GCM messages -->

            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <action android:name="com.twentyLines.GCM_NOTIFICATION_ACTION" />
            <category android:name="com.twentyLines.app" />
        </intent-filter>
    </receiver>

This is the broadcastReceiver for GCM, and the one below is the other receiver for parse:

<service android:name="com.parse.PushService" />

    <receiver android:name="com.parse.GcmBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter android:priority="0">

            <!--<action android:name="com.google.android.c2dm.intent.RECEIVE" />-->
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="com.twentyLines.app" />
        </intent-filter>
    </receiver>
    <!-- Can remove this if application is already registered for GCM -->
    <receiver android:name="com.parse.ParseBroadcastReceiver" android:exported="false" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.USER_PRESENT" />
            <action android:name="com.twentyLines.PARSE_NOTIFICATION_ACTION" />
        </intent-filter>
    </receiver>

I've tried to add a custom broadcast receiver to handle just parse notification, so I can avoid it to handle GCM too. I've done it like this:

<receiver android:name="com.twentyLines.app.ParseBroadcastReceiver" android:exported="false" >
        <intent-filter>
            <action android:name="com.twentyLines.PARSE_NOTIFICATION_ACTION" />
        </intent-filter>
    </receiver>

This is the implementation of my BroadcastReceiver for GCM, that avoid parse notification to be displayed.

public class GcmBroadcastReceiver extends WakefulBroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {

        boolean isFromParse = intent.getExtras().get("action").equals("com.twentyLines.PARSE_NOTIFICATION_ACTION");
        if (isFromParse)
            return;

        // Explicitly specify that GcmIntentService will handle the intent.
        ComponentName comp = new ComponentName(context.getPackageName(),
                GcmIntentService.class.getName());
        // Start the service, keeping the device awake while it is launching.
        startWakefulService(context, (intent.setComponent(comp)));
        setResultCode(Activity.RESULT_CANCELED);
    }
}

And this is the implementation for my PARSE custom BroadcastReceiver that SHOULD avoid GCM to be showed. The parse custom receiver is never invoked, I think because the com.parse.GcmBroadcastReceiver handle the notification itself instead of pass them. The result is that, when I send a notification from my server to GCM receiver, this is retrieved from both, and double notification is showed. (double is already good, every time I uninstall and reinstall my app Parse register another user.. the "UniqueId" I send to parse every time is not so UNIQUE for them).

What have I tried? I'm really getting crazy, I've tried about everything.

 - I've read all related questions, and no one is good for me;
 - I've tried to remove the parse receiver, and this cause a parse exception (so doesn't register to parse)
 - I've tried to set the intent to RESULT_CANCELED, but in some way parse get it before GCM, so isn't working when I use GCM. 
 - I've changed about all using cowboy-coding, and it still not work... 

Any help will be really welcome. Thank you guys!

EDIT - ADD WORKING MANIFEST

<!-- GCM BradcastReceiver & Service -->
    <service android:name=".GcmIntentService"
        android:enabled="true"/>
    <meta-data android:name="com.google.android.gms.version"
    android:value="@integer/google_play_services_version" />

    <receiver
        android:name=".GcmBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter
            android:priority="2"> <!-- higher priority to GCM messages -->

            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <action android:name="com.twentyLines.GCM_NOTIFICATION_ACTION" />
            <category android:name="com.twentyLines.app" />
        </intent-filter>
    </receiver>

    <!-- Parse service broacastReceiver and receiver. -->
    <service android:name="com.parse.PushService" />

    <receiver android:name="com.parse.GcmBroadcastReceiver"
        android:permission="com.google.android.c2dm.permission.SEND" >
        <intent-filter android:priority="1">

            <action android:name="com.google.android.c2dm.intent.RECEIVE" />
            <action android:name="com.google.android.c2dm.intent.REGISTRATION" />
            <category android:name="com.twentyLines.app" />
        </intent-filter>
    </receiver>
    <!-- Can remove this if application is already registered for GCM -->
    <receiver android:name="com.parse.ParseBroadcastReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.USER_PRESENT" />
            <action android:name="com.twentyLines.PARSE_NOTIFICATION_ACTION" />
        </intent-filter>
    </receiver>

    <receiver android:name="com.twentyLines.app.ParseBroadcastReceiver" android:exported="false" >
        <intent-filter>

            <action android:name="android.intent.action.BOOT_COMPLETED" />
            <action android:name="android.intent.action.USER_PRESENT" />
            <action android:name="com.twentyLines.PARSE_NOTIFICATION_ACTION" />
        </intent-filter>
    </receiver>

EDIT 2: HOW I REGISTER PARSE AND GCM

I register Parse in my application class:

Parse.initialize(this, PARSE_APP_KEY_VALUE, PARSE_CLIENT_KEY_VALUE);
    PushService.setDefaultPushCallback(getApplicationContext(), MainActivity.class);
    final ParseInstallation installation = ParseInstallation.getCurrentInstallation();
    final String  androidId = Settings.Secure.getString(getApplicationContext().getContentResolver(), Settings.Secure.ANDROID_ID);
    Handler handler = new Handler();
    handler.postDelayed(new Runnable() {
                            @Override
                            public void run() {
                                installation.put("UniqueId", androidId);
                                installation.saveInBackground(new SaveCallback() {
                                    @Override
                                    public void done(ParseException e) {
                                        Log.d("Parse installation saved in background", "If operation successfull, 'e' have to be NULL e= " + e);
                                    }
                                });
                            }
                        }, 5000
    );

And I get gcm registration_id in my MainActivity:

// Check if there is a saved registration_id in shared_prefs,
// or if app version has changed
private String getSavedRegistrationId() {
    final SharedPreferences gcmShared = getGCMSharedPrefss();
    String registrationId = gcmShared.getString(Constant.GCM_REGISTRATION_ID_KEY, "");
    if (registrationId.isEmpty()) {
        Log.i("GCM", "Registration not found.");
        return "";
    }

    int registeredVersion = gcmShared.getInt(Constant.APP_VERSION_KEY, Integer.MIN_VALUE);
    int currentVersion = getAppVersion(this);
    if (registeredVersion != currentVersion) {
        clearSavedGCMRegistrationId();
        Log.i("GCM", "App version changed.");
        return "";
    }
    return registrationId;
}

// If there isn't, request one
private void registerInBackground() {
    new AsyncTask<Void, Void, String>() {
        @Override
        protected String doInBackground(Void... params) {
            String msg;
            try {
                if (_gcm == null) {
                    _gcm = GoogleCloudMessaging.getInstance(MainActivity.this);
                }
                String regid = _gcm.register(Constant.GCM_SENDER_ID);
                msg = "Device registered, registration ID=" + regid;
                sendRegistrationIdToBackend(regid);
                storeRegistrationId(regid); // Save reg_id to shared
            } catch (IOException ex) {
                msg = "Error :" + ex.getMessage();
                // If there is an error, don't just keep trying to register.
                // Require the startupLoggedUser to click a button again, or perform
                // exponential back-off.
            }
            return msg;
        }

        @Override
        protected void onPostExecute(String msg) {
            Log.d("GCM registration", msg);
        }
    }.execute(null, null, null);
}

回答1:

We have two broadcast receivers here to listen c2dm intent

  1. .GcmBroadcastReceiver : lets call this as GCM receiver... Parse Push will never come to this receiver.
  2. com.parse.GcmBroadcastReceiver : lets call this as Parse receiver

As you have given higher priority to GCM receiver, broadcast will come to GCM receiver first and then to Parse receiver. This does not guarantee that Broadcast will not go to Parse receiver. You need to add abortBroadcast(); as last line in onReceive method to make sure that Parse receiver is not triggered when we have GCM receiver working.

As per Parse Push notification guidelines, Push is received with a specific intent action. Data is sent to broadcast receiver registered with that action. In your case, if push is received with action "com.twentyLines.PARSE_NOTIFICATION_ACTION", you can have a custom broadcast receiver to listen to this action. In that broadcast receiver you can fetch data by below code,

try {
      String action = intent.getAction();
      String channel = intent.getExtras().getString("com.parse.Channel");
      JSONObject json = new JSONObject(intent.getExtras().getString("com.parse.Data"));

      Log.d(TAG, "got action " + action + " on channel " + channel + " with:");
      Iterator itr = json.keys();
      while (itr.hasNext()) {
        String key = (String) itr.next();
        Log.d(TAG, "..." + key + " => " + json.getString(key));
      }
    } catch (JSONException e) {
      Log.d(TAG, "JSONException: " + e.getMessage());
    }

When there is GCM push, this custom receiver will never get broadcast event as the C2DM broadcast is being aborted in GCM receiver (".GcmBroadcastReceiver")

Hope This Helps!