AVOS Cloud 移动开发 SDK 为 iOS、Android 和 WindowsPhone® 应用程序提供了基于云的 API 和服务,并且还提供了 JavaScript 和 REST API。使用 AVOS Cloud API,您可以极快地以最少工作量让您的移动应用程序支持云处理。集成了 AVOS Cloud SDK 的移动应用程序可以轻松地在 AVOS Cloud 云上存储数据对象和文件,发送并侦听推送通知,管理用户,处理地理位置数据,并可以轻松获得移动IM的能力。
AVOS Cloud 控制台可以帮助开发人员管理应用程序。该控制台为 API、文件和推送的通知提供了一般指标和应用程序特定的使用指标。通过控制台可管理应用程序的基本设置信息,它还提供了数据浏览器,使开发人员能够浏览(甚至编辑)所存储的数据对象。数据浏览器对于调试非常有用。图 1 是 AVOS Cloud 控制台的屏幕截图:
通过一个 App ID 和 App Key 对应用程序进行身份验证。为了获得您的应用程序 ID 和 Key,必须通过 AVOS Cloud 控制台注册您的应用程序。在您的应用程序上初始化 AVOS Cloud SDK 时,会用到这些信息。
在 AVOS Cloud 中,使用 “键-值” 对的容器 AVObject 表示数据。AVObject 可以存储任何与 JSON 兼容的数据,如清单 1 所示:
AVObject myObject = new AVObject("MyObject"); // Class Name myObject.put("name", “Feng Junwen"); myObject.put("twitterHandle", “jwfing"); myObject.put("followers", 123456);
AVObject 在实例化时被赋予一个 classname(类名称)。在上述代码中,类名称是 "MyObject"。类名称与关系数据库中的表名称相似,同一类的 AVObject 对象相当于表中的行。
AVObject 暴露的方法类似于 Java Map 类中的方法,如 put、get 和 remove,以及大量特定于 AVObject 的其他方法。
AVObject名称键 必须是字母数字的,作为一个指导方针,请对名称键使用驼峰式大小写 (camel-casing) 格式。值可以是存储在 JSON 中的任何数据类型,也就是说,可以是数字、字符串、布尔值、数组、JSONObject.NULL、JSONObject 和 JSONArray。AVObject 所支持的其他数据类型是 Java Date 和 byte[] 数组。AVObject 还可以嵌套包含其他 AVObject。
清单 2 显示了部分受支持的 AVObject 值数据类型:
// Byte Array byte[] byteArray = {1, 2, 3, 4, 5}; // A date Date d = new Date(); // java.util.Date // A number int number = 21; // A String String name = "Enrique"; // A JSONArray - any mix of JSONObjects, JSONArrays, Strings, Booleans, // Integers, Longs, Doubles, null or NULL. JSONArray jArray = new JSONArray(); jArray.put(number); jArray.put(name); // A JSONObject JSONObject jObject = new JSONObject(); try { jObject.put("number", number); jObject.put("name", name); } catch (JSONException e) { e.printStackTrace(); } // A AVObject AVObject pObject = new AVObject("MyObject"); // Class name pObject.put("myByteArray", byteArray); pObject.put("myDate", d); pObject.put("myString", name); pObject.put("myNumber", number); pObject.put("myJsonArray", jArray); pObject.put("myJsonObject", jObject); pObject.put("myNull", JSONObject.NULL);
上述代码创建了一个 AVObject,它在 AVOS Cloud 云端被存储为一个对象。之后,我们可以保存、查询和更新,并能从云端存储中删除。甚至可以在应用程序离线时保存数据,AVOS Cloud SDK 将数据保存在本地,直到重新建立网络连接。
pObject.remove("myNumber"); // remove the field/key "myNumber" from pObject
在删除或添加字段后,或在更新当前字段后,您可以在云端保存(或更新)数据对象,只需调用其中一个 AVObject 的 save...() 方法,我稍后会在本文中讨论它们。
在我告诉您如何在 AVOS Cloud 对象上执行 CRUD 操作之前,您应该了解一下 用户、角色和 ACL(访问控制列表)。这三个都是非常重要的概念,有助于保护您的应用程序的数据对象。
名称为 AVUser 的类代表一个用户,并为应用程序提供帐户管理功能。每个应用程序都有与之关联的 AVUser 表。一个 AVUser 也是一个 AVObject,但具有更多的属性,如用户名、密码和电子邮件。您可以添加任何您认为合适的其他数据值。
在 AVOS Cloud 中,匿名用户是没有用户名或密码的用户。匿名用户对于那些不要求用户身份验证的移动应用程序功能非常有用。匿名用户可以创建数据对象,但这些对象寿命很短,并且在匿名用户登出之后就不再可用。
用户可以注册成为您应用的 AVUser 用户,如清单 3 所示:
AVUser user = new AVUser(); user.setUsername(“Feng Junwen"); user.setPassword(“123456&asdQWE"); user.setEmail(“[email protected]"); user.put("userType", "Author"); // add another field // Call the asynchronous background method to sign up user.signUpInBackground(new SignUpCallback() { public void done(AVException e) { if (e == null) { // Successful. Allow access to app. } else { // Failed.... } } });
username 和 email 必须是应用内惟一的。如果 username 或 email 已经被使用,那么注册调用将会失败。您应该有一个通知用户字段限制的机制,并提供一个重试的进程。
在注册之后,用户就可以登录您的应用,如清单 4 所示:
AVUser.logInInBackground(“Feng Junwen", “123456&asdQWE", new LogInCallback() { public void done(AVUser user, AVException e) { if (user != null) { // Successful. Allow access to app. } else { // Failed } } });
AVUser currentUser = AVUser.getCurrentUser(); if (currentUser != null) { // current user is valid } else { // current user not valid, ask for credentials }
在 AVOS Cloud 中,您可以通过调用 AVUser.logOut() 来重置当前用户,如清单 6 所示:
AVUser.logOut(); // static method
ACL 是关联到数据对象的访问权限(或控制)列表。AVACL 类允许您为给定的 AVObject 定义权限。使用 ACL,您可以定义对您的应用程序数据对象的公共访问,并且可以(通过角色)限制对特定用户或用户组的访问。清单 7 演示了 AVOS Cloud ACL 的用法:
// Define a AVOS Cloud user AVUser user = new AVUser(); user.setUsername(username); : : // Define a read/write ACL AVACL rwACL = new AVACL(); rwACL.setReadAccess(user, true); // allow user to do reads rwACL.setWriteAccess(user, true); // allow user to do writes : : // Define a AVOS Cloud object and its ACL AVObject gameObject = new AVObject("Game"); gameObject.setACL(rwACL); // allow user do read/writes on gameObject gameObject.saveInBackground(); // save the ACL'ed object to the cloud : : // You can define a public ACL that gives public access to the object AVACL publicACL = new AVACL(); publicACL.setPublicReadAccess(true); publicACL.setPublicWriteAccess(true); gameObject.setACL(publicACL); // allow public read/writes gameObject.saveInBackground(); // save the ACL'ed object to the cloud
您还可以为所有新创建的对象定义一个默认的 ACL。在清单 8 中,我已经将可读取和写入的默认 ACL 设置为 public,清单 7 中的 publicACL定义。
// Set a default ACL for all newly created objects as public access AVACL.setDefaultACL(publicACL, true);
虽然在这里没有演示,但我们也可以使用 AVRole 类为用户组授予访问权限。
接下来,我们将查看如何将 AVObject 数据对象保存到 AVOS Cloud 云,并在云端检索它们。
一旦创建并填充了 AVObject,就可以在 AVOS Cloud 云上保存。在云端保存数据对象实际上是利用 AVOS Cloud 最简单的操作之一,其复杂性通常与数据表示、编组、网络通信和传输等有关联,AVOS Cloud SDK 完全隐藏了此复杂性。您需要使用 helper 方法来将数据对象实例映射到 AVObject,并映射回来,您需要决定是在您自己的线程上调度 AVOS Cloud 保存操作,还是使用 save-in-the-background 方法。
谨防系统线程阻塞!
回想一下,在移动应用程序中,长时间的操作(如网络、文件或长的计算)不应该在主系统线程上完成。相反,应在一个单独的工作线程中执行它们。阻塞系统线程会对应用程序的用户界面的响应能力产生负面影响,有可能导致强行关闭您的应用程序。
AVObject 提供两个保存方法: save() 和 saveInBackground()。 saveInBackground()是建议的保存方法,因为它在自己的工作线程上运行保存操作。如果您选择使用同步的save() 方法,需要注意的是,您需要在该方法自己的工作线程上调用该方法,以避免 UI 被阻塞。
清单 9 显示的代码在后台保存 AVObject 数据对象:
// AVObject AVObject pObject = new AVObject("ExampleObject"); pObject.put("myNumber", number); pObject.put("myString", name); pObject.saveInBackground(); // asynchronous, no callback
清单 10 显示的代码使用了一个回调,在后台保存 AVObject 数据对象:
pObject.saveInBackground(new SaveCallback () { @Override public void done(AVException ex) { if (ex == null) { isSaved = true; } else { // Failed isSaved = false; } } });
save...() 方法的变形包括以下几种:
使用或不使用回调,saveAllinBackground() 保存 AVObject。
saveAll(List<AVObject> objects) 保存 AVObjects 的列表。
saveAllinBackground(List<AVObject> objects) 在后台保存 AVObjects 的列表。
saveEventually() 让您能够在未来某个时点将数据对象保存到服务器;如果 AVOS Cloud 云目前不可访问,则使用该方法。
一旦在云上已成功保存了 AVObject ,就会为对象提供惟一的 Object-ID。此 Object-ID 非常重要,因为它惟一地标识了此 AVObject 实例。例如,您可以使用 Object-ID 确定是否已在云上成功保存该对象,以便检索或刷新给定的对象实例和删除特定的 AVObject。
这一节将探讨查询和检索 AVOS Cloud 云上的数据对象的方法。您可以通过 object-ID 查询一个 AVObject,您也可以使用属性查询一个或多个 AVObject 对象。如果您已经有一个 AVObject,那么您可以通过从服务器提取最新的值,刷新或同步其内容。我们将在以下代码片段中探讨所有这些选项。
为了从 AVOS Cloud 云提取数据对象,使用 AVObject 方法 fetch() 或 fetchInBackground(),如清单 11 所示:
// AVObject AVObject pObject = new AVObject("ExampleObject"); : : // Fetch the AVOS Cloud object unconditionally try { pObject.fetch(); } catch (AVException e) { e.printStackTrace(); } // Fetch the AVOS Cloud object unconditionally, with Callback pObject.fetchInBackground(new GetCallback() { @Override public void done(AVObject obj, AVException ex) { if (ex == null) { // Success } else { // Failed } } });
您也可以仅在需要时提取数据,例如,提取一个 AVOS Cloud 对象关联的 AVObject 对象,如清单 12:
AVObject po = new AVObject("ExampleObject"); : AVObject po2 = po.getAVObject("key"); // Fetch only if needed try { po2.fetchIfNeeded(); } catch (AVException e) { e.printStackTrace(); }
另一个选项是在后台根据需要执行提取操作,并使用一个回调。为此,可以使用 AVObject 的 fetchIfNeededInBackground(GetCallback callback) 方法。
在某些情况下,您需要无条件地或仅在需要时一次提取一个 AVObject 集合。AVObject 为此提供了一组静态方法,每个方法都将一个 AVObject 的列表作为输入,然后返回一个 AVObject 的列表:
fetchAll(List<AVObject> objects)
fetchAllIfNeeded(List<AVObject> objects)
fetchAllIfNeededInBackground(List<AVObject> objects, FindCallback callback)
fetchAllInBackground(List<AVObject> objects, FindCallback callback)
希望您现在已看到 AVOS Cloud API 的强大,但是别着急,它还有更多功能!除了提取数据对象之外,AVOS Cloud 还可以让您使用 object-ID 或属性来查询数据对象。要从云端查询一个数据对象,可以使用 AVQuery。您可以使用 AVQuery 进行基本的和复杂的数据查询,接收给定的对象或匹配对象的 List。
清单 13 显示了如何在一个后台线程中使用给定 object-ID 从服务器检索特定的 AVObject:
String myID = "12345"; AVQuery query = new AVQuery("Players"); query.getInBackground(myID, new GetCallback() { @Override public void done(AVObject object, AVException e) { if (object != null) { // Get object } else { // Not found } } });
请注意,query.getInBackground() 没有使用 AVQuery 缓存。
清单 14 显示的查询检索 Players 类的所有数据对象。(没有提供约束。)
AVQuery query = new AVQuery("Players"); query.findInBackground(new FindCallback() { @Override public void done(List<AVObject> players, AVException e) { if (players != null) { // Get list of players } else { // No players } } });
在清单 15 中,我使用了一个查询约束来检索一个或多个匹配的 AVObject;在本例中,希望找到所有活跃的 Players:
AVQuery query = new AVQuery("Players"); query.whereEqualTo("status", "active"); query.findInBackground(new FindCallback() { @Override public void done(List<AVObject> players, AVException e) { if (players != null) { // Success - players contain active players } else { // Failed } } });
AVQuery 还提供了一个方法来获得匹配对象的计数,不需要检索对象本身,这非常有用。清单 16 展示了如何获得活跃玩家的计数:
AVQuery query = new AVQuery("Players"); query.whereEqualTo("status", "active"); //remove this line to count ALL query.countInBackground(new CountCallback() { @Override public void done(int count, AVException e) { if (e == null) { // Success, see count variable } else { // Failed } } });
AVQuery 支持 20 多个不同的查询约束方法,以下是一些示例:
whereMatches(String key, String regex) 查找与所提供的正则表达式相匹配的字符串值。
whereStartsWith(String key, String prefix) 查找使用所提供的字符串开头的字符串值。
whereContains(String key, String substring) 查找包含所提供的字符串的值。
whereGreaterThan(String key, Object value) 查找大于所提供的值的值。
whereWithinKilometers(String key, AVGeoPoint point, double maxDistance) 查找点值在给定点附近,并且在给定最大距离内的对象。
查询结果可以被排序,如清单 17 所示。为了对查询结果进行排序,可以调用其中一个 query orderBy...() 方法,指定作为排序依据的字段。
query.orderByAscending("name"); query.orderByDescending("name"); // For example: AVQuery query = new AVQuery("Players"); query.whereEqualTo("status", "active"); query.orderByAscending("lastName"); // By lastname ascending order query.findInBackground(new FindCallback() { @Override public void done(List<AVObject> players, AVException e) { if (players != null) { // Success - players contain active players } else { // Failed } } });
另外,需要注意的是,AVQuery 结果被缓存。您可以根据应用程序的需求,采用不同的方式设置查询缓存策略。目前支持以下缓存策略:
IGNORE_CACHE:不使用缓存,这是默认的缓存策略。
CACHE_ONLY:只从缓存加载。如果没有已缓存的结果,将会有一个 AVException。
NETWORK_ONLY:始终从网络加载,但将结果保存到缓存。
CACHE_ELSE_NETWORK:首先查找缓存。如果失败,则从网络加载。如果缓存与网络均不成功,结果将是 AVException。
NETWORK_ELSE_CACHE:首先查找网络。如果失败,则从缓存加载。如果缓存与网络均不成功,结果将是 AVException。
CACHE_THEN_NETWORK:首先查找缓存。如果失败,则从网络加载。请注意,FindCallback 实际上被调用了两次:第一次包括缓存的结果,然后包括网络结果。该策略只能与 findInBackground 异步使用。
设置缓存策略很简单。设置之前,首先要调用 find...() 方法,如清单 18 所示:
AVQuery query = new AVQuery("Players"); query.setCachePolicy(AVQuery.CachePolicy.NETWORK_ELSE_CACHE); query.findInBackground(new FindCallback() { @Override public void done(List<AVObject> players, AVException e) { if (e == null) { // Success - players contain active players } else { // Failed } } });
AVQuery 类提供了查询存储在云端的数据对象所需的所有方法。有了 AVQuery,就可以指定各种查询约束,对匹配的数据对象进行计数,设置限制,跳过数据对象,排序,清除缓存等。
从 AVOS Cloud 云端删除数据对象也很简单。如果您已经有了对象实例,那么您就可以通过调用 AVObject 的 delete() 或 deleteInBackground()方法,从云中删除现有的 AVObject。这两个方法如清单 19 所示:
pObject.delete(); pObject.deleteInBackground();
如您所料,delete() 是一个阻塞调用,这意味着您需要负责适当地在自己的工作线程上调用它。或者可以使用带有或不带有回调的deleteInBackground() 方法来完成这一操作。
至此,我们一直在使用 AVObject 对象和 AVQuery 查询。我还向您介绍了 AVUser(用户)、ACL 和角色。最后,我将示范如何在 AVOS Cloud 中使用文件的读、写和保存函数。
回想一下,您可以在 AVObject 中存储原始 byte[] 数据,这对于小规模数剧来说是没问题的。但在存储较大的项目(如图像或文档)时,应该使用 AVFiles。
AVOS Cloud 云端的文件使用 AVFile 来表示,它提供获得文件名、URL、文件数据(假设数据可用)的方法。您也可以下载和上传文件,并访问其他一些辅助方法。
文件数据以 byte[] 语法表示。请注意,目前,给定的文件不得超过 10MB。在命名文件时,需要避免潜在的名称冲突,提供文件扩展名可以帮助 AVOS Cloud 处理文件的内容。
清单 20 演示了保存一个 JPG 文件:
// Save image file Drawable drawable = ...; Bitmap bitmap = (Bitmap)((BitmapDrawable) drawable).getBitmap(); ByteArrayOutputStream stream = new ByteArrayOutputStream(); bitmap.compress(Bitmap.CompressFormat.JPEG, 100, stream); byte[] data = stream.toByteArray(); AVFile imageFile = new AVFile("image.jpg", data); imageFile.saveInBackground();
上述代码中最初的语句将位图转换成一个byte[]。然后,使用 AVFile.saveInBackground() 方法保存 byte[],这与将 AVObject 保存在服务器上的方法类似。
一旦将文件保存到 AVOS Cloud 上,必须将它与 AVOject 关联(放入 AVOject)。换句话说,AVFile 文件不是真正的独立对象,并且为了以后检索和使用,必须将它关联到给定的 AVObject 实例。这种局限可能会在未来版本中进行处理。清单 21 将图像文件关联到一个 Player 对象:
// Associate image with AVOS Cloud object AVObject po = new AVObject("Players"); po.put("name", "eortiz"); po.put("photo", imageFile); po.saveInBackground();
我已将文件关联到数据对象,然后使用 saveInBackgroud() 方法将对象保存在服务器上,我在前面已讨论过这一点。
清单 22 显示了如何检索与数据对象有关联的文件:
// Retrieving the file AVFile imageFile2 = (AVFile)po.get("photo"); imageFile2.getDataInBackground(new GetDataCallback() { public void done(byte[] data, AVException e) { if (data != null) { // Success; data has the file } else { // Failed } } });
从 AVObject 对象收到 AVFile 引用之后,我调用 getDataInBackground() 从服务器中检索 AVFile。请注意,我使用了回调GetDataCallback 来检索 AVOS Cloud 文件,并没有使用 GetCallback,后者用于通过 AVQuery 检索 AVObject 对象。
AVOS Cloud API 非常全面,包括访问移动服务的类,比如实时消息,推送通知,使用地理数据,移动统计等等。在本文中,我通过介绍 AVOS Cloud API 实现数据和文件的云存储,简单介绍了如何使用 AVOS Cloud。我们还了解了如何在 AVOS Cloud 云端存储和操纵用户、数据对象、文件和 ACL,这是进一步探索云平台进行移动开发的良好基础。