我正在使用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问题中,有人告诉我应该使用Bolts
中的Parse
库。我从here和一些Parse
博客文章中了解到了这一点,但我不完全理解如何使用我已有的方法,以及如何同步查询要一个接一个地执行
如果有人使用此库,请指导我如何执行此操作或提供一些基本示例,以便我了解工作流程。
谢谢!
答案 0 :(得分:1)
听起来你有竞争条件。有很多不同的方法可以解决这个问题。这是一种非螺栓替代方案。
主要问题是搜索查询大致同时发生,因此他们不会在数据库中找到重复项。在这种情况下,我们解决它的方式是一次只搜索和添加一个人,显然不在主线程上,因为我们不想绑定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();
}
}
}
答案 1 :(得分:0)
在将重复记录保存到数据库之前,应删除它们。使用HashSet
并创建人员对象并覆盖其equals()
和hashCode()
方法将解决重复记录问题。电话号码是唯一值,因此如果电话号码与其他电话号码相同,我们应该只保存其中一个号码。这意味着您应该仅使用equals()
字段覆盖hashCode()
对象的Person
和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 + "]";
}
}
测试:
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);
}
打印:
[Person [name = Joe,phone = + 199999,isFav = false],Person [name = Jessie,phone = + 133333,isFav = false]]
HashSet包含Person
个具有唯一电话号码的对象。 Johnny与Joe有相同的电话号码,因此HashSet
拒绝将Johnny添加到列表中。
在CallBack
上添加putPerson()
方法也可以帮助您解决钉扎操作失败问题。
person.pinInBackground( new SaveCallback() {
@Override
public void done( ParseException e ) {
if( e == null ) {
pinnedPersonList.add(person)
} else {
unPinnedPersonList.add(person)
}
}
} );
答案 2 :(得分:0)
这是来自Bolts的文档。
系列中的任务
当您想要连续执行一系列任务时,每个任务都很方便,每个任务都在等待前一个任务完成。例如,假设您要删除博客上的所有评论。
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;
}
});
这就是它的完成方式。如果你想要了解到底发生了什么,你应该阅读文档,即使它看起来很简单:)