移動(dòng)后端服務(wù)BaaS現(xiàn)狀
目前,國外至少已經(jīng)有二十多家企業(yè)進(jìn)入了這個(gè)領(lǐng)域,其中,提供的后端服務(wù)比較全面的有StackMob、Parse、Kinvey。而國內(nèi),和國外的概念比較接近的有Bmob、AMTBaaS,還有我們的Xone。
StackMob
StackMob算是最早進(jìn)入該領(lǐng)域的,成立于2010年1月,去年5月份的時(shí)候,那時(shí)該團(tuán)隊(duì)還只有6個(gè)人,就獲得了750萬美元的投資。目前該團(tuán)隊(duì)有24個(gè)人,每月訪問量已經(jīng)超過300萬,使用其服務(wù)的應(yīng)用已經(jīng)達(dá)到120萬。支持的SDK已經(jīng)涵蓋了iOS、Android、JavaScript、 Ruby,還有Custom Code。其中,Custom Code是用于服務(wù)端開發(fā)的,可以用Java和Scala語言進(jìn)行開發(fā)。通過Custom Code,開發(fā)者就可以在服務(wù)端處理一些無法在客戶端處理的邏輯。StackMob提供的功能服務(wù)也挺全面的,大的方面,數(shù)據(jù)存儲(chǔ)、推送服務(wù)、地理位置服務(wù)、社交等,再細(xì)入到支持角色管理、ACL、復(fù)雜和多層的數(shù)據(jù)模型、多種查詢條件等等。
而那么多SDK中,我只看了Android的。其SDK也挺簡單易用的,應(yīng)用程序自身的數(shù)據(jù)模型只要繼承StackMobModel即可,而用戶類則繼承StackMobUser,然后可以直接定義自己的屬性。另外,它也支持嵌套的數(shù)據(jù)模型,最多支持3層嵌套。比如下面的代碼:
- public class Task extends StackMobModel {
- private String name;
- private Date dueDate;
- private int priority;
- private boolean done;
- private Task prerequisite;
- public Task(String name, Date dueDate) {
- super(Task.class);
- this.name = name;
- this.dueDate = dueDate;
- this.priority = 0;
- this.done = false;
- }
- public void setPrerequisite(Task prereq) {
- this.prerequisite = prereq;
- }
- }
- public class TaskList extends StackMobModel {
- private String name;
- private List<Task> tasks;
- private Task topTask;
- private TaskList parentList;
- public TaskList(String name) {
- super(TaskList.class);
- tasks = new ArrayList<Task>();
- this.name = name;
- }
- public String getName() {
- return name;
- }
- public List<Task> getTasks() {
- return tasks;
- }
- }
保存數(shù)據(jù)時(shí),代碼就可以如下:
- TaskList taskList = new TaskList("StackMob Tasks");
- taskList.getTasks().add(new Task("Learn about relations", new Date()));
- taskList.getTasks().add(new Task("Learn about queries", new Date()));
- taskList.save(StackMobOptions.depthOf(1));
StackMob的這種模式,簡單易懂,操作上也很方便,數(shù)據(jù)模型的擴(kuò)展性也很好。
不過,文件存儲(chǔ)就沒這么方便了,StackMob不提供將文件上傳到它自己的平臺(tái)服務(wù)器,只提供接口,讓你將文件上傳到開發(fā)者自己的S3上面。也就是說,開發(fā)者必須自己先申請AWS的S3,才可以使用StackMob提供的文件存儲(chǔ)服務(wù)。
還有,StackMob的推送服務(wù)是使用Google的GCM服務(wù)的,所以,開發(fā)者還需要擁有Google的GCM賬號才能注冊使用StackMob的推送服務(wù)。
另外,其Model和Controller的分離也還不夠好。例如,對于用戶操作的方法,在StackMobUser里提供了很多方法,但在StackMob里也提供了不少方法。如果將數(shù)據(jù)操作的部分可以統(tǒng)一接口就更好了。
再看看StackMob提供的Custom Code,它也提供了操作后臺(tái)數(shù)據(jù)很全面的各種方法和類。使用起來也挺簡單,比如寫個(gè)hello_world的例子,后臺(tái)服務(wù)類需要實(shí)現(xiàn)CustomCodeMethod接口,比如下面代碼:
- public class HelloWorldExample implements CustomCodeMethod {
- /**
- * This method simply returns the name of your method that we'll expose over REST for
- * this class. Although this name can be anything you want, we recommend replacing the
- * camel case convention in your class name with underscores, as shown here.
- *
- * @return the name of the method that should be exposed over REST
- */
- @Override
- public String getMethodName() {
- return "hello_world";
- }
- /**
- * This method returns the parameters that your method should expect in its query string.
- * Here we are using no parameters, so we just return an empty list.
- *
- * @return a list of the parameters to expect for this REST method
- */
- @Override
- public List<String> getParams() {
- return Arrays.asList();
- }
- /**
- * This method contains the code that you want to execute.
- *
- * @return the response
- */
- @Override
- public ResponseToProcess execute(ProcessedAPIRequest request, SDKServiceProvider serviceProvider) {
- //Send push messages...
- //Query the datastore...
- //Run complex server side operations..
- //Then prepare your custom JSON to send back to the mobile client
- Map<String, String> args = new HashMap<String, String>();
- args.put("msg", "hello world!");
- return new ResponseToProcess(HttpURLConnection.HTTP_OK, args);
- }
- }
然后,在Android端這樣調(diào)用:
- StackMob.getStackMob().getDatastore().get("hello_world", new StackMobCallback() {
- @Override public void success(String responseBody) {
- //responseBody is "{ \"msg\": \"Hello, world!\" }"
- }
- @Override public void failure(StackMobException e) {
- }
- });
StackMob教程指南:https://developer.stackmob.com/tutorials
#p#
Parse
Parse比StackMob晚成立一年,近期也得到了700萬美元的注資,目前團(tuán)隊(duì)20人,有2萬名開發(fā)者使用其平臺(tái),40萬款應(yīng)用使用其服務(wù)。 支持的API有iOS、Android、JavaScript、Windows 8,以及提供了REST API和Cloud Code JS API。Cloud Code JS API和StackMob的Custom Code一樣用于服務(wù)端開發(fā),處理自己的邏輯,不同的是,StackMob用Java或Scala開發(fā),而Parse提供JS的接口。Parse提供的功 能服務(wù)也算是比較全面的,StackMob提供的大部分功能服務(wù),它也提供了。而支持Windows8,這點(diǎn)還比StackMob領(lǐng)先了一步。另外,它對 ACL的設(shè)置也比StackMob靈活。StackMob只能在后臺(tái)管理頁面對角色設(shè)置ACL,而Parse可以在代碼中設(shè)置全局的ACL,也可以對每個(gè) 對象設(shè)置ACL。文件存儲(chǔ)方面,Parse雖然也是保存在S3,但它將用戶的文件保存在自己的平臺(tái),而不是開發(fā)者自己的S3,這一點(diǎn)也比StackMob 要方便得多。
不過,Parse的SDK使用起來就沒StackMob那么方便了。Parse保存和查詢對象的代碼如下:
- ParseObject gameScore = new ParseObject("GameScore");
- gameScore.put("score", 1337);
- gameScore.put("playerName", "Sean Plott");
- gameScore.put("cheatMode", false);
- gameScore.saveInBackground();
- ParseQuery query = new ParseQuery("GameScore");
- query.getInBackground("xWMyZ4YEGZ", new GetCallback() {
- public void done(ParseObject object, ParseException e) {
- if (e == null) {
- int score = object.getInt("score");
- String playerName = object.getString("playerName");
- boolean cheatMode = object.getBoolean("cheatMode");
- } else {
- // something went wrong
- }
- }
這樣子,開發(fā)者就很難定義自己的數(shù)據(jù)模型,全部操作都只能通過ParseObject了。Model和Controller完全耦合在一起,應(yīng)用與Parse的耦合性非常強(qiáng),如果開發(fā)者想要移植到其他平臺(tái)就會(huì)很麻煩。而且,這種模式,對于開發(fā)比較復(fù)雜的應(yīng)用也將變得麻煩。
再看看Parse的Cloud Code。使用Parse的Cloud Code,需要下載Parse提供的命令行工具,然后需要用parse new命令創(chuàng)建一個(gè)存放代碼的目錄,還需要輸入郵箱和密碼登錄開發(fā)者的賬號,選擇一個(gè)應(yīng)用,并用cd命令進(jìn)入創(chuàng)建的目錄:
$ parse new MyCloudCode Email: ninja@gmail.com Password: 1:MyApp Select an App: 1 $ cd MyCloudCode
MyCloudCode目錄會(huì)自動(dòng)生成幾個(gè)文件:
-config/ global.json -cloud/ main.js
其中,main.js就是保存開發(fā)者自己所有Cloud Code的地方。寫個(gè)最簡單的例子:
- Parse.Cloud.define("hello", function(request, response) {
- response.success("Hello world!");
- });
然后用parse deploy命令部署到Parse平臺(tái)上:
$ parse deploy
然后,在Android端就可以用以下代碼調(diào)用:
- ParseCloud.callFunction("hello", new HashMap<Object, Object>(), new FunctionCallback<String>() {
- void done(String result, ParseException e) {
- if (e == null) {
- // result is "Hello world!"
- }
- }
- });
Parse文檔:https://parse.com/docs
#p#
Kinvey
Kinvey則是TechStars下的一家創(chuàng)業(yè)公司,也是去年成立,到目前為止也已經(jīng)得到了700萬美元的投資,目前團(tuán)隊(duì)19人,而使用其平臺(tái)的 應(yīng)用數(shù)則沒有公布。前端支持的API有iOS、Android、JavaScript和REST,后端同樣提供了Backend Logic接口供開發(fā)者處理后臺(tái)邏輯。相較于StackMob和Parse,Kinvey提供的功能就沒那么全面了,缺少了角色管理,也缺少了地理位置服 務(wù)。
Kinvey的SDK有一點(diǎn)我比較喜歡,那就是Model和Controller有了較好的分離,Model提供了接口 MappedEntity,Controller則用KCSClient做入口。不過,數(shù)據(jù)模型的定義卻不如StackMob那樣方便,多了一步屬性與后 臺(tái)表字段的映射。代碼如下:
- public class MyEntity implements MappedEntity {
- private String uname;
- private String id;
- @Override
- public List<MappedField> getMapping() {
- return Arrays.asList(new MappedField[] { new MappedField("uname", "name"),
- new MappedField ("id", "_id") });
- }
- // Getters and setters for all fields are required
- public String getUname() { return uname; }
- public void setUname(String n) { uname = n; }
- public String getId() { return id; }
- public void setId(String i) { id = i; }
- }
其中,getMapping方法就是處理映射關(guān)系。然后保存數(shù)據(jù)就可以通過以下代碼:
- MyEntity item = ...;
- item.setName("Idle Blue");
- mKinveyClient.mappeddata("collectionName").save(item, new ScalarCallback<MyEntity>() {
- @Override
- public void onFailure(Throwable e) { ... }
- @Override
- public void onSuccess(MyEntity r) { ... }
- });
其中,mKinveyClient.mappeddata("collectionName")方法得到MappedAppData,該類封裝了 MappedEntity相關(guān)的各種操作。另外,通過mKinveyClient.resource("name")方法則得到 KinveyResource,該類封裝了文件相關(guān)的各種操作。
Kinvey另外還提供了和ParseObject類似的方式,代碼如下:
- EntityDict album = mKinveyClient.entity("albums");
- album.putProperty("title", "Idle Blue");
- album.save(new ScalarCallback<EntityDict>() {
- @Override
- public void onFailure(Throwable e) { ... }
- @Override
- public void onSuccess(EntityDict r) { ... }
- });
Kinvey的Backend Logic與StackMob和Parse有著很大不同。StackMob和Parse雖然處理方式不同,但基本思路都是前端傳送指定的方法(如 hello_world),后臺(tái)寫相應(yīng)的代碼處理這個(gè)方法。而Kinvey的Backend Logic則是在對數(shù)據(jù)庫操作的前后分別提供接口處理。如下圖:
PreProcess提供了幾個(gè)方法:
- function onPreSave(request,response,modules){
- }
- function onPreDelete(request,response,modules){
- }
- function onPreFetch(request,response,modules){
- }
PostProcess也提供了幾個(gè)方法:
- function onPostSave(request,response,modules){
- }
- function onPostDelete(request,response,modules){
- }
- function onPostFetch(request,response,modules){
- }
Kinvey文檔:http://docs.kinvey.com/overview.html
#p#
Bmob
Bmob不是一家公司的名字,只是廣州一家叫鵬銳的IT公司的其中一個(gè)產(chǎn)品。Bmob應(yīng)該算是國內(nèi)第一個(gè)BaaS平臺(tái),今年4月份的時(shí)候上線的。而 到目前為止,Bmob還只有Android的SDK,而且還是山寨Parse的,但功能比Parse少了很多,沒有角色管理,沒有ACL,也沒有關(guān)系查 詢,更沒有后端的Cloud Code,跟Parse的SDK比較一下,就會(huì)知道相比Parse少了非常多的東西,基本上就是Parse的一個(gè)簡化版。版本升級過一次,在第二版加入了 支付功能,而支付卡基本上都是些游戲卡,很明顯,這是為付費(fèi)游戲提供的服務(wù)。
看看Bmob的代碼,和Parse一樣的吧:
- BmobObject gameScore = new BmobObject("GameScore");
- gameScore.put("score", 1200);
- gameScore.put("playerName", "張小明");
- gameScore.put("cheatMode", false);
- try {
- gameScore.save();
- } catch (BmobException e) {
- // e.getMessage() 捕獲的異常信息
- }
Bmob第一版的時(shí)候,就出現(xiàn)過很多問題,如經(jīng)常斷線,數(shù)據(jù)丟失等,逐漸失去開發(fā)者的信任,基本功能也還很不完善。而第二版沒有去完善那些重要的功 能,提高開發(fā)者的信任度,反而推出了支付功能,我覺得這是個(gè)錯(cuò)誤的策略。首先,重點(diǎn)應(yīng)該放在完善基本功能和提高開發(fā)者對平臺(tái)的信任度;其次,支付功能這種 安全性要求非常高的服務(wù),只有在平臺(tái)已經(jīng)非常成熟,用戶對其已經(jīng)非常信任的前提下提供才會(huì)有效用,在經(jīng)常斷線,數(shù)據(jù)會(huì)出現(xiàn)丟失的情況下,有誰敢去用支付功 能呢?
另外,Bmob還提供了很多應(yīng)用分析的功能,這一點(diǎn),我也覺得多余了。專業(yè)的應(yīng)用分析,已經(jīng)有友盟平臺(tái)了,沒必要自己再做一套。
Bmob還存在很多不成熟的地方,第一版上線到目前也已經(jīng)半年多了,改善的東西很少,發(fā)展速度較慢,給我的感覺就是他們沒有抓到真正的重點(diǎn),或者他們將精力移到了廣告平臺(tái)——他們的另一個(gè)產(chǎn)品,畢竟那是實(shí)實(shí)在在可以賺到錢的產(chǎn)品。
Bmob官網(wǎng):http://bmob.cn/
#p#
AMTBaaS
AMTBaaS是誠邁科技最近推出的BaaS平臺(tái),9月中旬發(fā)布了iOS的SDK,10月中旬又發(fā)布了Android的SDK。目前,處于內(nèi)測階 段。提供的文檔,除了API,也只有一個(gè)用戶手冊提供下載。網(wǎng)站和文檔的用戶體驗(yàn)都比較差,其SDK的體驗(yàn)也是很差,就說一個(gè)用戶注冊的方法:
- register(Context context, java.lang.String useremail, java.lang.String passWord, java.lang.String confirmPwd, boolean ischeckCode, java.lang.String checkcode, AmtUserCallback amtUserCallback)
再看看用戶注冊的操作:
- // 注冊消息
- public static final int MESSAGE_REGISTER = 1001;
- // 構(gòu)造AmtUser對象
- private AmtUser amtUser = new AmtUser();
- // 注冊
- amtUser.register(UserActivity.this, “user@qq.com”, “123456”, “123456”, true, “53Fe”, new AmtUserCallback() {
- @Override
- public void onSuccess(Object object) {
- if (object != null) {
- // 回調(diào)方法中不可直接操作UI控件
- mhandler.sendMessage(mhandler.obtainMessage(
- MESSAGE_REGISTER, object));
- }
- }
- @Override
- public void onFailure(AmtException amtException) {
- if (amtException != null) {
- // 回調(diào)方法中不可直接操作UI控件
- mhandler.sendMessage(mhandler.obtainMessage(
- MESSAGE_REGISTER, amtException));
- }
- }
- });
- /**
- * 注冊回調(diào)句柄
- */
- Handler handler = new Handler() {
- @Override
- public void handleMessage(Message msg) {
- super.handleMessage(msg);
- switch (msg.what) {
- case MessageTypes.MESSAGE_REGISTER:
- // 注冊失敗
- if (msg.obj instanceof AmtException) {
- AmtException amtException = (AmtException)msg.obj;
- AmtUtil.log("UserActivity", "注冊失?。?quot;+
- amtException.getMessage(), "i");
- }
- // 注冊成功
- else if (msg.obj instanceof String) {
- String result = (String)msg.obj;
- If (AmtConstants.SUCCESS.equals(result) {
- AmtUtil.log("UserActivity", "注冊成功", "i");
- }
- }
- break;
- }
- }
- };
一個(gè)注冊就這么麻煩,完全不懂用戶體驗(yàn)。對于其前景不看好。
AMTBaaS官網(wǎng):http://amtbaas.com/
#p#
Xone
Xone則是我們公司現(xiàn)在的核心產(chǎn)品,目前開發(fā)了兩個(gè)月左右,前端只支持 Android,目前的SDK提供的功能有版本管理、數(shù)據(jù)存儲(chǔ)、用戶管理、文件存儲(chǔ)、地理信息服務(wù),推送服務(wù)還沒開發(fā)出來。后端也提供JS的 Backend Logic服務(wù),剛開發(fā)完。網(wǎng)站也只是搭了個(gè)雛形,現(xiàn)在正在重新設(shè)計(jì)和完善之中。
為了將Model和Controller較好地分離并能簡單的使用SDK,我們采用了這樣的結(jié)構(gòu):
1. 數(shù)據(jù)模型我們定義了一個(gè)基類XoneEntity,類里只定義了幾個(gè)基本屬性,不帶任何數(shù)據(jù)操作的方法。另外定義了繼承它的兩個(gè)子 類,XoneGeoEntity和XoneUser,XoneGeoEntity用于帶有地理信息的數(shù)據(jù)模型,XoneUser則是用戶數(shù)據(jù)模型。應(yīng)用程 序自身的數(shù)據(jù)模型則繼承XoneEntity,然后定義自己的屬性即可,不需要像Kinvey那樣還要做映射。如果應(yīng)用程序自身的數(shù)據(jù)模型需要包含地理信 息,則繼承XoneGeoEntity,用戶模型則繼承XoneUser。
2. 對各種服務(wù)分類提供了接口,每個(gè)接口里定義了相應(yīng)的數(shù)據(jù)操作的方法。服務(wù)接口有:
- AppService:應(yīng)用程序和平臺(tái)相關(guān)的服務(wù)接口
- CollectionService<T extends XoneEntity>:集合相關(guān)的服務(wù)接口
- UserService<T extends XoneUser>:用戶相關(guān)的服務(wù)接口
- FileService:文件相關(guān)的服務(wù)接口
3. XoneClient是Controller的入口,通過XoneClient實(shí)例的getXXXService()的各種方法調(diào)用各種服務(wù)接口,再通過服務(wù)接口調(diào)用具體的數(shù)據(jù)操作方法。比如,用戶注冊:
- UserService<XoneUser> userService = xoneClient.getUserService();
- userService.signUp(user, callback);
一切都還在完善之中,各種功能服務(wù),以及網(wǎng)站。后續(xù),當(dāng)我們的平臺(tái)正式上線時(shí),我們的SDK還將會(huì)開源。
Xone官網(wǎng):http://xone.im/