面向 Android 应用程序的基于 Parse 云的服务

对 Android 应用程序中的用户、数据对象和文件执行云存储和查询

本文将探索在私有云中存储移动应用程序数据的优势,并将移动应用程序数据引入 Parse SDK for Android。移动专家 C. Enrique Ortiz 介绍了 Parse API 类,它适用于移动应用程序的云存储用户、云操作用户、数据对象和文件。

C. Enrique Ortiz, 移动技术专家

C. Enrique Ortiz 是一名经验丰富的移动技术专家、作家和博主。他在智能手机变得智能之前就创建了端到端的移动软件。他已撰写了多篇技术文章和两本关于软件开发的书籍,而且他还帮助了数十家公司解决他们的移动计算需求。



2012 年 12 月 18 日

Parse 移动 SDK 为 iOS、Android 和 Windows® 应用程序提供了基于云的 API 和服务。Parse SDK 还提供了 JavaScript 和 REST API。使用 Parse API,您可以极快地以最少工作量让您的移动应用程序支持云处理。集成了 Parse API 的移动应用程序可以轻松地在 Parse 云上存储数据对象和文件,发送并侦听推送通知,管理用户,处理地理位置数据,并使用 Twitter 和 Facebook 等社交媒体平台。对于需要扩展的移动应用程序,Parse SDK 可以提供所有灵活的云平台。

开始之前

出于本文的目的,我假设您已经非常熟悉使用 JSON、Android 和 Eclipse 进行移动应用编程的基本概念。在您继续阅读本文之前,请访问 Parse.com 并定义您的应用程序。只需遵循注册页面中的简单指令即可。

本文介绍了面向 Parse 用户、数据对象和文件的核心 Parse API 类。您将学习如何使用访问控制列表 (ACL),以及如何在数据对象上执行 CRUD 操作,还有如何在 Parse 云中存储和检索文件。示例均构建于 Parse SDK for Android 之上(请参阅 参考资料)。

Parse 仪表板

Parse 仪表板可以帮助开发人员管理应用程序。该仪表板为 API、文件和推送的通知提供了一般指标和应用程序特定的使用指标。通过仪表板可管理应用程序的键和设置。该仪表板还提供了数据浏览器,使开发人员能够浏览(甚至编辑)所存储的 Parse 对象。数据浏览器对于调试非常有用。图 1 是 Parse 仪表板的屏幕截图:

图 1. Parse 仪表板
Parse 仪表板的屏幕截图。

通过一个应用程序 ID 和客户端 ID 对应用程序进行身份验证。为了获得您的应用程序和客户端 ID,必须通过 Parse 仪表板注册您的应用程序。在您的应用程序上初始化 Parse 库时,会用到这些键。

Parse 数据对象

在 Parse 中,使用 “名称-值” 对的容器 ParseObject 表示数据。ParseObject 可以存储任何与 JSON 兼容的数据,如清单 1 所示:

清单 1. ParseObject 示例
ParseObject myParseObject = new ParseObject("MyObject"); // Class Name
myParseObject.put("name", "C. Enrique Ortiz");
myParseObject.put("twitterHandle", "eortiz");
myParseObject.put("followers", 123456);

ParseObject 在实例化时被赋予一个 classname(类名称)。在 清单 1 中,类名称是 "MyObject"。类名称与关系数据库中的表名称相似,同一类的 Parse 对象相当于表中的行。

ParseObject 暴露的方法类似于 Java Map 类中的方法,如 putgetremove,以及大量特定于 ParseObject 的其他方法。

ParseObject名称键 必须是字母数字的,作为一个指导方针,请对名称键使用驼峰式大小写 (camel-casing) 格式。值可以是存储在 JSON 中的任何数据类型,也就是说,可以是数字、字符串、布尔值、数组、JSONObject.NULLJSONObjectJSONArrayParseObject 所支持的其他数据类型是 Java Datebyte[] 数组。ParseObject 还可以包含其他 ParseObject

清单 2 显示了部分受支持的 ParseObject 值数据类型:

清单 2. ParseObject: 部分支持的值数据类型
// 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 ParseObject
ParseObject pObject = new ParseObject("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);

清单 2 中的代码创建了一个 ParseObject,它在 Parse 云中被存储为一个对象。然后,许多同一类的 MyObject 被存储为 ParseObject 数据对象的行,可以保存、查询和更新,并能从 Parse 的云存储中删除。甚至可以在应用程序离线时保存数据,Parse 库将数据保存在本地,直到重新建立网络连接。

修改 ParseObject

如果您熟悉移动应用程序开发,那么您就会知道,网络操作等长时间操作一般都是在后台完成,或在一个工作线程上完成,而不是在主系统的 UI 线程上完成。这样可以避免主系统线程发生阻塞并影响用户界面的响应速度。稍后,在本文的后半部分,我会告诉您 Parse 如何为后台工作提供帮助,从而保存、删除和查找对象。现在,考虑下面的同步 remove() 方法,可以使用它从 Parse 对象删除一个键:

pObject.remove("myNumber"); // remove the field/key "myNumber" from pObject

在删除或添加字段后,或在更新当前字段后,您可以在云上保存(或更新)数据对象,只需调用其中一个 ParseObjectsave...() 方法,我稍后会在本文中讨论它们。

摘要:ParseObject

ParseObject 代表在 Parse 云上的一个数据对象。它提供各种方法,可以添加 “名称-值” 对,测试给定的键是否存在,从服务器删除或提取给定的 ParseObjectParseObject 也让您可以使用多个 get...()put...() 方法,操纵 ParseObject 数据,合并 ParseObject,在服务器中保存 ParseObject,等等。

Parse 用户、角色和 ACL

在我告诉您如何在 Parse 对象上执行 CRUD 操作之前,您应该了解一下 Parse 用户、角色和 ACL(访问控制列表)。这三个都是非常重要的概念,有助于保护您的应用程序的数据对象。

Parse 用户

名称为 ParseUser 的类代表一个用户,并为 Parse 应用程序提供用户帐户功能。每个 Parse 应用程序都有与之关联的 Parse 用户。一个 ParseUser 是一个 ParseObject,但具有更多的属性,如用户名、密码和电子邮件。您可以添加任何您认为合适的其他数据值。

Parse 中的匿名用户

在 Parse 中,匿名用户是没有用户名或密码的用户。匿名用户对于那些不要求用户身份验证的移动应用程序功能非常有用。匿名用户可以创建数据对象,但这些对象寿命很短,并且在匿名用户登出之后就不再可用。

用户可以注册为您应用的 Parse 用户,如清单 3 所示:

清单 3. ParseUser — 注册
ParseUser user = new ParseUser();
user.setUsername("eortiz");
user.setPassword("123456");
user.setEmail("eortiz@nospam.com");
user.put("userType", "Author"); // add another field

// Call the asynchronous background method to sign up 
user.signUpInBackground(new SignUpCallback() {
  public void done(ParseException e) {
    if (e == null) {
      // Successful. Allow access to app.
    } else {
      // Failed....
    }
  }
});

usernameemail 必须是惟一的。如果 username 或 email 已经被使用,那么注册调用将会失败。您应该有一个通知用户字段限制的机制,并提供一个重试的进程。

在注册之后,用户就可以登录您的应用,如清单 4 所示:

清单 4. ParseUser — 登录
ParseUser.logInInBackground("eortiz", "123456", new LogInCallback() {
  public void done(ParseUser user, ParseException e) {
    if (user != null) {
      // Successful. Allow access to app.
    } else {
      // Failed
    }
  }
});

您可以通过调用 ParseUser.save() 更新用户信息。 但是请注意,只有 ParseUser 的所有者可以修改其内容,数据对于其他任何人都是只读的。

Parse 会缓存当前登录的用户。您可以通过调用 ParseUser.currentUser() 查询当前用户。currentUser 方法使您能够快速访问当前用户信息,那么如果当前用户会话没有激活,您只需要根据提示输入验证。清单 5 显示了如何在 Parse 中检索当前用户:

清单 5. ParseUser — 获得当前用户
ParseUser currentUser = ParseUser.getCurrentUser();
if (currentUser != null) {
  // current user is valid
} else {
  // current user not valid, ask for credentials
}

重置当前用户

在 Parse 中,您可以通过调用 ParseUser.logOut() 来重置当前用户,如清单 6 所示:

清单 6. ParseUser — 重置当前用户(登出)
ParseUser.logOut(); // static method

Parse ACL

ACL 是关联到数据对象的访问权限(或控制)列表。ParseACL 类允许您为给定的 ParseObject 定义权限。使用 ACL,您可以定义对您的应用程序数据对象的公共访问,并且可以(通过角色)限制对特定用户或用户组的访问。清单 7 演示了 Parse ACL 的用法:

清单 7. 使用 ParseACL 实现访问权限控制
// Define a Parse user
ParseUser user = new ParseUser();
user.setUsername(username);
:
:

// Define a read/write ACL
ParseACL rwACL = new ParseACL();
rwACL.setReadAccess(user, true); // allow user to do reads
rwACL.setWriteAccess(user, true); // allow user to do writes
:
:

// Define a Parse object and its ACL
ParseObject gameObject = new ParseObject("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
ParseACL publicACL = new ParseACL();
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 定义。

清单 8. 设置默认的 ACL
// Set a default ACL for all newly created objects as public access
ParseACL.setDefaultACL(publicACL, true);

虽然在这里没有演示,但我们也可以使用 ParseRole 类为用户组授予访问权限。

接下来,我们将查看如何将 Parse 数据对象保存到 Parse 云,并在 Parse 云中检索它们。

在云上的 Parse 数据对象

一旦创建并填充了 ParseObject,就可以在 Parse 云上保存。在 Parse 云上保存数据对象实际上是利用 Parse 的最简单操作之一,其复杂性通常与数据表示、编组、网络通信和传输等有关联,Parse 完全隐藏了此复杂性。您需要使用 helper 方法来将数据对象实例映射到 ParseObject,并映射回来,您需要决定是在您自己的线程上调度 Parse 保存操作,还是使用 save-in-the-background 方法。

谨防系统线程阻塞!

回想一下,在移动应用程序中,长时间的操作(如网络、文件或长的计算)不应该在主系统线程上完成。相反,应在一个单独的工作线程中执行它们。阻塞系统线程会对应用程序的用户界面的响应能力产生负面影响,有可能导致强行关闭您的应用程序。

ParseObject 提供两个保存方法: save()saveInBackground()saveInBackground() 是建议的保存方法,因为它在自己的工作线程上运行保存操作。如果您选择使用同步的 save() 方法,需要注意的是,您需要在该方法自己的工作线程上调用该方法,以避免 UI 被阻塞。

清单 9 显示的代码在后台保存 Parse 数据对象:

清单 9. 在后台保存 ParseObject
// ParseObject
ParseObject pObject = new ParseObject("ExampleObject");
pObject.put("myNumber", number);
pObject.put("myString", name);
pObject.saveInBackground(); // asynchronous, no callback

清单 10 显示的代码使用了一个回调,在后台保存 Parse 数据对象:

清单 10. 使用回调,在后台保存
pObject.saveInBackground(new SaveCallback () {
    @Override
    public void done(ParseException ex) {
        if (ex == null) {
            isSaved = true;
        } else {
            // Failed
            isSaved = false;
        }
    }
  });

save...() 方法的变形包括以下几种:

  • 使用或不使用回调,saveAllinBackground() 保存 ParseObject
  • saveAll(List<ParseObject> objects) 保存 ParseObjects 的列表。
  • saveAllinBackground(List<ParseObject> objects) 在后台保存 ParseObjects 的列表。
  • saveEventually() 让您能够在未来某个时点将数据对象保存到服务器;如果 Parse 云目前不可访问,则使用该方法。

一旦在云上已成功保存了 ParseObject ,就会为对象提供惟一的 Object-ID。此 Object-ID 非常重要,因为它惟一地标识了此 ParseObject 实例。例如,您可以使用 Object-ID 确定是否已在云上成功保存该对象,以便检索或刷新给定的 Parse 对象实例和删除特定的 ParseObject

从云中检索数据对象

这一节将探讨查询和检索 Parse 云上的数据对象的方法。您可以通过 object-ID 查询一个 ParseObject,您也可以使用属性查询一个或多个 Parse 对象。如果您已经有一个 ParseObject,那么您可以通过从服务器提取最新的值,刷新或同步其内容。我们将在以下代码片段中探讨所有这些选项。

提取 ParseObjects

为了从 Parse 云提取数据对象,使用 ParseObject 方法 fetch()fetchInBackground(),如清单 11 所示:

清单 11. 提取(无条件)
// ParseObject
ParseObject pObject = new ParseObject("ExampleObject");
:
:

// Fetch the parse object unconditionally
try {
    pObject.fetch();
} catch (ParseException e) {
    e.printStackTrace();
}

// Fetch the parse object unconditionally, with Callback
pObject.fetchInBackground(new GetCallback() {
    @Override
    public void done(ParseObject obj, ParseException ex) {
        if (ex == null) {
            // Success
        } else {
            // Failed
        }            
    }
});

您也可以仅在需要时提取数据,例如,提取一个相关 Parse 对象的 Parse 对象,如清单 12:

清单 12. 根据需要进行提取
ParseObject po = new ParseObject("ExampleObject");
:

ParseObject po2 = po.getParseObject("key");

// Fetch only if needed
try {
    po2.fetchIfNeeded();
} catch (ParseException e) {
    e.printStackTrace();
}

另一个选项是在后台根据需要执行提取操作,并使用一个回调。为此,可以使用 Parse 对象的 fetchIfNeededInBackground(GetCallback callback) 方法。

在某些情况下,您需要无条件地或仅在需要时一次提取一个 Parse 对象集合。ParseObject 为此提供了一组静态方法,每个方法都将一个 Parse 对象的列表作为输入,然后返回一个 Parse 对象的列表:

  • fetchAll(List<ParseObject> objects)
  • fetchAllIfNeeded(List<ParseObject> objects)
  • fetchAllIfNeededInBackground(List<ParseObject> objects, FindCallback callback)
  • fetchAllInBackground(List<ParseObject> objects, FindCallback callback)

在云上查询数据对象

希望您现在已看到 Parse API 的强大,但是别着急,它还有更多功能!除了提取数据对象之外,Parse 还可以让您使用 object-ID 或属性来查询数据对象。要从 Parse 云查询一个数据对象,可以使用 ParseQuery。您可以使用 ParseQuery 进行基本的和复杂的数据查询,接收给定的对象或匹配对象的 List

清单 13 显示了如何在一个后台线程中使用给定 object-ID 从服务器检索特定的 Parse 对象:

清单 13. 使用 ParseQuery 检索给定的 ParseObject
String myID = "12345";
ParseQuery query = new ParseQuery("Players");
query.getInBackground(myID, new GetCallback() {
    @Override
    public void done(ParseObject object, ParseException e) {
        if (object != null) {
            // Get object
        } else {
            // Not found
        }
    }            
});

请注意,query.getInBackground() 没有使用 ParseQuery 缓存。

清单 14 显示的查询检索 Players 类的所有数据对象。(没有提供约束。)

清单 14. 对给定类的所有 ParseObjects 使用 ParseQuery
ParseQuery query = new ParseQuery("Players");
query.findInBackground(new FindCallback() {
    @Override
    public void done(List<ParseObject> players, ParseException e) {
        if (players != null) {
            // Get list of players
        } else {
            // No players
        }
    }
});

在清单 15 中,我使用了一个查询约束来检索一个或多个匹配的 Parse 对象;在本例中,使用了一个活动的 Players

清单 15. 使用 ParseQuery 检索匹配的 ParseObjects
ParseQuery query = new ParseQuery("Players");
query.whereEqualTo("status", "active");
query.findInBackground(new FindCallback() {
    @Override
    public void done(List<ParseObject> players, ParseException e) {
        if (players != null) {
            // Success - players contain active players 
        } else {
            // Failed
        }
    }
});

ParseQuery 还提供了一个方法来获得匹配对象的计数,不需要检索对象本身,这非常有用。清单 16 展示了如何获得活动玩家的计数:

清单 16. 使用 ParseQuery 对匹配的 ParseObjects 进行计数
ParseQuery query = new ParseQuery("Players");
query.whereEqualTo("status", "active"); //remove this line to count ALL
query.countInBackground(new CountCallback() {
    @Override
    public void done(int count, ParseException e) {
        if (e == null) {
            // Success, see count variable
        } else {
            // Failed
        }
    }
});

ParseQuery 方法和约束

ParseQuery 支持 20 多个不同的查询约束方法,以下是一些示例:

  • whereMatches(String key, String regex) 查找与所提供的正则表达式相匹配的字符串值。
  • whereStartsWith(String key, String prefix) 查找使用所提供的字符串开头的字符串值。
  • whereContains(String key, String substring) 查找包含所提供的字符串的值。
  • whereGreaterThan(String key, Object value) 查找大于所提供的值的值。
  • whereWithinKilometers(String key, ParseGeoPoint point, double maxDistance) 查找点值在给定点附近,并且在给定最大距离内的对象。

查询结果可以被排序,如清单 17 所示。为了对查询结果进行排序,可以调用其中一个 query orderBy...() 方法,指定作为排序依据的字段。

清单 17. 排序查询结果
query.orderByAscending("name"); 

query.orderByDescending("name");



For example:



ParseQuery query = new ParseQuery("Players");
query.whereEqualTo("status", "active");
query.orderByAscending("lastName"); // By lastname ascending order
query.findInBackground(new FindCallback() {
    @Override

  public void done(List<ParseObject> players, ParseException e) {

    if (players != null) {
      // Success - players contain active players

    } else {

      // Failed
		
                          } 

             }	

});

另外,需要注意的是,ParseQuery 结果被缓存。您可以根据应用程序的需求,采用不同的方式设置查询缓存策略。目前支持以下缓存策略:

  • IGNORE_CACHE:不使用缓存,这是默认的缓存策略。
  • CACHE_ONLY:只从缓存加载。如果没有已缓存的结果,将会有一个 ParseException
  • NETWORK_ONLY:始终从网络加载,但将结果保存到缓存。
  • CACHE_ELSE_NETWORK:首先查找缓存。如果失败,则从网络加载。如果缓存与网络均不成功,结果将是 ParseException
  • NETWORK_ELSE_CACHE:首先查找网络。如果失败,则从缓存加载。如果缓存与网络均不成功,结果将是 ParseException
  • CACHE_THEN_NETWORK:首先查找缓存。如果失败,则从网络加载。请注意,FindCallback 实际上被调用了两次:第一次包括缓存的结果,然后包括网络结果。该策略只能与 findInBackground 异步使用。

设置缓存策略很简单。设置之前,首先要调用 find...() 方法,如清单 18 所示:

清单 18. 管理 CachePolicy
ParseQuery query = new ParseQuery("Players");
query.setCachePolicy(ParseQuery.CachePolicy.NETWORK_ELSE_CACHE);
query.findInBackground(new FindCallback() {
    @Override
    public void done(List<ParseObject> players, ParseException e) {
        if (e == null) {
            // Success - players contain active players 
        } else {
            // Failed
        }
    }
});

ParseQuery 类提供了查询存储在云中的数据对象所需的所有方法。有了 ParseQuery,就可以指定各种查询约束,对匹配的数据对象进行计数,设置限制,跳过数据对象,排序,清除缓存等。


删除数据对象

从 Parse 云删除数据对象也很简单。如果您已经有了对象实例,那么您就可以通过调用ParseObjectdelete()deleteInBackground() 方法,从云中删除现有的 ParseObject。这两个方法如清单 19 所示:

清单 19. 从云中删除 Parse 对象
parseObject.delete();
parseObject.deleteInBackground();

如您所料,delete() 是一个阻塞调用,这意味着您需要负责适当地在自己的工作线程上调用它。或者可以让 Parse 使用带有或不带有回调的 deleteInBackground() 方法来负责线程。

使用文件

至此,我们一直在使用 Parse 对象和 Parse 查询。我还向您介绍了 Parse 用户、ACL 和角色。最后,我将示范如何在 Parse 中使用文件的读、写和保存函数。

回想一下,您可以在 ParseObject 中存储原始 byte[] 数据,这对于小规模数剧来说是没问题的。但在存储较大的项目(如图像或文档)时,应改使用 Parse Files。

Parse 云中的文件使用 ParseFile 来表示,它提供获得文件名、URL、文件数据(假设数据可用)的方法。您也可以下载和上传文件,并访问其他一些辅助方法。

文件数据以 byte[] 语法表示。请注意,目前,给定的文件不得超过 10MB。在命名文件时,Parse 库需要避免潜在的名称冲突,提供文件扩展名可以帮助 Parse 处理文件的内容。

清单 20 演示了保存一个 JPG 文件:

清单 20. 保存一个 ParseFile
// 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();                
ParseFile imageFile = new ParseFile("image.jpg", data);
imageFile.saveInBackground();

清单 20 中最初的语句将位图转换成一个byte[]。然后,使用 ParseFile saveInBackground() 方法保存 byte[],这与将 ParseObject 保存在服务器上的方法类似。

一旦将文件保存到 Parse 上,必须将它与 ParseOject 关联(放入 ParseOject)。换句话说,Parse 文件不是真正的独立对象,并且为了以后检索和使用,必须将它关联到给定的 ParseObject 实例。这种局限可能会在未来版本的 Parse 中进行处理。清单 21 将图像文件关联到一个 Player Parse 对象:

清单 21. 关联 ParseFile 与 ParseObject
// Associate image with Parse object
ParseObject po = new ParseObject("Players");
po.put("name", "eortiz");
po.put("photo", imageFile);
po.saveInBackground();

我已将文件关联到数据对象,然后使用 saveInBackgroud() 方法将对象保存在服务器上,我在前面已讨论过这一点。

清单 22 显示了如何检索与数据对象有关联的文件:

清单 22. 检索 ParseFile
// Retrieving the file 
ParseFile imageFile2 = (ParseFile)po.get("photo");
imageFile2.getDataInBackground(new GetDataCallback() {
  public void done(byte[] data, ParseException e) {
    if (data != null) {
      // Success; data has the file
    } else {
      // Failed
    }
  }
});

从 Parse 对象收到 ParseFile 引用之后,我调用 getDataInBackground() 从服务器中检索 ParseFile。请注意,我使用了回调 GetDataCallback 来检索 Parse 文件,并没有使用 GetCallback,后者用于通过 ParseQuery 检索 Parse 对象。

结束语

Parse API 非常全面,包括访问移动服务的类,比如推送通知,使用地理数据,集成社交媒体平台等。在本文中,我通过介绍 Parse API 实现数据和文件的云存储,简单介绍了如何使用 Parse。我们还了解了如何在 Parse 云中存储和操纵 Parse 用户、数据对象、文件和 ACL,这是进一步探索云平台进行移动开发的良好基础。

致谢

非常感谢 Athen O'Shea 对本文的审查。

参考资料

学习

  • 了解有关 Parse Android SDK 的更多信息;并参阅 Parse Quick Guide 选择移动平台,设置一个应用,并下载和安装您的 Parse SDK。
  • 查看 Parse Android API 的完整列表。
  • "Develop Android applications with Eclipse(Frank Ableson,developerWorks,2008 年 2 月):获得采用 Android Eclipse 插件在 Eclipse 开发环境中开发 Android 应用程序的更多实践。
  • "Introduction to jQuery Mobile"(C. Enrique Ortiz,developerWorks,2012 年 5 月):了解 jQuery Mobile 的基础知识,以及如何编写函数式移动 Web 应用程序用户界面。可用的示例引导您在 jQuery Mobile 中完成页面、导航、工具栏、列表视图、表单控件和过渡效果。
  • "解决多设备对多平台的移动应用程序集成挑战"(Olivier Picciotto,developerWorks,2012 年 8 月):现在,移动开发和云计算实际上是分不开的,但将移动应用程序集成到云中,这仍然是新的领域。了解移动企业应用程序平台 (MEAP) 如何解决一些从移动到云的集成挑战。
  • "用于移动开发的 DevOps" (Michael Rowe,developerWorks,2012 年 7 月):世界各地的公司都希望通过为客户和用户提供使移动计算更简单的应用程序而开发移动市场。本文考虑的是在工作场所的移动平台上集成开发和运营所涉及的技术和业务问题。
  • 关注 Twitter 上的 developerWorks
  • 观看 developerWorks 点播演示,那里提供了面向初学者的产品安装和设置演示,以及面向经验丰富的开发人员的高级功能演示。

讨论

  • 加入 developerWorks 社区。探索由开发人员推动的博客、论坛、组和维基,并与其他 developerWorks 用户进行交流。

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


在您首次登录 developerWorks 时,会为您创建一份个人概要。您的个人概要中的信息(您的姓名、国家/地区,以及公司名称)是公开显示的,而且会随着您发布的任何内容一起显示,除非您选择隐藏您的公司名称。您可以随时更新您的 IBM 帐户。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Java technology, 移动开发, Cloud computing
ArticleID=851636
ArticleTitle=面向 Android 应用程序的基于 Parse 云的服务
publish-date=12182012