(二)进阶练习____3、云同步——与App Engine同步
负责人:sfshine
原文链接:http://docs.eoeandroid.com/training/cloudsync/aesync.html
目录[隐藏] |
Syncing with App Engine-和App引擎同步
Writing an app that syncs to the cloud can be a challenge. There are many little details to get right, like server-side auth, client-side auth, a shared data model, and an API. One way to make this much easier is to use the Google Plugin for Eclipse, which handles a lot of the plumbing for you when building Android and App Engine applications that talk to each other. This lesson walks you through building such a project.
写一个和云端同步的应用是很有挑战性的.有许多小细节需要做好,比如服务器端授权,客户端授权,数据的共享形式以及API.一种简单的方式是使用Google的Eclipse插件,它提供了很多在创建应用和App引擎程序需要对话时候用的管道.这节叫你怎么建立这样的应用.
Following this lesson shows you how to:
这节教你:
- Build Android and Appengine apps that can communicate with each other
- 创建一个可以彼此交流的Android应用和App引擎
- Take advantage of Cloud to Device Messaging (C2DM) so your Android app doesn't have to poll for updates
- 利用云推送(C2DM)你的Android应用不必持续请求数据.
This lesson focuses on local development, and does not cover distribution (i.e, pushing your App Engine app live, or publishing your Android App to market), as those topics are covered extensively elsewhere.
这节主要讲本地编程,没有涉及分配问题(比如:是你的App引擎可以使用,或者把你的Android应用发布到Market),因为这些信息在别的章节详细的讲过了.
Prepare Your Environment-搭建你的开发环境
If you want to follow along with the code example in this lesson, you must do the following to prepare your development environment:
如果你想按照课程的案例代码来学习,那你必须先搭建开发环境
- Install the Google Plugin for Eclipse.
- 安装google的eclipse插件
- Install the GWT SDK and the Java App Engine SDK. The Quick Start Guide shows you how to install these components.
- 安装GWT SDK和 java 应用引擎SDK(JDK).快速向导想你展示了怎么安装这些组件.
- Sign up for C2DM access. We strongly recommend creating a new Google account specifically for connecting to C2DM. The server component in this lesson uses this role account repeatedly to authenticate with Google servers.
- 注册C2DM帐户.我们强烈建议你创建一个专门用来连接C2DM的新google帐户.本文中的服务器组件将不断使用这个帐户来和Google服务器进行认证操作.
Create Your Projects-创建你的应用
After installing the Google Plugin for Eclipse, notice that a new kind of Android project exists when you create a new Eclipse project: The App Engine Connected Android Project (under the Google project category). A wizard guides you through creating this project, during the course of which you are prompted to enter the account credentials for the role account you created.
在安装了Google eclipse插件之后,创建eclipse项目的时候会多出一种Android项目选项, App Engine Connected Android Project(在Google项目目录下).选择后会出现一个向导引导你创建这个项目, 在此过程中,系统会提示你输入你所创建帐户的信息作为您拥有这个帐户的凭证。
Note: Remember to enter the credentials for your role account (the one you created to access C2DM services), not an account you'd log into as a user, or as an admin.
注意:记着输入你帐号的凭证信息(你为了使用C2DM而创建的那个帐号),而不是你作为用户或者管理员登陆时候的帐号信息.
Once you're done, you'll see two projects waiting for you in your workspace—An Android application and an App Engine application. Hooray! These two applications are already fully functional— the wizard has created a sample application which lets you authenticate to the App Engine application from your Android device using AccountManager (no need to type in your credentials), and an App Engine app that can send messages to any logged-in device using C2DM. In order to spin up your application and take it for a test drive, do the following:
完成之后你将在workspace中看到两个项目,一个是Android应用一个是AppEngine应用.不错!这两个应用已经拥有了完整的功能---向导已经创建了一个简单的应用来让你使用AccountManager(不用输入验证信息)通过Android设备授权AppEngine,这样AppEngine就可以发送消息给任何使用C2DM登陆的设备.
To spin up the Android application, make sure you have an AVD with a platform version of at least Android 2.2 (API Level 8). Right click on the Android project in Eclipse, and go to Debug As > Local App Engine Connected Android Application. This launches the emulator in such a way that it can test C2DM functionality (which typically works through Google Play). It'll also launch a local instance of App Engine containing your awesome application.
为了确保可以正常发挥功能,请确保你的虚拟机平台至少是Android 2.2.在Eclipse的Android工程上右击,然后Debug As > Local App Engine Connected Android Application.这样运行虚拟机可以测试C2EM的功能(主要通过GooglePlay工作).他也将启动AppEngine的一个含有你的应用的本地实例.
Create the Data Layer-创建数据层
At this point you have a fully functional sample application running. Now it's time to start changing the code to create your own application.
现在你有了一个完整功能的简单应用.是时候调整代码来创建你自己的应用了.
First, create the data model that defines the data shared between the App Engine and Android applications. To start, open up the source folder of your App Engine project, and navigate down to the (yourApp)-AppEngine > src > (yourapp) > server package. Create a new class in there containing some data you want to store server-side. The code ends up looking something like this:
首先,创建一个数据模型,这个数据模型定义了如何在AppEngine和Android设备直接共享数据.首先,打开你AppEngine的工程,然后(yourApp)-AppEngine > src > (yourapp) > server 找到这个包.创建一个含有你想在服务器端保存的数据的类.比如下面的代码:
package com.cloudtasks.server; import javax.persistence.*; @Entity public class Task { private String emailAddress; private String name; private String userId; private String note; @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; public Task() { } public String getEmailAddress() { return this.emailAddress; } public Long getId() { return this.id; } ... }
Note the use of annotations: Entity, Id and GeneratedValue are all part of the Java Persistence API. Essentially, the Entity annotation goes above the class declaration, and indicates that this class represents an entity in your data layer. The Id and GeneratedValue annotations, respectively, indicate the field used as a lookup key for this class, and how that id is generated (in this case, GenerationType.IDENTITY indicates that the is generated by the database). You can find more on this topic in the App Engine documentation, on the pageUsing JPA with App Engine.
注意注释的使用:实体,Id和产生值的方法都是Java 持久化接口(JPA) 的一部分.实体的注释要在类的声明前面写上来表明这个类代表你数据层的一个实体.Id和产生值方法分别用来表示一个你的类的主键和这个id是怎么生成的(在本例中, GenerationType.IDENTITY 表示id是从数据库生成的)你可以在AppEngine文档的<在AppEngine中使用JPA>一页中获取更多资料.
Once you've written all the classes that represent entities in your data layer, you need a way for the Android and App Engine applications to communicate about this data. This communication is enabled by creating a Remote Procedure Call (RPC) service. Typically, this involves a lot of monotonous code. Fortunately, there's an easy way! Right click on the server project in your App Engine source folder, and in the context menu, navigate to New > Other and then, in the resulting screen, select Google > RPC Service. A wizard appears, pre-populated with all the Entities you created in the previous step, which it found by seeking out the @Entity annotation in the source files you added. Pretty neat, right? Click Finish, and the wizard creates a Service class with stub methods for the Create, Retrieve, Update and Delete (CRUD) operations of all your entities.
一旦你在你的数据层里写了所有的实体类,你需要一个在Android和AppEngine之间进行这些数据的交流的途径.这个交流通过RPC(远程调用)服务来实现.通常,这会用到很多单调的代码.幸运的是,有一种简单的方法:在服务端AppEngine源码工程上面右击,在菜单中选择New>Other,然后在弹出的对话窗口选择Google>Rpc Service.一个向导就会出现,里面有你在上一步里创建的所有类,它是通过查找源代码里你添加的 @Entity 注释来获取的.是不是快搞定了亲?点击Finish,向导会创建一个含有对你的所有实体进行创建,查询,更新和删除(增删改查)操作方法的服务.
Create the Persistence Layer-创建持久层
The persistence layer is where your application data is stored long-term, so any information you want to keep for your users needs to go here. You have several options for writing your persistence layer, depending on what kind of data you want to store. A few of the options hosted by Google (though you don't have to use these services) include Google Storage for Developers and App Engine's built-in Datastore. The sample code for this lesson uses DataStore code.
持久层是你的应用储存需要长时间保存的数据的地方,所有任何需要为用户保存的数据都要放到持久层.持久层有几种方式,你可以根据需要存储数据的特点来选择适当的方式.有一些方式是Google服务器提供的(当然你不一定非要使用这些服务)包括Google Storage for Developers (Google为开发者提供的存储服务)和在AppEngine集成的数据库.下面的实例代码使用DataStore代码.
Create a class in your com.cloudtasks.server package to handle persistence layer input and output. In order to access the data store, use the PersistenceManager class. You can generate an instance of this class using the PMF class in the com.google.android.c2dm.server.PMF package, and then use that to perform basic CRUD operations on your data store, like this:
在你的com.cloudtasks.server里创建一个类来处理持久层的输入输出.为了访问数据存储,使用了PersistenceManager 类.你可以使用com.google.android.c2dm.server.PMF 包的PMF类来生成这个类的实例然后使用这个类来进行基本的增删改查,如下:
/** * Remove this object from the data store. */ public void delete(Long id) { PersistenceManager pm = PMF.get().getPersistenceManager(); try { Task item = pm.getObjectById(Task.class, id); pm.deletePersistent(item); } finally { pm.close(); } }
You can also use Query objects to retrieve data from your Datastore. Here's an example of a method that searches out an object by its ID.
你也可以使用Query类来从你的数据库中查询数据.这里是通过ID来找到Object的例子.
public Task find(Long id) { if (id == null) { return null; } PersistenceManager pm = PMF.get().getPersistenceManager(); try { Query query = pm.newQuery("select from " + Task.class.getName() + " where id==" + id.toString() + " && emailAddress=='" + getUserEmail() + "'"); List<Task> list = (List<Task>) query.execute(); return list.size() == 0 ? null : list.get(0); } catch (RuntimeException e) { System.out.println(e); throw e; } finally { pm.close(); } }
For a good example of a class that encapsulates the persistence layer for you, check out the DataStore class in the Cloud Tasks app.
在Cloud Tasks应用的DataStore类有一个很好的封装了持久层的例子.
Query and Update from the Android App-来自Android应用的查询和更新
In order to keep in sync with the App Engine application, your Android application needs to know how to do two things: Pull data from the cloud, and send data up to the cloud. Much of the plumbing for this is generated by the plugin, but you need to wire it up to your Android user interface yourself.
为了和AppEngine应用程序同步,你的Android应用需要知道怎么做两件事情:从云端获取数据,发送数据给云端.插件为这个功能创建了很多通道,但是你需要自己把它和用户Android用户接口连接起来.
Pop open the source code for the main Activity in your project and look for <YourProjectName> Activity.java, then for the method setHelloWorldScreenContent(). Obviously you're not building a HelloWorld app, so delete this method entirely and replace it with something relevant. However, the boilerplate code has some very important characteristics. For one, the code that communicates with the cloud is wrapped in an AsyncTask and therefore not hitting the network on the UI thread. Also, it gives an easy template for how to access the cloud in your own code, using the RequestFactory class generated that was auto-generated for you by the Eclipse plugin (called MyRequestFactory in the example below), and various Request types.
打开你的工程找到<YourProjectName> Activity.java,然后找到setHelloWorldScreenContent().很明显你不是在创建一个HelloWorld应用,那么删掉这个方法然后写上相应的方法.不过这个模版是有很多重要的特色的.比如,他通过AsyncTask(异步任务)来和云端交互而不是在主UI线程.同时,他提供了一个简单的代码模版来访问云端服务器,这通常通过使用Eclipse插件自动生成的RequestFactory 类(下面的例子中调用了MyRequestFactory)和各种请求类型来实现.
For instance, if your server-side data model included an object called Task when you generated an RPC layer it automatically created a TaskRequest class for you, as well as a TaskProxy representing the individual task. In code, requesting a list of all these tasks from the server looks like this:
举个例子, 如果你的服务器端数据模型包括一个被称为Task的object时,当你生成一个RPC层的时候它会自动创建一个TaskRequest类和一个代表现存的任务的TaskProxy类.从服务端请求一系列这类任务数据的代码类型这样:
public void fetchTasks (Long id) { // Request is wrapped in an AsyncTask to avoid making a network request // on the UI thread. new AsyncTask<Long, Void, List<TaskProxy>>() { @Override protected List<TaskProxy> doInBackground(Long... arguments) { final List<TaskProxy> list = new ArrayList<TaskProxy>(); MyRequestFactory factory = Util.getRequestFactory(mContext, MyRequestFactory.class); TaskRequest taskRequest = factory.taskNinjaRequest(); if (arguments.length == 0 || arguments[0] == -1) { factory.taskRequest().queryTasks().fire(new Receiver<List<TaskProxy>>() { @Override public void onSuccess(List<TaskProxy> arg0) { list.addAll(arg0); } }); } else { newTask = true; factory.taskRequest().readTask(arguments[0]).fire(new Receiver<TaskProxy>() { @Override public void onSuccess(TaskProxy arg0) { list.add(arg0); } }); } return list; } @Override protected void onPostExecute(List<TaskProxy> result) { TaskNinjaActivity.this.dump(result); } }.execute(id); } ... public void dump (List<TaskProxy> tasks) { for (TaskProxy task : tasks) { Log.i("Task output", task.getName() + "\n" + task.getNote()); } }
This AsyncTask returns a list of TaskProxy objects, and sends it to the debug dump() method upon completion. Note that if the argument list is empty, or the first argument is a -1, all tasks are retrieved from the server. Otherwise, only the ones with IDs in the supplied list are returned. All the fields you added to the task entity when building out the App Engine application are available via get/set methods in the TaskProxy class.
这个异步类返回TaskProxy列表对象,完成后发送它到debug的dump()方法.注意如果参数列表是空的或者第一个参数是-1,那所有的任务都将会从服务器中检索.否则,只有那些列表中存在对于ID的会被返回.创建AppEngine应用的时候,你在任务实体添加的所有字段都可以在TaskProxy类的get/set方法中得到.
In order to create new tasks and send them to the cloud, create a request object and use it to create a proxy object. Then populate the proxy object and call its update method. Once again, this should be done in anAsyncTask to avoid doing networking on the UI thread. The end result looks something like this.
为了创建新的任务并发送到云端,需要创建一个对象并使用它创建一个代理对象.然后填充这个代理对象并调用他的更新方法.并且,这个工作应该在AsynTask中完成来避免在UI线程中执行网络操作而引起UI阻塞.就像这样:
new AsyncTask<Void, Void, Void>() { @Override protected Void doInBackground(Void... arg0) { MyRequestFactory factory = (MyRequestFactory) Util.getRequestFactory(TasksActivity.this, MyRequestFactory.class); TaskRequest request = factory.taskRequest(); // Create your local proxy object, populate it TaskProxy task = request.create(TaskProxy.class); task.setName(taskName); task.setNote(taskDetails); task.setDueDate(dueDate); // To the cloud! request.updateTask(task).fire(); return null; } }.execute();
Configure the C2DM Server-Side-配置C2DM服务端
In order to set up C2DM messages to be sent to your Android device, go back into your App Engine codebase, and open up the service class that was created when you generated your RPC layer. If the name of your project is Foo, this class is called FooService. Add a line to each of the methods for adding, deleting, or updating data so that a C2DM message is sent to the user's device. Here's an example of an update task:
为了让C2DM信息发送到你的Android设备上,返回到你的AppEngine代码,打开在你生成RPC层的时候创建的服务类.如果你工程的名字是Foo,这个类就叫做FooService.为增删改查数据的每一条方法添加一行是的C2DM信息可以发送到用户的设备,代码如下:
public static Task updateTask(Task task) { task.setEmailAddress(DataStore.getUserEmail()); task = db.update(task); DataStore.sendC2DMUpdate(TaskChange.UPDATE + TaskChange.SEPARATOR + task.getId()); return task; } // Helper method. Given a String, send it to the current user's device via C2DM. public static void sendC2DMUpdate(String message) { UserService userService = UserServiceFactory.getUserService(); User user = userService.getCurrentUser(); ServletContext context = RequestFactoryServlet.getThreadLocalRequest().getSession().getServletContext(); SendMessage.sendMessage(context, user.getEmail(), message); }
In the following example, a helper class, TaskChange, has been created with a few constants. Creating such a helper class makes managing the communication between App Engine and Android apps much easier. Just create it in the shared folder, define a few constants (flags for what kind of message you're sending and a seperator is typically enough), and you're done. By way of example, the above code works off of a TaskChangeclass defined as this:
在接下来的例子中,定义一些常量(用来标记你在发送哪一种信息,这是很必要的),那么你就搞定了.按照这种方式,可以创建下面的TaskChange类:
public class TaskChange { public static String UPDATE = "Update"; public static String DELETE = "Delete"; public static String SEPARATOR = ":"; }
Configure the C2DM Client-Side-配置C2DM的客户端
In order to define the Android applications behavior when a C2DM is recieved, open up the C2DMReceiver class, and browse to the onMessage() method. Tweak this method to update based on the content of the message.
为了在C2DM接受接时候定义Android应用的行为,请打开C2DMReceiver类,然后找到onMessage()方法,根据消息的内容来修改这个方法.
//In your C2DMReceiver class public void notifyListener(Intent intent) { if (listener != null) { Bundle extras = intent.getExtras(); if (extras != null) { String message = (String) extras.get("message"); String[] messages = message.split(Pattern.quote(TaskChange.SEPARATOR)); listener.onTaskUpdated(messages[0], Long.parseLong(messages[1])); } } }
// Elsewhere in your code, wherever it makes sense to perform local updates public void onTasksUpdated(String messageType, Long id) { if (messageType.equals(TaskChange.DELETE)) { // Delete this task from your local data store ... } else { // Call that monstrous Asynctask defined earlier. fetchTasks(id); } }
Once you have C2DM set up to trigger local updates, you're all done. Congratulations, you have a cloud-connected Android application!
当你的C2DM触发了本地更新,那你就成功了.祝贺你,你搞定了一个Android云应用.

浙公网安备 33010602011771号