2015-12-28 13 views
8

Ich verwende Parse.com als Backend für meine App. Sie bieten auch eine lokale Datenbank zum Speichern von Informationen als Alternative zu SQLite.Android: Wie synchronisiert man Abfragen mit Schrauben von Parse.com?

Ich möchte Nummern aus dem Telefon zu meiner Datenbank mit Parse hinzufügen. Bevor ich eine Nummer hinzufüge, muss ich prüfen, ob die Nummer bereits in der Datenbank existiert, also verwende ich findInBackground(), um eine Liste von Nummern zu erhalten, die mit der Nummer übereinstimmen, die ich hinzufügen möchte. Wenn die Liste leer ist, existiert die Nummer, die ich hinzufügen möchte, nicht in der Datenbank.

Die Methode, dies zu tun ist:

public void putPerson(final String name, final String phoneNumber, final boolean isFav) { 

     // Verify if there is any person with the same phone number 
     ParseQuery<ParseObject> query = ParseQuery.getQuery(ParseClass.PERSON_CLASS); 
     query.whereEqualTo(ParseKey.PERSON_PHONE_NUMBER_KEY, phoneNumber); 
     query.fromLocalDatastore(); 
     query.findInBackground(new FindCallback<ParseObject>() { 
            public void done(List<ParseObject> personList, 
                ParseException e) { 
             if (e == null) { 
              if (personList.isEmpty()) { 
               // If there is not any person with the same phone number add person 
               ParseObject person = new ParseObject(ParseClass.PERSON_CLASS); 
               person.put(ParseKey.PERSON_NAME_KEY, name); 
               person.put(ParseKey.PERSON_PHONE_NUMBER_KEY, phoneNumber); 
               person.put(ParseKey.PERSON_FAVORITE_KEY, isFav); 
               person.pinInBackground(); 

               Log.d(TAG,"Person:"+phoneNumber+" was added."); 
              } else { 
               Log.d(TAG, "Warning: " + "Person with the number " + phoneNumber + " already exists."); 
              } 
             } else { 
              Log.d(TAG, "Error: " + e.getMessage()); 
             } 
            } 
           } 
     ); 
    } 

Dann wurden 3 mal diese Methode nenne ich drei Zahlen addieren:

ParseLocalDataStore.getInstance().putPerson("Jack", "0741234567", false); 
ParseLocalDataStore.getInstance().putPerson("John", "0747654321", false); 
ParseLocalDataStore.getInstance().putPerson("Jack", "0741234567", false); 
ParseLocalDataStore.getInstance().getPerson(); // Get all persons from database 

Beachten Sie, dass die dritte Zahl die gleiche wie die erste, und Es sollte nicht zur Datenbank hinzugefügt werden. Aber die logcat zeigt:

12-26 15:37:55.424 16408-16408/D/MGParseLocalDataStore: Person:0741234567 was added. 
12-26 15:37:55.424 16408-16408/D/MGParseLocalDataStore: Person:0747654321 was added. 
12-26 15:37:55.484 16408-16408/D/MGParseLocalDataStore: Person:0741234567 was added. 

Die dritte Zahl hinzugefügt wurde, auch wenn es nicht tun sollte, weil fintInBackground() fast gleichzeitig in 3 Hintergrund-Threads ausgeführt wird, so wird er feststellen, dass es keine Nummer in der Datenbank wie die, die ich hinzufügen möchte.

In this Frage ein Kerl sagte mir, dass ich Bolts Bibliothek von Parse verwenden sollte. Ich lese darüber aus here und einige Parse Blog-Posts, aber ich verstehe nicht vollständig, wie man dies mit der Methode, die ich bereits habe, und wie man die nacheinander auszuführenden Abfragen syncronize.

Wenn jemand mit dieser Bibliothek gearbeitet bitte leite mich auf, wie dies zu tun oder einige grundlegende Beispiele geben, damit ich den Workflow verstehen.

Vielen Dank!

+0

wenn Sie -Rückrufmethode in Ihre pinInBackground() fügen Sie die Zeile vervielfältigt Problem zu lösen. –

+0

@SedatPolat Wie? Das Problem ist nicht mit PinInBackground(). Das Problem ist, dass alle 3 Abfragen fast gleichzeitig bearbeitet werden, wenn findInBackgroud() aufgerufen wird. Ich möchte diese Abfragen nacheinander bearbeiten. –

+0

Wenn Sie CallBack zu pinInBackground() wie in meiner Antwort hinzufügen, warten Ihre Sicherungsvorgänge gegenseitig. –

Antwort

0

Sie sollten doppelte Datensätze entfernen, bevor Sie sie in der Datenbank speichern. Wenn Sie HashSet verwenden und ein Personenobjekt erstellen, dessen Methodenund hashCode() überschrieben werden, wird das Problem mit doppelten Datensätzen gelöst. Die Telefonnummer ist ein eindeutiger Wert. Wenn die Telefonnummern mit denen anderer übereinstimmen, sollten Sie nur eine davon speichern. Dies bedeutet, dass Sie equals() und hashCode() Methoden von Person Objekt überschreiben sollten, indem Sie nur phone Feld verwenden.

public class Person { 
    private String name; 
    private String phone; 
    private boolean isFav; 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getPhone() { 
     return phone; 
    } 

    public void setPhone(String phone) { 
     this.phone = phone; 
    } 

    public boolean isFav() { 
     return isFav; 
    } 

    public void setFav(boolean isFav) { 
     this.isFav = isFav; 
    } 

    @Override 
    public int hashCode() { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + ((phone == null) ? 0 : phone.hashCode()); 
     return result; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) 
      return true; 
     if (obj == null) 
      return false; 
     if (getClass() != obj.getClass()) 
      return false; 
     Person other = (Person) obj; 
     if (phone == null) { 
      if (other.phone != null) 
       return false; 
     } else if (!phone.equals(other.phone)) 
      return false; 
     return true; 
    } 

    @Override 
    public String toString() { 
     return "Person [name=" + name + ", phone=" + phone + ", isFav=" + isFav + "]"; 
    } 
} 

Testing:

public static void main(String[] args) { 

    HashSet<Person> persons = new HashSet<Person>(); 

    Person person; 

    person = new Person(); 
    person.setName("Joe"); 
    person.setPhone("+199999"); 
    person.setFav(false); 
    persons.add(person); 

    person = new Person(); 
    person.setName("Jessie"); 
    person.setPhone("+133333"); 
    person.setFav(false); 
    persons.add(person); 

    person = new Person(); 
    person.setName("Johnny"); 
    person.setPhone("+199999"); 
    person.setFav(false); 
    persons.add(person); 

    System.out.println(persons); 
} 

Drucke:

[Person [name = Joe, Telefon = + 199999, isFav = false], Person [name = Jessie, Telefon = + 133333, isFav = false]]

HashSet hat Person Objekte, die einzigartige Telefonnummern haben. Johnny hat dieselbe Telefonnummer wie Joe, also lehnt HashSet ab, Johnny in die Liste aufzunehmen.

Hinzufügen Auch CallBack Methode auf putPerson() werden Sie über fehlgeschlagene Pinningvorgang helfen.

person.pinInBackground(new SaveCallback() { 

    @Override 
    public void done(ParseException e) { 
     if(e == null) { 
      pinnedPersonList.add(person) 
     } else { 
      unPinnedPersonList.add(person) 
     } 
    } 
}); 
+0

Ich habe es getestet, aber das Ergebnis ist das gleiche. Die dritte wird noch hinzugefügt. Es wurde erwartet, dass dies so ist, weil '' findInBackground() '' die Methode ist, die sucht, wenn die Nummer existiert. Es hat nichts mit '' pinInBackGround() '' zu tun. Ich muss '' findInBackground() '' Methoden machen, um aufeinander zu warten. –

+0

anstelle von "findInBackground()", können Sie "query.find()" verwenden –

+0

Ja query.find() funktioniert, aber es dauert zwischen 1,5 und 3 Sekunden, um 100 Zahlen hinzuzufügen (für den Fall, dass meine Datenbank beim ersten Lauf leer ist), Sonst könnte es länger dauern). Die ideale Lösung wäre die Verwendung von findInBackground(), die nicht den gesamten Job im UI-Thread erledigt, und das Synchronisieren der Abfragen in diesem separaten Thread. Ich würde auch gerne lernen, wie man die Bolts-Bibliothek benutzt, falls ich sie für ein anderes Problem brauche. –

0

Dies ist aus Bolzen Dokumentation.

Aufgaben in Serie

Aufgaben sind praktisch, wenn Sie eine Reihe von Aufgaben in einer Reihe tun mögen, die jeweils für die vorherigen warten zu beenden. Angenommen, Sie möchten alle Kommentare in Ihrem Blog löschen. Diese

ParseQuery<ParseObject> query = ParseQuery.getQuery("Comments"); 
query.whereEqualTo("post", 123); 

findAsync(query).continueWithTask(new Continuation<List<ParseObject>, Task<Void>>() { 
    public Task<Void> then(Task<List<ParseObject>> results) throws Exception { 
    // Create a trivial completed task as a base case. 
    Task<Void> task = Task.forResult(null); 
    for (final ParseObject result : results) { 
     // For each item, extend the task with a function to delete the item. 
     task = task.continueWithTask(new Continuation<Void, Task<Void>>() { 
     public Task<Void> then(Task<Void> ignored) throws Exception { 
      // Return a task that will be marked as completed when the delete is finished. 
      return deleteAsync(result); 
     } 
     }); 
    } 
    return task; 
    } 
}).continueWith(new Continuation<Void, Void>() { 
    public Void then(Task<Void> ignored) throws Exception { 
    // Every comment was deleted. 
    return null; 
    } 
}); 

ist, wie ihre getan. Wenn Sie erfahren möchten, was genau vor sich geht, sollten Sie die Dokumentation lesen, obwohl es ziemlich einfach aussehen könnte :)

https://github.com/BoltsFramework/Bolts-Android

1

Es klingt wie Sie eine Race-Bedingung haben. Es gibt viele verschiedene Möglichkeiten, wie Sie dieses Problem lösen können. Hier ist eine Alternative ohne Bolzen.

Das Hauptproblem besteht darin, dass die Suchanfragen ungefähr zur gleichen Zeit stattfinden, so dass sie keine Duplikate in der Datenbank finden. In diesem Fall ist es die Art und Weise, wie wir es lösen, nur eine Person zu suchen und hinzuzufügen, offensichtlich nicht auf dem Hauptthread, da wir die Suche nach ui nicht anbinden wollen. Also machen wir eine Liste, die zwei Dinge macht 1) enthält Elemente, die hinzugefügt werden müssen, 2) überprüft, dass sie hinzugefügt werden können. Wir können eine asynchrone Aufgabe verwenden, um unsere Suche vom Hauptfaden fernzuhalten.

Hier ist eine grobe Vorstellung davon, dass getan werden muss:

import com.parse.ParseException; 
import com.parse.ParseObject; 
import com.parse.ParseQuery; 

import java.util.ArrayList; 
import java.util.List; 

public class AddPersonAsyncQueue { 

    ArrayList<ParseObject> mPeople = new ArrayList(); 
    Boolean mLock; 
    AddTask mRunningTask; 

    public synchronized void addPerson(final String name, final String phoneNumber, final boolean isFav) { 

     // we aren't adding a person just yet simply creating the object 
     // and keeping track that we should do the search then add for this person if they aren't found 
     ParseObject person = new ParseObject(ParseClass.PERSON_CLASS); 
     person.put(ParseKey.PERSON_NAME_KEY, name); 
     person.put(ParseKey.PERSON_PHONE_NUMBER_KEY, phoneNumber); 
     person.put(ParseKey.PERSON_FAVORITE_KEY, isFav); 

     synchronized (mLock) { 
      mPeople.add(person); 
     } 
     processQueue(); 
    } 

    public boolean processQueue() { 
     boolean running = false; 

     synchronized (mLock) { 
      if (mRunningTask == null) { 
       if (mPeople.size() > 0) { 

        mRunningTask = new AddTask(null); 
        mRunningTask.execute(); 
        running = true; 
       } else { 
        // queue is empty no need waste starting an async task 
        running = false; 
       } 
      } else { 
       // async task is already running since it isn't null 
       running = false; 
      } 
     } 
     return running; 
    } 

    protected void onProcessQueueCompleted() { 
     mRunningTask = null; 
    } 

    private class AddTask extends AsyncTask<Void, ParseObject, Boolean> { 
     AddPersonAsyncQueue mAddPersonAsyncQueue; 

     public AddTask(AddPersonAsyncQueue queue) { 
      mAddPersonAsyncQueue = queue; 
     } 

     @Override 
     protected Boolean doInBackground(Void... voids) { 
      boolean errors = false; 
      ParseObject nextObject = null; 

      while (!isCancelled()) { 

       synchronized (mLock) { 
        if (mPeople.size() == 0) { 
         break; 
        } else { 
         // always take the oldest item fifo 
         nextObject = mPeople.remove(0); 
        } 
       } 

       if (alreadyHasPhoneNumber(nextObject.getInt(ParseKey.PERSON_PHONE_NUMBER_KEY))) { 
        // do nothing as we don't want to add a duplicate 
        errors = true; 
       } else if (addPerson(nextObject)) { 
        // nice we were able to add successfully 

       } else { 
        // we weren't able to add the person object we had an error 
        errors = true; 
       } 

      } 
      return errors; 
     } 

     private boolean alreadyHasPhoneNumber(int phoneNumber) { 
      try { 
       ParseQuery<ParseObject> query = ParseQuery.getQuery(ParseClass.PERSON_CLASS); 
       query.whereEqualTo(ParseKey.PERSON_PHONE_NUMBER_KEY, phoneNumber); 
       query.fromLocalDatastore(); 
       List<ParseObject> objects = query.find(); 
       return objects.size() > 0; 
      } catch (Exception error) { 
       // may need different logic here to do in the even of an error 
       return true; 
      } 
     } 

     private boolean addPerson(ParseObject person) { 
      try { 
       // now we finally add the person 
       person.pin(); 
       return true; 
      } catch (ParseException e) { 
       e.printStackTrace(); 
       return false; 
      } 
     } 

     @Override 
     protected void onPostExecute(Boolean aBoolean) { 
      super.onPostExecute(aBoolean); 
      onProcessQueueCompleted(); 
     } 
    } 


} 
Verwandte Themen