2

When i send out a push notification, 5 of the same notifications appear on the device. Any reason why? Here are my application.java and manifest:

public class Application extends android.app.Application {

public Application() {
}

@Override
public void onCreate() {
    super.onCreate();
    Parse.setLogLevel(Parse.LOG_LEVEL_VERBOSE);

    // Initialize the Parse SDK.
    Parse.initialize(this, "__REMOVED KEY FOR SECURITY___", "__REMOVED KEY FOR SECURITY___");


    // Specify an Activity to handle all pushes by default.

    PushService.setDefaultPushCallback(this, SplashActivity.class);
    ParseInstallation.getCurrentInstallation().saveInBackground();
}

And my manifest:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.bordengrammar.bordengrammarapp"
    android:versionCode="1"
    android:versionName="1" >

    <!-- OpenGL For Map -->

    <uses-feature
        android:glEsVersion="0x00020000"
        android:required="true" />

    <!-- Permmisions -->

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.WAKE_LOCK" />
    <uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
    <uses-permission android:name="android.permission.VIBRATE" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />
    <uses-permission android:name="com.google.android.c2dm.permission.RECEIVE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="com.google.android.providers.gsf.permission.READ_GSERVICES" />
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
    <uses-permission android:name="android.permission.CALL_PHONE" />

    <!-- Maps -->

    <permission
        android:name="com.bordengrammar.bordengrammarapp.permission.MAPS_RECEIVE"
        android:protectionLevel="signature" />

    <uses-permission android:name="com.bordengrammar.bordengrammarapp.permission.MAPS_RECEIVE" />

    <!-- Push notifcation -->

    <permission
        android:name="com.bordengrammar.bordengrammarapp.permission.C2D_MESSAGE"
        android:protectionLevel="signature" />

    <uses-permission android:name="com.bordengrammar.bordengrammarapp.permission.C2D_MESSAGE" />

    <!-- Android 3.0+ -->

    <uses-sdk
        android:minSdkVersion="11"
        android:targetSdkVersion="17" />

    <!-- Main settings for application -->

    <application
        android:name=".Application"
        android:allowBackup="true"
        android:description="@string/des"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/Theme.Example" >

        <!-- Splash screen -->
        <activity
            android:name=".SplashActivity"
            android:label="@string/app_name"
            android:theme="@style/FullBleedTheme" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <!-- Main Activity -->
        <activity
            android:name=".MainActivity"
            android:label="@string/app_name" >
        </activity>

        <!-- Push Stuff -->

        <receiver
            android:name=".BGSWidgetProvider"
            android:icon="@drawable/ic_launcher"
            android:label="BGS Widget" >
            <intent-filter>
                <action android:name="android.appwidget.action.APPWIDGET_UPDATE" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.ALTERNATIVE" />
            </intent-filter>
            <intent-filter>
                <action android:name="com.bordengrammar.bordengrammarapp.BGSWidgetService.MOODY" />
            </intent-filter>

            <meta-data
                android:name="android.appwidget.provider"
                android:resource="@xml/widget_info" />
        </receiver>


        <!-- Other activitys -->

        <activity
            android:name=".AboutActivity"
            android:label="@string/title_activity_about" >
        </activity>
        <activity
            android:name=".SettingsActivity"
            android:label="@string/title_activity_settings" >
        </activity>
        <activity android:name=".LinkActivity"
                  android:label="View Link" >
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="MainActivity" />
        </activity>

        <!-- Meta-data (keys etc) -->

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />
        <meta-data
            android:name="com.google.android.maps.v2.API_KEY"
            android:value="AIzaSyB7Vi7qBh0Ui0jC5A_cJkeCG2-jB6mQJ0w" />

        <activity
            android:name=".TwitterActivity"
            android:label="@string/title_activity_twitter"
            android:parentActivityName=".MainActivity" >
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="MainActivity" />
        </activity>
        <activity
            android:name=".PrivacyActivity"
            android:label="@string/title_activity_privacy"
            android:parentActivityName=".MainActivity" >
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="MainActivity" />
        </activity>

        <meta-data
            android:name="com.crashlytics.ApiKey"
            android:value="3100c5a6401376fafb134b3000e78233a9a7f693" />

        <!-- For widget -->
        <activity
            android:name=".Licenses"
            android:label="@string/title_activity_licenses"
            android:parentActivityName=".MainActivity" >
            <meta-data
                android:name="android.support.PARENT_ACTIVITY"
                android:value="MainActivity" />
        </activity>








        <!-- Push Service -->
        <service android:name="com.parse.PushService" />

        <receiver android:name="com.parse.ParseBroadcastReceiver" >
            <intent-filter>
                <action android:name="android.intent.action.BOOT_COMPLETED" />
                <action android:name="android.intent.action.USER_PRESENT" />
            </intent-filter>
        </receiver>
        <receiver
            android:name="com.parse.GcmBroadcastReceiver"
            android:permission="com.google.android.c2dm.permission.SEND" >
            <intent-filter>
                <action android:name="com.google.android.c2dm.intent.RECEIVE" />
                <action android:name="com.google.android.c2dm.intent.REGISTRATION" />

                <category android:name="com.bordengrammar.bordengrammarapp" />
            </intent-filter>
        </receiver>
    </application>

</manifest>
Finley Smith
  • 1,599
  • 2
  • 11
  • 15
  • check your installation class on your data browser and see if there are same multiple device token – dgzz Jun 24 '14 at 18:07
  • And how would one do that @johngomez – Finley Smith Jun 24 '14 at 22:27
  • 2
    @epicfinley Login to parse.com , go to your dashboard, and check the Data Browser, there you would see the table and data for Parse Installation class. It seems you have duplicate rows for same device token in your parse installation table. You may run a cron job to remove the duplicates and only keep the last updated one – Thiyaga B Jul 25 '14 at 05:52
  • Thanks @thiyagab post it as a answer and i will accept it. – Finley Smith Jul 25 '14 at 20:31
  • the same happens for me when I install, uninstall and install again. However when I go to the dashboard I can see two objectId, two deviceToken, two installationId and all different. No duplicate – Vito Valov Jul 15 '15 at 15:43
  • @thiyagab I am having this same problem. How can we ensure that duplicate device ids are not added to the database automatically? It seems weird that I'd have to clean out duplicates in this table every time I want to send a push... – phreakhead Oct 20 '15 at 21:58
  • 1
    @phreakhead you can have cloud code beforeSave where you can check if data exists and then allow to add or skip – Thiyaga B Oct 23 '15 at 13:16
  • Check this out: http://stackoverflow.com/questions/28050986/how-to-avoid-installation-duplicates-using-parse/33306713#33306713 – iTurki Oct 23 '15 at 17:09

3 Answers3

4

I had the same problem. The problem was, the 'params' parameter was null when I call the cloud function. When I put values to that, the problem was solved.

JINS M.THOMAS
  • 121
  • 10
1

Login to parse.com , go to your dashboard, and check the Data Browser, there you would see the table and data for Parse Installation class. It seems you have duplicate rows for same device token in your parse installation table. You may run a cron job to remove the duplicates and only keep the last updated one

Thiyaga B
  • 971
  • 1
  • 9
  • 26
  • Cron Job!! Parse doesn't have cron jobs. – iTurki Oct 23 '15 at 17:13
  • 1
    @iturki I was referring to the background jobs, please refer here http://blog.parse.com/announcements/introducing-background-jobs/ – Thiyaga B Oct 26 '15 at 05:51
  • 1
    The problem is not how you run queries in your data. It is how you _identify duplicates in your data_. It is possible to have multiple rows in your data, with different deviceTokens, that point to the same physical device. – iTurki Oct 26 '15 at 10:04
  • @iturki yes and we have a way to handle this using canonical IDs http://stackoverflow.com/questions/23293444/android-gcm-and-multiple-tokens – Thiyaga B Oct 27 '15 at 11:45
  • And how would you use canonical IDs with Parse? – iTurki Oct 28 '15 at 18:44
1

Reason:

This happens because every time you install the application on a (same) device, parse will create a new record in the 'Installation' class, thus deviceToken gets duplicated.

Solution:

You can solve this by finding for the duplicate entry and avoid the insertion in 'beforeSave' function for 'Installation' Class. It's better to write this section in javascript and move to the CloudCode. Hence the communication between client (android) device and the server can be reduced.

Parse.Cloud.beforeSave(Parse.Installation, function(request, response) {
    if(!request.object.isNew()) {                       // Old Obj - Updating
        response.success();
    } else {                                            // New Obj - Inserting
        Parse.Cloud.useMasterKey();
        var query = new Parse.Query(Parse.Installation);
        query.equalTo("deviceToken", request.object.get("deviceToken"));
        query.first().then(function(duplicate) {
            if (typeof duplicate === "undefined") {
                console.log("New installation ..");
                response.success();
            } else {
                console.log("Duplicate exist ..");
                response.error();          
            }
        }, function(error) {
            console.warn(error.code + error.message);
            response.error();
        });
    }
});

You can use either 'deviceToken' or your custom field like 'userID' for checking duplicates or both as per your needs.

Update: In the above code, I've just avoided the duplicate entry. But it is good practice to remove the existing and insert the new one (as below).

Parse.Cloud.beforeSave(Parse.Installation, function(request, response) {
    if(!request.object.isNew()) {                       // Old Obj - Updating
        response.success();
    } else {                                            // New Obj - Inserting
        Parse.Cloud.useMasterKey();
        var query = new Parse.Query(Parse.Installation);
        query.equalTo("deviceToken", request.object.get("deviceToken"));
        query.first().then(function(duplicate) {
            if (typeof duplicate === "undefined") {
                console.log("New installation ..");
                response.success();  // Allows Insertion
            } else {
                console.log("Duplicate exist ..");
                duplicate.destroy(); // Removes old         
                response.success();  // Allows new insertion
            }
        }, function(error) {
            console.warn(error.code + error.message);
            response.error();
        });
    }
});
Ram Babu
  • 2,692
  • 3
  • 23
  • 28
  • I think this code will delete the updated Installation, not the old one. – iTurki Oct 23 '15 at 17:11
  • 1
    Yes @iturki. As I already mentioned in my answer, I just avoided the duplicate insertion (new one). But the good practice is to remove the existing and insert the new one. – Ram Babu Oct 26 '15 at 01:47