Android Parse Push 通知设备仅在一台设备上注册一次

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/22294181/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 05:47:36  来源:igfitidea点击:

Android Parse Push notification device registration only one time on one device

androidpush-notificationparse-platformgoogle-cloud-messagingdevicetoken

提问by Mayur Raval

Every one I am using the parse service for push notification in my app. but it register all time when i re-install the app in one device.Then problem is that,one device receive multiple notifications on each. I have done some code for registration which is shown below. please help me,thanks in advance.

我在我的应用程序中使用解析服务推送通知的每一个。但是当我在一台设备上重新安装该应用程序时,它会一直注册。然后问题是,一台设备在每台设备上都会收到多个通知。我已经完成了一些注册代码,如下所示。请帮助我,提前致谢。

Parse.initialize(this, PARSE_APP_ID, PARSE_CLIENT_KEY);
ParseACL defaultACL = new ParseACL();
defaultACL.setPublicReadAccess(true);
ParseACL.setDefaultACL(defaultACL, true);
PushService.setDefaultPushCallback(this, MainActivity.class);
ParseInstallation.getCurrentInstallation().getInstallationId();
ParseInstallation.getCurrentInstallation().saveInBackground();

And for subscribe:

订阅:

PushService.subscribe(this, userName, Detail.class);

In Manifest

在清单中

Above

以上

  <permission
    android:name="com.example.app.permission.C2D_MESSAGE"
    android:protectionLevel="signature" />

  <uses-permission android:name="com.example.app.permission.C2D_MESSAGE" />

In application tag:

在应用程序标签中:

    <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="act" />
        </intent-filter>
    </receiver>

    <receiver android:name="com.app.example.PushReceiver" >

        <intent-filter>
            <action android:name="act" />
            </action>
        </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" />
            <action android:name="act" />
            <category android:name="com.example.app" />
        </intent-filter>
    </receiver>

And each time when I am Install, it show error which is shown below.

每次安装时,它都会显示如下所示的错误。

03-10 12:18:48.555: E/ParseCommandCache(12709): Failed to run command.
03-10 12:18:48.555: E/ParseCommandCache(12709): com.parse.ParseException: at least one ID field (installationId,deviceToken) must be specified in this operation
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.ParseCommand.then(ParseCommand.java:348)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.run(Task.java:452)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.execute(Task.java:68)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.completeImmediately(Task.java:448)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.continueWith(Task.java:322)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.continueWith(Task.java:333)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:385)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:1)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.run(Task.java:485)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.execute(Task.java:68)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.completeAfterTask(Task.java:481)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.access(Task.java:477)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:350)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:1)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.runContinuations(Task.java:514)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.access(Task.java:510)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at   com.parse.Task$TaskCompletionSource.trySetResult(Task.java:569)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$TaskCompletionSource.setResult(Task.java:603)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:497)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:1)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.run(Task.java:452)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.execute(Task.java:68)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.completeImmediately(Task.java:448)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.access(Task.java:444)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:315)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:1)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.runContinuations(Task.java:514)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.access(Task.java:510)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$TaskCompletionSource.trySetResult(Task.java:569)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$TaskCompletionSource.setResult(Task.java:603)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:497)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:1)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.run(Task.java:452)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.execute(Task.java:68)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.completeImmediately(Task.java:448)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.continueWith(Task.java:322)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.continueWith(Task.java:333)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.run(Task.java:489)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.execute(Task.java:68)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.completeAfterTask(Task.java:481)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.access(Task.java:477)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:350)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.then(Task.java:1)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.runContinuations(Task.java:514)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.access(Task.java:510)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$TaskCompletionSource.trySetResult(Task.java:569)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$TaskCompletionSource.setResult(Task.java:603)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task.run(Task.java:228)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:422)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at java.util.concurrent.FutureTask.run(FutureTask.java:237)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access1(ScheduledThreadPoolExecutor.java:152)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:265)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at java.lang.Thread.run(Thread.java:841)

I want to register only one time for on device. there should be not an issue if app installed multiple time in device.please help me, thanks in advance.

我只想在设备上注册一次。如果应用程序在设备中多次安装,应该没有问题。请帮助我,在此先感谢。

In my case samsung tab 3 registers twice with same UniqueId. Is UniqueId is marked as unique column in parse db? Or we should make it unique? if yes, how?

在我的情况下,samsung tab 3 使用相同的 UniqueId 注册了两次。在解析数据库中,UniqueId 是否被标记为唯一列?或者我们应该让它独一无二?如果是,如何?

采纳答案by Mayur Raval

I got it after update the table with the send of unique id of the android device.

我在发送了 android 设备的唯一 id 更新表后得到了它。

 String  android_id = Secure.getString(getApplicationContext().getContentResolver(),Secure.ANDROID_ID);         
    Log.e("LOG","android id >>" + android_id);

    PushService.setDefaultPushCallback(this, MainActivity.class);

    ParseInstallation installation = ParseInstallation.getCurrentInstallation();
    installation.put("installationId",android_id);

    installation.saveInBackground();

It will update the raw ,but it doesn't re-register the device .

它将更新原始数据,但不会重新注册设备。

回答by user1169079

I think Mukul has provided great cloud code for this issue

我认为 Mukul 为这个问题提供了很棒的云代码

here it is

这里是

Parse.Cloud.beforeSave(Parse.Installation, function(request, response) {
Parse.Cloud.useMasterKey();
var query = new Parse.Query(Parse.Installation);
query.equalTo("owner", request.user);
query.equalTo("uniqueID", request.object.get("uniqueID"));
query.first().then(function(duplicate) {
    if (typeof duplicate === "undefined") {
        console.log("Duplicate does not exist,New installation");
        response.success();
    } else {
        console.log("Duplicate exist..Trying to delete " + duplicate.id);
        duplicate.destroy().then(function(duplicate) {
            console.log("Successfully deleted duplicate");
            response.success();
        }, function() {
            console.log(error.code + " " + error.message);
            response.success();
        });

    }
}, function(error) {
    console.warn(error.code + error.message);
    response.success();
});
});

Note that owner is the username or the primary key that you think u can use.

请注意,所有者是您认为可以使用的用户名或主键。

here's the link of the same with better explanation by mukul https://www.parse.com/questions/check-for-duplicate-installations-of-same-user-on-re-installation-of-app

这是相同的链接,mukul 有更好的解释 https://www.parse.com/questions/check-for-duplicate-installations-of-same-user-on-re-installation-of-app

回答by Eran

PushService.subscribeseems to cache the subscription in local storage, to avoid re-subscribing when you launch the app multiple times.

PushService.subscribe似乎将订阅缓存在本地存储中,以避免在多次启动应用程序时重新订阅。

This is what the first parameter of that method is used for :

这是该方法的第一个参数的用途:

context - This is used to access local storage to cache the subscription, so it must currently be a viable context.

context - 这用于访问本地存储以缓存订阅,因此它当前必须是可行的上下文。

(quote from here).

(引自此处)。

However, when you uninstall the app, local storage for that app is wiped from your device, so the new installation will cause PushService.subscribeto re-register to Google Cloud Messaging. If the new registration returns a new registration ID, Parse would have two registration IDs that can be used to send push notifications to your app, and both of them would be linked to the same userName you supplied to subscribe. Therefore sending a notification to that userName will send it to both registration IDs, causing it to arrive twice.

但是,当您卸载该应用程序时,该应用程序的本地存储空间将从您的设备中擦除,因此新安装将导致PushService.subscribe重新注册到 Google Cloud Messaging。如果新注册返回一个新的注册 ID,Parse 将有两个注册 ID,可用于向您的应用发送推送通知,并且它们都将链接到您提供给的同一个 userName subscribe。因此,向该 userName 发送通知会将其发送到两个注册 ID,从而使其到达两次。

When Parse send the notifications for you, they should get from Google a response with canonical_registration_id, which will let them know one of the registration IDs associated with your app on your device is old, and should not be used anymore. Therefore (assuming Parse have a decent implementation of GCM) the next time you send a notification to your device, you should receive it only once.

当 Parse 为您发送通知时,他们应该从 Google 获得带有 的响应canonical_registration_id,这会让他们知道与您设备上的应用相关联的注册 ID 之一已经过时,不应再使用。因此(假设 Parse 有一个不错的 GCM 实现)下次您向设备发送通知时,您应该只收到一次。

回答by Sheraz Ahmad Khilji

I was also facing this issue. I sort of solved it by calling the below method in my Activity's onCreate()

我也面临这个问题。我通过在我的活动中调用以下方法来解决它onCreate()

/**
     * Initialize Push Messaging Service and subscribe to all-users channel
     */
    private void initParsePushMessaging() {
        ParseInstallation parseInstallation = ParseInstallation
                .getCurrentInstallation();
        //You might skip this if
        if (ParseUser.getCurrentUser() != null) {
            parseInstallation.put("user",
                    ParseUser.getCurrentUser());
        }
        if (parseInstallation.getObjectId() != null)
            parseInstallation.saveInBackground(new SaveCallback() {

                @Override
                public void done(ParseException e) {
                    PushService.subscribe(getApplicationContext(),"channel_name",
                            MainHomeActivity.class);
                }
            });

    }

Even though it didn't completely solve my problem but now my app doesn't hang and no more ANR's due to this Parse implementation. If i re install an app and run it now then the app creates a new installation record and remove's the last one. The only problem is that the channel_nameis not subscribed on this runbut on the next run the channel are successfully subscribed.

即使它没有完全解决我的问题,但现在我的应用程序没有挂起,并且由于这个 Parse 实现,没有更多的 ANR。如果我现在重新安装一个应用程序并运行它,那么该应用程序会创建一个新的安装记录并删除最后一个。唯一的问题是,CHANNEL_NAME没有订阅这个运行,但在接下来的运行通道被成功认购

回答by Albert Vila Calvo

What worked for me to get rid of this exception was using saveEventually()instead of saveInBackground().

对我来说摆脱这个异常的方法是使用saveEventually()而不是saveInBackground().

Here you have a linkto my answer to a similar question.

在这里,您可以找到我对类似问题的回答的链接

I think that saveEventually()is a better option because it assures that the installation will always be saved, regardless of the netwwork availability. In contrast, with saveInBackground()there is a chance that the save fails due to no network connectivity. Also with saveEventually()you don't need to do any error checking, which you should do in a SaveCallback()with saveInBackground().

我认为这saveEventually()是一个更好的选择,因为它确保无论网络可用性如何,都将始终保存安装。相反,saveInBackground()由于没有网络连接,保存有可能失败。同样 withsaveEventually()你不需要做任何错误检查,你应该在SaveCallback()with 中做saveInBackground()

Regarding the duplicate notifications, this shouldn't occur if you are using the latest Parse SDK (it doesn't happen to me with 1.7.1). There was a bug that has been solved now. See this SO questionand this FB bug.

关于重复通知,如果您使用的是最新的 Parse SDK,则不会发生这种情况(我在 1.7.1 中不会发生这种情况)。现在已经解决了一个错误。看到这个 SO question这个 FB bug

Note that first time the user receives a notification after reinstalling the app, this notification can be delivered twice. It has happened to me, but only happens for the very first notification. (See the FB link for more details.) After that duplicate notification, the old installation will be removed automatically by Parse. This is my experience.

请注意,用户在重新安装应用程序后第一次收到通知时,此通知可以发送两次。它发生在我身上,但只发生在第一个通知中。(有关更多详细信息,请参阅 FB 链接。)在重复通知之后,Parse 将自动删除旧安装。这是我的经验。

If you are trying to avoid sending duplicate notifications by implementing some logic at the CloudCode (using a beforeSave that triggers when saving a new Installation, check if the app had already been installed into the device and deleting the old installation), don't do that!There is not need. Parse will do it for you: it will delete the old installation :)

如果您试图通过在 CloudCode 上实现一些逻辑来避免发送重复通知(使用在保存新安装时触发的 beforeSave,检查应用程序是否已经安装到设备中并删除旧安装),请不要这样做那!没有必要。Parse 会为你做:它会删除旧的安装:)

回答by Kleand Sherali

I found work around which will re-register the deleted installations in Parse again.

我找到了解决方法,它将再次在 Parse 中重新注册已删除的安装。

public void clearApplicationData() {
        File cache = getCacheDir();
        File appDir = new File(cache.getParent());
        if (appDir.exists()) {
            String[] children = appDir.list();
            for (String s : children) {
                if (s.equals("app_Parse")) {
                    deleteDir(new File(appDir, s));
                    System.out.println( "**************** File /data/data/APP_PACKAGE/" + s + " DELETED *******************");
                }
            }
        }
    }

    public static boolean deleteDir(File dir) {
        if (dir != null && dir.isDirectory()) {
            String[] children = dir.list();
            for (int i = 0; i < children.length; i++) {
                boolean success = deleteDir(new File(dir, children[i]));
                if (!success) {
                    return false;
                }
            }
        }

        return dir.delete();
    }

And when initializing Parse just

并且在初始化 Parse 时

Parse.initialize(this, Constants.PARSE_APP_ID, Constants.PARSE_CLIENT_ID);

        ParseInstallation installation = ParseInstallation.getCurrentInstallation();

//Trying  to update the current installation with a custom key and this will trigger the ParseException if this installation is not found in Installations Table in Parse Server
        String value= "Value";

        if(installation.get("customKey") != null){
            value= installation.get("customKey").toString();
        }
        installation.put("customKey", value);
//Now lets see what call back brings in
        installation.saveInBackground(new SaveCallback() {
            @Override
            public void done(ParseException e) {
                System.out.println("Done");
                if (e == null) {
                    System.out.println("Succesfull Registration.....");
                } else {

                    System.out.println("Cleare cache");

//By clearing the cache the next time user will close and re open the app it will be installed in installations again
                    clearApplicationData();
                }
            }
        });

回答by Shauket Sheikh

Create new class and extend it with Application. write code in this way

创建新类并使用 Application 扩展它。这样写代码

  public class BBApplication extends Application {


   @Override
   public void onCreate() {
      super.onCreate();

       Parse.initialize(this, "app key", "client key");
          ParseInstallation.getCurrentInstallation().saveInBackground();
           ParsePush.subscribeInBackground("", new SaveCallback() {

               @Override
               public void done(com.parse.ParseException arg0) {
                // TODO Auto-generated method stub
                     if (arg0 == null) {
                         Log.d("com.parse.push", "successfully    subscribed to the broadcast channel.");
                    }    else {
                          Log.e("com.parse.push", "failed to subscribe for push", arg0);
                    }
            }
            });

       ParseUser.enableAutomaticUser();
       ParseACL defaultACL = new ParseACL();
      // Optionally enable public read access.
      // defaultACL.setPublicReadAccess(true);
      ParseACL.setDefaultACL(defaultACL, true);
}

}

}

Add entry in manifestfile

manifest文件中添加条目

<application android:label="@string/app_name"
    android:name="org.cocos2dx.cpp.BBApplication"
             android:icon="@drawable/icon">