Регистрация устройства Android Parse Push только один раз на одном устройстве

Каждый я использую службу разбора для push-уведомления в своем приложении. но он регистрируется все время, когда я переустанавливаю приложение на одном устройстве. Проблема заключается в том, что одно устройство получает несколько уведомлений по каждому из них. Я сделал код для регистрации, который показан ниже. пожалуйста, помогите мне, спасибо заранее.

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();

И для подписки:

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

В манифесте

Выше

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

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

В теге приложения:

    <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>

И каждый раз, когда я устанавливаю, он показывает ошибку, которая показана ниже.

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$3.then(ParseCommand.java:348)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$10.run(Task.java:452)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$1.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$8.then(Task.java:385)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$8.then(Task.java:1)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$11.run(Task.java:485)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$1.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$9(Task.java:477)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$7.then(Task.java:350)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$7.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$5(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$11$1.then(Task.java:497)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$11$1.then(Task.java:1)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$10.run(Task.java:452)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$1.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$8(Task.java:444)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$6.then(Task.java:315)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$6.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$5(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$11$1.then(Task.java:497)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$11$1.then(Task.java:1)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$10.run(Task.java:452)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$1.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$11.run(Task.java:489)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$1.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$9(Task.java:477)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$7.then(Task.java:350)
03-10 12:18:48.555: E/ParseCommandCache(12709):     at com.parse.Task$7.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$5(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$3.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.access$201(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)

Я хочу зарегистрировать только одно время для устройства. не должно быть проблем, если приложение установило несколько раз в device.please, помогите мне, спасибо заранее.

В моем случае вкладка samsung 3 регистрируется дважды с тем же UniqueId. Уникален ли UniqueId как уникальный столбец в parse db? Или мы должны сделать его уникальным? если да, то как?

Ответы

Ответ 1

Я получил его после обновления таблицы с отправкой уникального идентификатора устройства Android.

 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();

Он обновит raw, но не перерегистрирует устройство.

Ответ 2

Я думаю, что Mukul предоставил большой код облака для этой проблемы

здесь

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();
});
});

Обратите внимание, что владелец - это имя пользователя или первичный ключ, который, по вашему мнению, может использовать.

здесь ссылка того же с лучшим объяснением mukul https://www.parse.com/questions/check-for-duplicate-installations-of-same-user-on-re-installation-of-app

Ответ 3

PushService.subscribe похоже, кэширует подписку в локальном хранилище, чтобы избежать повторной подписки при запуске приложения несколько раз.

Это первый параметр этого метода:

context - используется для доступа к локальному хранилищу для кэширования подписка, поэтому она должна в настоящее время           быть жизнеспособным контекстом.

(цитата из здесь).

Однако при удалении приложения локальное хранилище для этого приложения стирается с вашего устройства, поэтому новая установка приведет к тому, что PushService.subscribe перерегистрируется в Google Cloud Messaging. Если новая регистрация возвращает новый идентификатор регистрации, Parse будет иметь два регистрационных идентификатора, которые могут быть использованы для отправки push-уведомлений в ваше приложение, и оба они будут связаны с тем же именем пользователя, которое вы указали в subscribe. Поэтому отправка уведомления этому имени пользователя отправит его на оба регистрационных идентификатора, в результате чего он будет поступать дважды.

Когда Parse отправит вам уведомления, они должны получить от Google ответ с canonical_registration_id, который позволит им узнать, что один из идентификаторов регистрации, связанных с вашим приложением на вашем устройстве, является старым и больше не должен использоваться. Поэтому (при условии, что Parse имеет достойную реализацию GCM) при следующем отправке уведомления на ваше устройство вы должны получить его только один раз.

Ответ 4

Я также столкнулся с этой проблемой. Я как-то решил его, вызвав метод ниже в моей Activity 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);
                }
            });

    }

Несмотря на то, что это не полностью решило мою проблему, но теперь мое приложение не зависает и больше не ANR из-за этой реализации Parse. Если я снова установлю приложение и запустил его, приложение создаст новую установочную запись и удалит последнюю. Единственная проблема заключается в том, что имя_канала не подписано в этом прогоне, но при следующем запуске канал успешно подписан.

Ответ 5

Что мне удалось избавиться от этого исключения, было , используя saveEventually() вместо saveInBackground().

Здесь у вас есть ссылка на мой ответ на аналогичный вопрос.

Я думаю, что saveEventually() является лучшим вариантом, поскольку он гарантирует, что установка будет всегда сохранена, независимо от доступности сети. Напротив, при saveInBackground() существует вероятность того, что сохранение завершится неудачно из-за отсутствия подключения к сети. Также с помощью saveEventually() вам не нужно выполнять проверку ошибок, что вы должны сделать в SaveCallback() с saveInBackground().

Что касается повторяющихся уведомлений, это не должно происходить, если вы используете последнюю версию Parse SDK (это не происходит со мной с 1.7.1). Была ошибка, которая была решена сейчас. См. этот вопрос SO и эту ошибку FB.

Обратите внимание, что в первый раз, когда пользователь получает уведомление после переустановки приложения, это уведомление может быть доставлено дважды. Это случилось со мной, но происходит только для самого первого уведомления. (Для получения дополнительной информации см. Ссылку FB.) После этого повторного уведомления старая установка будет автоматически удалена с помощью Parse. Это мой опыт.

Если вы пытаетесь избежать отправки повторяющихся уведомлений, реализовав некоторую логику в CloudCode (используя предыскание, которое запускается при сохранении новой установки, проверьте, было ли приложение уже установлено на устройство и удалило старую установку), don Не делай этого! Нет необходимости. Parse сделает это за вас: он удалит старую установку:)

Ответ 6

Я нашел работу, в которой будет снова зарегистрирована удаленная установка в 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();
    }

И при инициализации 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();
                }
            }
        });

Ответ 7

Создайте новый класс и расширьте его с помощью приложения. напишите код таким образом

  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);
}

}

Добавить запись в manifest файл

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