通过互联网连接提供功能强大的API,Android框架,可以帮助你建立丰富的云功能的应用程序,他们的数据同步到远程Web服务,确保您的所有设备始终保持同步,和您的宝贵数据备份到云。
本课程涵盖了不同的策略,云功能的应用程序。它涵盖了使用自己的后端Web应用程序与云同步数据,使用云计算的数据,使用户可以恢复他们的数据时,一个新的设备上安装应用程序和备份。
本篇重点分为两部分讲解,1、与App Engine同步 2、使用备份API
一、用App Engine的同步
编写同步到云中的应用程序,它可以是一个挑战。有很多的小细节,以得到正确的,如服务器端的认证,客户端认证,共享数据模型,以及一个API。使这更容易的方法之一是使用谷歌插件为Eclipse,为您构建Android和App Engine应用程序相互交谈时,处理大量的管道。这一课,引导您通过建立这样一个项目
准备好你的环境
如果你想跟随这一课中的代码示例,您必须执行以下操作来准备你的开发环境:
1、安装谷歌的Eclipse插件。
2、安装GWT SDK和Java App引擎SDK。快速入门指南,告诉你如何安装这些组件。
3、注册为C2DM访问。我们强烈建议创建一个新的Google帐户,专门用于连接到C2DM。在本课程中的服务器组件重复使用此角色帐户与谷歌服务器进行身份验证。
创建项目
在安装谷歌插件为Eclipse,通知了一种新的Android项目的存在,当你创建一个新的Eclipse项目:连接App Engine的Android项目(在谷歌项目类别)。此时出现一个向导,该向导将引导您通过创建这个项目,在此过程中,系统会提示您输入的帐户凭据创建帐户的作用。
注:请记住,输入您的角色帐户(你创建访问C2DM服务的),而不是你登录的用户帐户,或以管理员的凭据。
一旦你做,你会看到两个项目等着你,在您的工作区的Android应用程序和App Engine应用程序。万岁!这两个应用程序已经完全功能的向导创建了一个示例应用程序,它可以让你进行身份验证的App Engine应用程序从你的Android设备使用的AccountManager(无需输入您的凭据),和一个App Engine应用程序,可以发送邮件到任何已登录的设备使用C2DM。以旋转您的应用程序,并把它用于测试驱动器,请执行以下操作:
要旋转的Android应用程序,请确保你有一个AVD提供了一个平台版本的Android 2.2(API等级8级)。右键单击Android的Eclipse项目中,去调试连接>本地应用程序引擎Android应用。这将启动模拟器在这样一种方式,它可以测试C2DM功能(通常通过谷歌游戏)
创建数据层
打开你的app项目,在
(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; } ... }
一旦你写的所有的类,表示你的数据层中的实体,你需要在Android和App Engine应用程序进行通信有关此数据的一种方式。这种通信可以通过创建一个远程过程调用(RPC)服务。通常情况下,这涉及到很多的单调的代码。幸运的是,有一个简单的方法!右键单击您的应用程序引擎的源文件夹中的服务器项目,并在上下文菜单中,导航到“新建”>“其他”,然后在出现的屏幕中,选择谷歌> RPC服务。 出现一个向导,预填充的所有实体在上一步中创建的,它通过寻找您添加的源文件中的注释@Entity。漂亮整洁的,对不对?单击“ 完成“,向导将创建一个服务类的创建,检索,更新和删除(CRUD)操作的所有实体的存根方法。
创建持久层
持久层是应用程序数据长期存储,所以你要保持你的用户需要的任何信息去这里。您有几种选择写你的持久层,这取决于你想要什么样的数据存储
创建一个类在您的com.cloudtasks.server包,处理持久层的输入和输出。为了访问数据存储,使用的PersistenceManager实例 类。您可以生成这个类的一个实例,使用PMF类在com.google.android.c2dm.server.PMF包,然后用它来 执行基本的CRUD操作上的数据存储区,像这样:
/** * 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(); } }
你也可以查询一个对象根据id:
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(); } }
从android app上查询和更新
为了保持同步的App Engine应用程序,Android应用程序需要知道如何做两件事情:从云中提取数据,以及将数据发送到云。大部分的管道产生的插件,但是你需要将其连接到您的Android用户界面的自己。
例如,如果您的服务器端数据模型包括一个对象称为任务当你生成一个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()); } }
为了创建新任务,把它们发送到云,创建一个请求对象,并使用它创建一个代理对象。然后填充代理对象和调用它的update方法。再一次,这应该是做一个AsyncTask来避免做网络在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();
配置C2DM服务端
为了建立C2DM消息发送到你的Android设备,回到你的应用程序引擎代码库,和开放服务类生成了你你的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); }
在接下来的例子中,一个helper类,TaskChange,已经创建了一个有几个常量。创建这样一个助手类使管理应用程序引擎之间的通信和Android应用程序更加容易。只是创建它在共享文件夹中,定义一些常量,你就完成了。举个例子,上面的代码从一个TaskChange工作类的定义是这样的:
public class TaskChange { public static String UPDATE = "Update"; public static String DELETE = "Delete"; public static String SEPARATOR = ":"; }
配置C2DM客户端
为了定义Android应用程序的行为,当一个C2DM接待、开放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); } }
一旦你C2DM触发设置本地更新,你们都做了。祝贺你,你有一个Android应用程序的云!
二、使用备份Api
对于数据量的情况下,是比较轻(不到一兆字节),用户的喜好,游戏的高分或其他属性一样,备份API提供了一个轻量级的解决方案。这节课将引导您完成备份API集成到您的应用程序,并恢复数据到新的设备,使用备份API。
注册api备份服务
一旦完成,服务向导XML标记插入在你的Android Manifest,它看起来像这样:
<meta-data android:name="com.google.android.backup.api_key" android:value="ABcDe1FGHij2KlmN3oPQRs4TUvW5xYZ" />
注意,每个备份关键作品与特定的包名。如果你有不同的应用程序,注册为每一个单独的key。
配置你的Mainfest
使用Android备份服务需要两个添加到你的应用程序清单文件。首先,声明类的名称,作为您的备份代理,然后添加上面的代码片段作为一个应用程序标记的子元素
<application android:label="MyApp" android:backupAgent="TheBackupAgent"> ... <meta-data android:name="com.google.android.backup.api_key" android:value="ABcDe1FGHij2KlmN3oPQRs4TUvW5xYZ" /> ... </application>
编写你的备份代理
最简单的方式来创建你的备份代理是通过扩展BackupAgentHelper包装类。创建这个helper类实际上是一个非常简单的过程,这里的例子用来备份用户的分数信息:
import android.app.backup.BackupAgentHelper; import android.app.backup.FileBackupHelper; public class TheBackupAgent extends BackupAgentHelper { // The name of the SharedPreferences file static final String HIGH_SCORES_FILENAME = "scores"; // A key to uniquely identify the set of backup data static final String FILES_BACKUP_KEY = "myfiles"; // Allocate a helper and add it to the backup agent @Override void onCreate() { FileBackupHelper helper = new FileBackupHelper(this, HIGH_SCORES_FILENAME); addHelper(FILES_BACKUP_KEY, helper); } }
为了增加灵活性,FileBackupHelper的构造函数可以接受一个变量数目的文件名。你可以简单地支持上述两种分数和一个游戏进展文件只需添加一个额外的参数,就像这样
@Override void onCreate() { FileBackupHelper helper = new FileBackupHelper(this, HIGH_SCORES_FILENAME, PROGRESS_FILENAME); addHelper(FILES_BACKUP_KEY, helper); }
备份的偏好也同样容易。创建一个SharedPreferencesBackupHelper相同的方式做了一个FileBackupHelper。在这种情况下,而不是添加文件名的构造函数,的名称添加共享选择小组被您的应用程序。
import android.app.backup.BackupAgentHelper; import android.app.backup.SharedPreferencesBackupHelper; public class TheBackupAgent extends BackupAgentHelper { // The names of the SharedPreferences groups that the application maintains. These // are the same strings that are passed to getSharedPreferences(String, int). static final String PREFS_DISPLAY = "displayprefs"; static final String PREFS_SCORES = "highscores"; // An arbitrary string used within the BackupAgentHelper implementation to // identify the SharedPreferencesBackupHelper's data. static final String MY_PREFS_BACKUP_KEY = "myprefs"; // Simply allocate a helper and install it void onCreate() { SharedPreferencesBackupHelper helper = new SharedPreferencesBackupHelper(this, PREFS_DISPLAY, PREFS_SCORES); addHelper(MY_PREFS_BACKUP_KEY, helper); } }
您可以添加尽可能多的备份助手实例备份代理助手如你所愿,但记住,你只需要其中一种类型。一个FileBackupHelper处理所有的文件,你需要备份,一个SharedPreferencesBackupHelper处理所有共享preferencegroups你需要备份。
请求一个备份
为了请求一个备份,就创建一个实例,并调用它的BackupManager dataChanged()方法。
import android.app.backup.BackupManager; ... public void requestBackup() { BackupManager bm = new BackupManager(this); bm.dataChanged(); }
恢复一个备份
通常你不应该有手工请求恢复,因为它发生时自动应用程序是安装在一个设备。然而,如果需要触发一个手动恢复,就调用requestRestore()方法。