Android:如何使用 Parse.com 的 Bolts 同步查询?
Posted
技术标签:
【中文标题】Android:如何使用 Parse.com 的 Bolts 同步查询?【英文标题】:Android: How to synchronize queries with Bolts from Parse.com? 【发布时间】:2016-04-02 06:05:22 【问题描述】:我使用Parse.com
作为我的应用程序的后端。他们还提供本地数据库来存储信息,作为SQLite
的替代方案。
我想通过解析将电话中的号码添加到我的数据库中。在添加数字之前,我需要检查该数字是否已存在于数据库中,因此我使用findInBackground()
来获取与我要添加的数字匹配的数字列表。如果列表为空,则我要添加的数字在数据库中不存在。
这样做的方法是:
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());
);
然后我调用这个方法3次相加3个数字:
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
请注意,第三个数字与第一个数字相同,不应将其添加到数据库中。但logcat
显示:
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.
第三个数字即使不应该这样做也被添加了,因为fintInBackground()
几乎同时在3个后台线程中运行,所以它会发现数据库中没有我想要的数字添加。
在this 问题中,一个人告诉我我应该使用来自Parse
的Bolts
库。我从here 和一些Parse
博客文章中读到了它,但我不完全理解如何将它与我已有的方法一起使用,以及如何同步要一个接一个地执行的查询。
如果有人使用此库,请指导我如何执行此操作或提供一些基本示例,以便我了解工作流程。
谢谢!
【问题讨论】:
如果在 pinInBackground() 中添加 callBack 方法,您将解决重复行问题。 @SedatPolat 怎么样?问题不在于 pinInBackground()。问题是当调用 findInBackgroud() 时,所有 3 个查询几乎同时进行。我想一个接一个地处理这个查询。 如果您按照我的回答将 CallBack 添加到 pinInBackground(),您的保存操作将相互等待。 【参考方案1】:您应该在将重复记录保存到数据库之前删除它们。使用HashSet
并创建一个覆盖其equals()
和hashCode()
方法的人对象将解决重复记录问题。电话号码是唯一值,所以如果电话号码与其他号码相同,我们应该只保存其中一个。这意味着您应该只使用 phone
字段来覆盖 Person
对象的 equals()
和 hashCode()
方法。
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 + "]";
测试:
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);
打印:
[人 [name=Joe, phone=+199999, isFav=false], Person [name=Jessie, phone=+133333, isFav=false]]
HashSet 有 Person
具有唯一电话号码的对象。 Johnny 与 Joe 的电话号码相同,因此 HashSet
拒绝将 Johnny 添加到列表中。
还在putPerson()
上添加CallBack
方法将帮助您解决失败的固定操作。
person.pinInBackground( new SaveCallback()
@Override
public void done( ParseException e )
if( e == null )
pinnedPersonList.add(person)
else
unPinnedPersonList.add(person)
);
【讨论】:
我测试过但结果是一样的。第三个还在添加。预计会是这样,因为findInBackground()
是搜索号码是否存在的方法。它与pinInBackGround()
无关。我需要让findInBackground()
方法互相等待。
代替“findInBackground()”,你可以使用“query.find()”
是的 query.find() 有效,但添加 100 个数字需要 1.5 到 3 秒(以防我的数据库在第一次运行时为空,否则可能需要更长时间)。理想的解决方案是使用不在 UI 线程中完成所有工作的 findInBackground(),并在该单独线程中同步查询。另外我想学习如何使用 Bolts 库,以防我需要它来解决另一个问题。
如果我找不到更好的解决方案,您认为在单独的线程中使用 query.find() 可以吗?
您可以启动一个后台线程来放置人员列表。这将对您有所帮助。【参考方案2】:
这是来自 Bolts 文档。
系列任务
当您想要连续执行一系列任务时,任务很方便,每个任务都等待前一个任务完成。例如,假设您想删除博客上的所有 cmets。
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;
);
这就是它的完成方式。如果你想了解到底发生了什么,你应该阅读文档,即使它看起来很简单:)
https://github.com/BoltsFramework/Bolts-android
【讨论】:
【参考方案3】:听起来你有一个竞争条件。有很多不同的方法可以解决这个问题。这是一个非螺栓替代方案。
主要问题是搜索查询几乎同时发生,因此它们在数据库中找不到重复项。在这种情况下,我们解决它的方法是一次只搜索和添加一个人,显然不是在主线程上,因为我们不想捆绑 ui 进行搜索。因此,让我们创建一个 List 来做两件事 1) 包含需要添加的项目,2) 验证它们是否可以添加。我们可以使用异步任务来让我们的搜索远离主线程。
这是需要做的一个粗略的想法:
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();
【讨论】:
以上是关于Android:如何使用 Parse.com 的 Bolts 同步查询?的主要内容,如果未能解决你的问题,请参考以下文章
如何在 Android 中禁用多个推送通知图标,我正在使用 Parse.com
如何让 Parse.com 推送通知在 Cordova/Phonegap Android 应用程序中工作?
如何使用 Parse.com 推送通知服务在 Android 应用程序中推送 URL?
Parse.com 在 Xamarin 中推送 Android