处理Android应用程序中的离线功能和数据同步 – 第一部分
在本系列的两部分中,我们将通过一个示例餐厅 Android 应用来了解这些 API 的使用方式,从而提供无缝的用户体验。
Intel® Developer Zone 提供跨平台应用开发工具和操作指南、平台和技术信息、代码示例以及同行专业知识,以帮助开发者创新和取得成功。加入我们的社区,了解 Android、物联网、Intel® RealSense™ 技术 和 Windows,下载工具、访问开发套件、与志同道合的开发者分享想法,并参与黑客松、竞赛、路演和本地活动。
摘要
需要后端支持的移动应用需要提供离线功能,因为设备可能无法持续联网。我们还需要一种高效的方式让我们的应用自动与后端服务器同步。在本文中,我们将了解如何利用 Android* 同步适配器框架来实现餐厅示例应用,以支持无缝数据同步。我们将讨论如何使用内容提供者和本地 SQLite 数据库提供离线功能。这将是一个两部分系列;第一部分将涵盖内容提供者 API 与本地 SQLite 数据库的使用。
目录
使用 ContentResolver 访问 RestaurantContentProvider
将 RestaurantContentProvider 添加到应用清单。
概述
依赖后端服务器获取数据或内容的 Android* 应用需要提供离线功能以获得无缝的用户体验。这要求我们维护数据模型的本地副本。我们还需要一种高效的方式来保持本地数据模型与后端服务器上的数据模型同步。我们可以使用标准的 Android API 实现这两个功能。
内容提供者 API 可用于抽象数据模型,并通过 SQLite API,我们可以维护一个设备上的 SQLite 数据库作为数据的本地副本。为了实现与服务器的高效数据同步,Android 提供了同步适配器框架 API,用于自动处理网络连接中断、后台同步和调度。
此外,我们可以将同步适配器框架与内容提供者挂钩,使我们能够重用相同的数据抽象,用于应用内使用和后台同步。有关 Android 中同步适配器框架的更多详细信息,请参阅以下链接。
https://developer.android.com.cn/training/sync-adapters/index.html
在本系列的两部分中,我们将通过一个示例餐厅 Android 应用来了解这些 API 的使用方式,从而提供无缝的用户体验。第一部分将涵盖内容提供者 API 与本地 SQLite 数据库的使用。
一家零售餐饮企业样本应用 – Little Chef。
“Little Chef”是一个示例餐厅应用,具有菜单内容、忠诚俱乐部和基于位置的服务等功能。该应用使用后端服务器获取最新的菜单内容和更新。后端数据库可以通过 Web 前端进行更新。
我们将使用本地 SQLite 数据库来镜像应用所需的数据模型。使用内容提供者是为了启用搜索建议,或者在未来与其他 Android 应用共享菜单内容。Android 同步适配器框架用于保持应用和后端之间的数据同步。
在接下来的几个部分中,我们将探讨这些功能在此示例应用中的实现方式。
使用内容提供者和本地 SQLite 数据库
先前,我们讨论了如何在 Little Chef 示例应用中使用本地 SQLite 数据库。请参阅以下链接作为参考。
https://software.intel.com/en-us/Android/articles/using-a-database-with-your-Android-app
Android 内容提供者 API 可以帮助我们处理新的用例,例如与其他设备上的应用共享菜单内容,以及提供设备上的自定义搜索和/或高级复制/粘贴操作。
一旦实现,内容提供者就可以作为通用的数据抽象,与 Android 的其他 API(如 UI 适配器或同步适配器)一起使用,例如。
有关创建和使用内容提供者的全面参考,请参阅以下链接中的官方文档。
https://developer.android.com.cn/guide/topics/providers/content-providers.html
对于示例应用,我们将使用内容提供者作为本地 SQLite 数据库的前端。
以前,示例应用直接使用 getMenuItems
方法从本地 SQLite 数据库访问菜单项。我们可以用一个简单的内容提供者包装这个函数。
实现 RestaurantContentProvider
要在 Android 中实现内容提供者,我们需要创建一个扩展抽象类 ContentProvider
的类,并实现其 6 个必需方法:query()
、insert()
、update()
、delete()
getType()
、onCreate()
。
public class RestaurantContentProvider extends ContentProvider {
private static final String AUTHORITY = "com.example.restaurant.provider";
private static final int MENU = 1;
private static final int MENU_ID = 2;
private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH);
static {
sURIMatcher.addURI(AUTHORITY, "menu", MENU);
sURIMatcher.addURI(AUTHORITY, "menu/#", MENU_ID);
}
public static final Uri MENU_URI = Uri.parse("content://" + AUTHORITY + "/" + "menu");
private RestaurantDatabase mDb;
@Override
public boolean onCreate() {
mDb = new RestaurantDatabase(getContext());
return true;
}
@Override
public String getType(Uri uri) {
switch (sURIMatcher.match(uri)) {
case MENU:
return "vnd.Android.cursor.dir/vnd.com.example.restaurant.provider.menu";
case MENU_ID:
return "vnd.Android.cursor.dir/vnd.com.example.restaurant.provider.menu";
default:
throw new RuntimeException("getType No URI Match: " + uri);
}
}
@Override
public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
switch (sURIMatcher.match(uri)) {
case MENU:
return mDb.getMenuItems();
default:
throw new UnsupportedOperationException("Not yet implemented");
}
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
// Implement this to handle requests to delete one or more rows.
throw new UnsupportedOperationException("Not yet implemented");
}
URI 用于唯一地寻址资源,而 AUTHORITY
标签用于标识内容提供者。
通过应用程序上下文可用的 ContentResolver
对象来访问内容提供者。ContentResolver
使用 URI 和 AUTHORITY 标签将调用路由到相应的内容提供者。
使用 ContentResolver 访问 RestaurantContentProvider
在示例应用的当前版本中,我们直接从 SQLite 数据库访问菜单项。使用 ContentResolver
,我们可以将数据访问调用指向我们之前创建的内容提供者包装器。
public void loadDataFromLocalDataBase(Context ctx) {
if (mMenuItems != null && mMenuItems.size() > 0) {
clear();
}
mMenuItems = new ArrayList<MenuItem>();
Set<String> categories = new HashSet<String>();
//Cursor c = db.getMenuItems();
Cursor c = ctx.getContentResolver().query(RestaurantContentProvider.MENU_URI, null, null, null, null);
while (c.moveToNext()) {
String category = c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.CATEGORY));
categories.add(category);
MenuItem menuItem = new MenuItem();
menuItem.setCategory(category);
menuItem.setName(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.NAME)));
menuItem.setDescription(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.DESCRIPTION)));
menuItem.setNutrition(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.NUTRITION)));
menuItem.setPrice(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.PRICE)));
menuItem.setImagename(c.getString(c.getColumnIndexOrThrow(RestaurantDatabase.MenuColumns.IMAGENAME)));
mMenuItems.add(menuItem);
}
c.close();
mCategoryList = new ArrayList<String>(categories);
}
由于 SQLite
和内容提供者 API 都使用 cursor
对象,我们可以像上面的代码片段所示那样简单地切换调用。
将 RestaurantContentProvider 添加到应用清单
最后,我们需要通过将内容提供者添加到应用清单来向 Android 系统注册它。
<provider
Android:name="com.example.restaurant.RestaurantContentProvider"
Android:authorities="com.example.restaurant.provider"
Android:enabled="true"
Android:exported="true" >
</provider>
provider 元素有几个选项可以自定义内容提供者的权限和安全性。有关更多详细信息,请参阅以下链接。
https://developer.android.com.cn/guide/topics/manifest/provider-element.html
在代码片段 3 中,我们将 exported
标志设置为 'True',以便我们可以选择性地与其他设备上的应用共享一些餐厅应用内容。
在本文的第二部分,我们将探讨餐厅示例应用如何利用 Android 同步适配器框架来实现与后端服务器的无缝数据同步。
关于作者
Ashok Emani 是 Intel 软件和服务部门的一名软件工程师。他目前从事 Intel® Atom™ 处理器规模的启用项目。
注意事项
本文档中的信息是根据 Intel 产品提供的。本文档不授予任何知识产权的明示或暗示、禁止反言或其他任何许可。除 Intel 产品销售条款和条件中的规定外,Intel 对此不承担任何责任,并且 Intel 否认与 Intel 产品销售和/或使用相关的任何明示或暗示的保证,包括对特定用途的适用性、适销性或对任何专利、版权或其他知识产权的侵权的保证。
除非 Intel 书面同意,否则 Intel 产品不设计也不用于任何可能导致人员伤亡的应用程序。
Intel 可随时更改规格和产品描述,恕不另行通知。设计人员不得依赖标记为“保留”或“未定义”的任何功能或说明的缺失或特性。Intel 保留这些用于未来定义的权利,并且对因未来对其进行的更改而引起的任何冲突或不兼容不承担任何责任。此处提供的信息如有更改,恕不另行通知。请勿依据此信息最终确定设计。
本文档中描述的产品可能包含已知为勘误的设计缺陷或错误,这可能导致产品偏离已发布的规范。当前的已表征勘误可应要求提供。
请联系您当地的英特尔销售办事处或您的经销商以获取最新的规范,并在下订单前进行咨询。
可通过致电 1-800-548-4725 或访问以下网址获取本文档中引用的带有订单号的文档副本或其他 Intel 文档: http://www.intel.com/design/literature.htm
性能测试中使用的软件和工作负载可能已针对仅在 Intel 微处理器上进行性能优化。性能测试(如 SYSmark* 和 MobileMark*)是使用特定的计算机系统、组件、软件、操作和功能测量的。任何对这些因素的更改都可能导致结果有所不同。您应参考其他信息和性能测试,以帮助您全面评估您打算购买的产品,包括该产品与其他产品组合时的性能。
本文档中重印的任何软件源代码均根据软件许可证提供,并且只能根据该许可证的条款使用或复制。
Intel、Intel 徽标和 Atom 是 Intel Corporation 在美国和/或其他国家的商标。
版权所有 © 2013 英特尔公司。保留所有权利。
*其他名称和品牌可能被声明为他人的财产。