Android: ¿Cómo sincronizar las consultas con Bolts de Parse.com?

Estoy utilizando Parse.com como backend para mi aplicación. También ofrecen una base de datos local para almacenar información, como alternativa a SQLite .

Quiero agregar números de teléfono a mi base de datos con analizar. Antes de agregar un número necesito comprobar si el número ya existe en la base de datos, así que utilizo findInBackground() para obtener una lista de números que coinciden con el número que quiero agregar. Si la lista está vacía, el número que quiero agregar no existe en la base de datos.

El método para hacer esto es:

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

Entonces llamo este método 3 veces para agregar 3 números:

 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 

Observe que el tercer número es el mismo que el primero, y no debe agregarse a la base de datos. Pero el logcat muestra:

 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. 

El tercer número se agregó aunque no se suponía que lo hiciera, porque fintInBackground() se ejecuta en 3 subprocesos de fondo casi simultáneamente, por lo que encontrará que no hay número en la base de datos como el que quiero agregar.

En esta pregunta, un tipo me dijo que debía usar la biblioteca de Bolts de Parse . He leído sobre esto desde aquí y algunas publicaciones de blog de Parse , pero no entiendo completamente cómo usar esto con el método que ya tengo y cómo sincronizar las consultas que se ejecutan una tras otra.

Si alguien trabajó con esta biblioteca, por favor guíame sobre cómo hacer esto o proporcione algunos ejemplos básicos para que pueda entender el flujo de trabajo.

¡Gracias!

Parece que tienes una condición de carrera. Hay muchas maneras diferentes de resolver este problema. Aquí hay una alternativa no pernos.

El principal problema es que las consultas de búsqueda están ocurriendo aproximadamente al mismo tiempo para que no encuentren duplicados en la base de datos. En este caso la forma en que lo resolvemos es buscar y agregar a una persona a la vez, obviamente no en el hilo principal ya que no queremos atar el ui haciendo búsquedas. Así que vamos a hacer una lista que hace dos cosas 1) contiene elementos que deben agregarse, 2) verifica que se pueden agregar. Podemos utilizar la tarea asíncrona para mantener nuestra búsqueda fuera del mainthread.

He aquí una idea aproximada de lo que hay que hacer:

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

Debe eliminar los registros duplicados antes de guardarlos en la base de datos. Usar HashSet y crear un objeto de persona con sobrescribir sus métodos de equals() y hashCode() resolverá el problema de registros duplicados. El número de teléfono es un valor único, por lo que si los números de teléfono son iguales a los demás, debemos guardar sólo uno de ellos. Esto significa que debe sobreescribir los métodos equals() y hashCode() del objeto Person utilizando sólo phone campo de phone .

 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 + "]"; } } 

Pruebas:

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

Huellas dactilares:

[Persona [nombre = Joe, teléfono = + 199999, isFav = false], Persona [nombre = Jessie, teléfono = + 133333, isFav = false]]

HashSet tiene objetos Person que tienen números de teléfono únicos. Johnny tiene el mismo número de teléfono con Joe, por lo que HashSet rechaza agregar a Johnny a la lista.

También la adición del método CallBack en putPerson() le ayudará sobre la operación de fijación fallida.

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

Esto es de la documentación de los pernos.

Tareas en serie

Las tareas son convenientes cuando se desea realizar una serie de tareas en una fila, cada una esperando que la anterior termine. Por ejemplo, imagine que desea eliminar todos los comentarios de su blog.

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

Así es como se hace. Si quieres saber lo que está pasando exactamente debes leer la documentación aunque pueda parecer bastante sencillo 🙂

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

  • Cómo obtener todos los valores de columna de una sola fila en android parse.com
  • No se puede iniciar la aplicación de receptor com.parse.ParseBroadcastReceiver en Trigger.io
  • Xamarin falla aplicación Android en el modo de liberación (Parse.Android SDK)
  • Ingresar usando Google+ en Parse
  • Usando ParseFacebookUtils.logInWithReadPermissionsInBackground no guarda los permisos de Facebook Android
  • NoClassDefFoundError en ParseUtils
  • Almacenar varios objetos ParseFile en un único ParseObject
  • ParseFacebookUtils Android auth_type: 'reautenticar'
  • Force Parse Push Notifications para usar PPNS en lugar de GCM
  • Facebook Login no funciona correctamente (Parse)
  • No se puede recibir notificación push en Android mediante Parse
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.