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.
- Schedulers.io () no vuelve al hilo principal
- Cómo almacenar datos de sitio web en parse.com Y cómo obtener datos de él en mi aplicación
- Cómo cargar varias imágenes en ImageViews (parse.com)
- Parse: ¿Cómo configurar un puntero en Android?
- Causado por: java.lang.IllegalStateException: ParsePlugins ya está inicializado
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!
- Navegar hasta la actividad de los padres desde la notificación
- Varias llaves y valores de JSONObject no se guardarán en Parse
- La devolución de llamada saveInBackground no funciona
- Notificación de envío extra enviada al dispositivo Android
- Cómo cargar una imagen en el servidor de análisis utilizando analizar api en android
- NullPointerException al iniciar sesión con Facebook SDK y ParseFacebookUtils
- Parse.com runtime crash - android
- ¿Cómo enlazar a Google + usuarios registrados en el backend de Parse en Android?
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 🙂
- Cómo eliminar la ventana de mensaje Problemas de procesamiento en Intellij IDEA 13 vista previa del diseño
- ¿Puedo configurar un icono diferente para el selector de tareas de Android?