Parse作为一个Mobile Backend As A Service,2013年被Facebook收购(YC S11)。前些天,Parse宣布开源其后端SDK( Parse Platform)。笔者作为Parse的长期忠实用户,迫不及待的跑去狂刷代码了。
对于不熟悉Parse的读者,我先简单的介绍一下。Parse为开发者包办了后台服务,从任意数据保存到发送推送通知,以及Facebook/Twitter 帐号登陆,几乎囊括一个App所需要的大多数后台服务。所以,你用Parse来开发你的Android/iOS App,一般能节省你10-100倍的时间。
图中的一个ParseObject对应在Parse Cloud上的一个Table里面的一个record,按照documentation里面的说法就是:
ParseObject和Parse Cloud通过Controller来进行交互,Controller可以serialize和deserialize ParseObject的状态成Parse的public REST format,并能把所有的请求传到Parse的内部呢tworking logic。
本篇文章主要是分析其client端的SDK,也就是上图中除开Parse Cloud的那一部分。你要问我Parse Cloud怎么搭建起来,我也不知道。
BoltsFramework
我们不得不提到去年Parse开源的BoltsFramework,Parse的Android和iOS SDK都用Bolts来处理异步请求。Bolts的第一个Component就是Task,它可以让复杂的异步代码更好管理。熟悉Javascript的读者,应该知道JavaScript Promises。Task就好比Promise,下面的代码可以给读者带来比较直观的感受:
[[object saveAsync:obj] continueWithBlock:^id(BFTask *task) {
if (task.isCancelled) {
// the save was cancelled.
} else if (task.error) {
// the save failed.
} else {
// the object was saved successfully.
SaveResult *saveResult = task.result;
}
return nil;
}];
object.saveAsync().continueWith(new Continuation<ParseObject, Void>() {
public Void then(Task task) throws Exception {
if (task.isCancelled()) {
// the save was cancelled.
} else if (task.isFaulted()) {
// the save failed.
Exception error = task.getError();
} else {
// the object was saved successfully.
SaveResult saveResult = task.getResult();
}
return null;
}
});
一个Task表示一个asynchronous操作。一般来说,一个Task来自于一个asynchronous function,并且让你可以继续处理Task的执行结果。通过Bolts来创建Task非常简单:
public Task<String> succeedAsync() {
// Java Generics syntax can be confusing sometimes. :)
// This creates a TCS for a Task<String>.
Task<String>.TaskCompletionSource successful = Task.create();
successful.setResult("The good result.");
return successful.getTask();
}
创建完Task后,我们的Task有一个方法onSuccess来指定一个Continuation。当Task成功执行完,抛出异常,或被取消了,我们的Task会运行执行这个Continuation。Continuation 是一个接口,只有一个当task执行完后会被调用的then方法(这里还有一个onSuccessTask方法,这个方法会调用continueWithTask方法创建一个新的asynchronous task)。
succeedAsync().onSuccess(new Continuation<ParseObject, Void>() {
public Void then(Task<ParseObject> task) throws Exception {
// the object was saved successfully.
return null;
}
});
Parse-SDK-Android
在对Bolts有个基本的了解后,我们来分析下Parse-SDK-Android。对于一个ParseObject来说,常见的操作就是fetchInBackground(),即:在后台线程中,从server获取这个ParseObject的数据。
public final <T extends ParseObject> void fetchInBackground(GetCallback<T> callback) {
ParseTaskUtils.callbackOnMainThreadAsync(this.<T>fetchInBackground(), callback);
}
public final <T extends ParseObject> Task<T> fetchInBackground() {
return ParseUser.getCurrentSessionTokenAsync().onSuccessTask(new Continuation<String, Task<T>>() {
@Override
public Task<T> then(Task<String> task) throws Exception {
final String sessionToken = task.getResult();
return taskQueue.enqueue(new Continuation<Void, Task<T>>() {
@Override
public Task<T> then(Task<Void> toAwait) throws Exception {
return fetchAsync(sessionToken, toAwait);
}
});
}
});
}
一般我们会调用ParseObject的fetchInBackground(GetCallback<T> callback)方法,在这里创建一个Callback出来用以处理从Cloud接收到的数据。很多时候,我们可能会要在这个Callback里面更新UI,或者再去调用其它的网络请求。因此,这样会常常带来许多不必要的麻烦,以及潜在的Bug。后面我们会介绍如何处理这些callback hell或者切换线程的方法,请持续关注哦。
这里ParseTaskUtils的方法callbackOnMainThreadAsync通过Task来“包装”Callback中获取到的请求结果,因而这样的Task可以继续链接后续的Task。
static Task<Void> callbackOnMainThreadAsync(Task<Void> task, final ParseCallback1<ParseException> callback) {
return callbackOnMainThreadAsync(task, callback, false);
}
static <T> Task<T> callbackOnMainThreadAsync(Task<T> task, final ParseCallback2<T, ParseException> callback, final boolean reportCancellation) {
if (callback == null) {
return task;
}
final Task<T>.TaskCompletionSource tcs = Task.create();
task.continueWith(new Continuation<T, Void>() {
@Override
public Void then(final Task<T> task) throws Exception {
if (task.isCancelled() && !reportCancellation) {
tcs.setCancelled();
return null;
}
//回到ui thread来执行
ParseExecutors.main().execute(new Runnable() {
@Override
public void run() {
try {
Exception error = task.getError();
if (error != null && !(error instanceof ParseException)) {
error = new ParseException(error);
}
callback.done(task.getResult(), (ParseException) error);
} finally {
if (task.isCancelled()) {
tcs.setCancelled();
} else if (task.isFaulted()) {
tcs.setError(task.getError());
} else {
tcs.setResult(task.getResult());
}
}
}
});
return null;
}
});
return tcs.getTask();
}
在这里挖一个坑,下一篇我们会介绍一个fetchInBackground方法的调用是如何涉及到一个网络请求的,请继续关注哦。
如果你喜欢本文章,欢迎推荐到你的朋友圈。更多好文章请关注我们的微信。
WeChat ID: mtl-news
长按下图识别二维码,关注我们哦