COMP5216移动计算 辅导

该文档是关于COMP5216移动计算课程Lab 04的实验指导,主要目标是学习如何使用Google的Firebase平台开发移动应用,以及使用Cloud Firestore存储和读取数据。具体任务包括连接应用到Firebase并配置Cloud Firestore、向Cloud Firestore写入数据、从Cloud Firestore显示数据、在Cloud Firestore中对数据进行排序和过滤。文档详细介绍了每个任务的具体步骤和相关代码。

2024年第二学期

Lab 04 – 云服务

目标: 1. 学习如何使用Google的Firebase平台开发移动应用。 2. 理解如何使用Cloud Firestore存储和读取数据。

任务: 1. 连接应用到Firebase并配置Cloud Firestore。 2. 向Cloud Firestore写入数据。 3. 从Cloud Firestore显示数据。 4. 在Cloud Firestore中对数据进行排序和过滤。

计算机科学学院

第1页,共13页

COMP5216移动计算

LabW04

到目前为止,我们已经学习了移动应用如何在手机本地保存数据(参考Lab Week 03)。但这并不允许其他客户端(如另一个应用或网站)访问这些数据。

本教程改编自Cloud Firestore Android Codelab,向您展示如何将移动应用连接到Cloud Firestore(Google为移动和网络应用提供的云端NoSQL数据库),并从中查询数据。您将学习如何构建一个由Cloud Firestore支持的餐厅推荐应用,名为“Friendly Eats”。此外,您还将学习如何: • 将您的应用连接到Google的Firebase平台并配置Cloud Firestore。 • 从Android应用向Cloud Firestore读写数据。 • 实时监听Firestore数据的变化。 • 使用基本的Firebase身份验证。

注意:本教程使用免费的Spark计划,对于大多数应用的开发目的来说已经足够。

任务1:连接应用到Firebase并配置Cloud Firestore 1. 使用您的Google账户登录Firebase控制台。 2. 在Firebase控制台上,点击“添加项目”。 您的Firebase项目 添加项目 3. 在“创建项目(步骤1/3)”中,为您的Firebase项目输入一个名称,例如“Friendly Eats”。点击“继续”。 4. 在步骤2/3“您的Firebase项目的Google Analytics”中,选择“为这个项目启用Google Analytics”。点击“继续”。 5. 在步骤3/3“配置Google Analytics”中,选择“创建一个新账户”,输入“COMP5216”作为新的Google Analytics账户名称。点击“保存”。 6. 将“Analytics位置”选项保留为“美国”,并保留“使用默认设置共享Google Analytics数据”的默认选项。 7. 接受Google Analytics条款。 8. 点击“创建项目”。 9. 大约一分钟后,您的Firebase项目将准备就绪。点击“继续”。 10. 从Canvas下载并解压为Fire Eats应用提供的示例基础代码:“friendlyeats – android.zip”。解压后,您的机器上应该会创建一个文件夹“friendlyeats – android”。您也可以直接从GitHub下载示例代码。 11. 将项目导入到Android Studio中。您可能会看到一些编译错误或关于缺少google – services.json文件的警告。 12. 在Firebase控制台上,选择左侧导航中的“项目概述”。通过点击Android按钮选择平台,开始将Firebase添加到您的应用中。当提示输入Android包名称时,使用“com.google.firebase.example.fireeats”。 通过添加Firebase开始您的应用 iOS 添加一个应用开始 13. 点击“注册应用”,并按照说明下载“google – services.json”配置文件,并将其移动到您的Android应用代码的app/文件夹中。点击“下一步”。 14. 按照说明通过修改您的build.gradle文件来添加Firebase SDK依赖项,以使用Google服务插件: • 项目级build.gradle(/build.gradle) buildscript { dependencies { // 添加这一行 classpath ‘com.android.tools.build:gradle:8.5.0’ classpath ‘com.google.gms:google – services:4.4.2’ • 应用级build.gradle(/<app – module>/build.gradle): apply plugin: ‘com.google.gms.google – services’ // 添加这一行 dependencies { // 添加这一行 // 导入Firebase BoM实现平台(‘com.google.firebase:firebase – bom: 33.1.2’) // 使用BoM时,不要在Firebase依赖项中指定版本 implementation ‘com.google.firebase:firebase – analytics’ Gradle文件自上次项目以来已更改,您的项目可能需要同步。立即同步 15. 最后,在Android Studio中出现的栏中点击“立即同步”。点击“下一步”。 16. 运行应用以验证安装。如果Firebase已成功添加到您的应用中,您应该在Firebase控制台上看到以下消息。 恭喜,您已成功将Firebase添加到您的应用中! 17. 接下来,我们应该设置一些基本规则来限制已登录用户的数据访问,以防止未经验证的用户读取或写入。在“产品类别 -> 构建”中选择“身份验证”。然后,“开始” -> “登录方法”选项卡 -> 选择“电子邮件/密码”。 电子邮件/密码 启用 允许用户使用他们的电子邮件地址和密码注册。我们的SDK还提供电子邮件地址验证、密码恢复和电子邮件地址更改的基本功能。了解更多 电子邮件链接(无密码登录) 启用 18. 为您的项目启用Cloud Firestore 在“产品类别 -> 构建”中选择“Firestore数据库” -> “创建数据库”来配置Cloud Firestore数据库。 19. 为您的Cloud Firestore设置一个位置为“nam5(us – central)”。一旦设置了位置,以后就无法更改。要了解更多信息,您可以阅读为您的项目选择位置。 20. 为您的Cloud Firestore安全规则选择一个起始模式。选择“以测试模式开始”,通过允许对您的数据库进行所有读写操作来快速设置。点击“下一步”。 21. 点击“启用”来配置Cloud Firestore。 …. 22. 对Cloud Firestore中数据的访问由安全规则控制。首先,我们需要对我们的数据设置一些基本规则,以限制只有已登录用户才能访问。在控制台中,导航到“Cloud Firestore” -> “规则”选项卡,添加这些规则并点击“发布”: service cloud.firestore { match /databases/{database}/documents { match /{document = **} { allow read, write: if request.auth!= null; 23. 如果您正确设置了您的应用,项目现在应该可以编译。在Android Studio中点击“构建” -> “重建项目”。在您的Android设备上运行应用。起初,您将看到一个“登录”屏幕。您可以使用电子邮件和密码注册到应用中。一旦您完成登录过程,您应该看到主屏幕:

计算机科学学院

第2页,共13页

COMP5216移动计算

LabW04

Y1 Friendly Eats

任务2:向Cloud Firestore写入数据 在这个任务中,我们将向Cloud Firestore写入一些数据,以便填充当前为空的主屏幕。您可以在Firebase控制台中手动输入数据,但我们将在应用本身中演示如何使用Android SDK向Firestore写入数据。

我们应用中的主要模型对象是餐厅(参考model/Restaurant.java)。Firestore数据分为文档、集合和子集合。我们将每个餐厅存储为顶级集合“restaurants”中的一个文档。要了解更多关于Firestore数据模型的信息,请阅读关于文档和集合的内容。 1. 首先,让我们获取一个FirebaseFirestore的实例来进行操作。编辑MainActivity中的initFirestore()方法: private void initFirestore() { mFirestore = FirebaseFirestore.getInstance(); 2. 在应用中添加功能,当我们点击溢出菜单中的“添加随机项目”按钮时,创建10个随机餐厅。填写onAddItemsClicked()方法: private void onAddItemsClicked() { // 获取到“restaurants”集合的引用 CollectionReference restaurants = mFirestore.collection(“restaurants”); for (int i = 0; i < 10; i++) { // 获取一个随机的Restaurant POJO Restaurant restaurant = RestaurantUtil.getRandom(this); // 向“restaurants”集合添加一个新文档 restaurants.add(restaurant); 关于上面的代码,有几个重要的注意事项: • 我们首先获取了“restaurants”集合的引用。集合在添加文档时会隐式创建,因此在写入数据之前不需要创建集合。 • 文档可以使用普通的Java对象(POJOs)创建,我们使用它来创建每个Restaurant文档。 • add()方法向集合中添加一个文档,并自动生成一个ID,因此我们不需要为每个Restaurant指定一个唯一的ID。 3. 现在再次运行应用,并点击溢出菜单中的“添加随机项目”按钮来调用您刚刚编写的代码: 退出登录 添加随机项目 如果您导航到Firebase控制台 -> “Firestore数据库 -> “数据”选项卡,您应该看到新添加的数据如下。恭喜,您刚刚向Cloud Firestore写入了数据!在下一步中,我们将学习如何在应用中显示这些数据。

计算机科学学院

第3页,共13页

COMP5216移动计算

LabW04

restaurants>7Ay32c3kQ7Vo… friendly – eats – 8845b restaurants 7Ay32c3kQ7VodozVqpDC 1 开始收集 添加文档 开始收集 restaurants 7Ay32c3kQ7VodozVapDC 添加字段 FKEi5VyHFSSK7TDOHN11 Cz2eE2NXhrntUvEYOXBz avgRating:3.072849915035425 category:“Indian I2e5AbgvgCW7wV8Bjx8S K8TY6Php03epGn6bgP1m KULBcBPKEOv1KtOr6zz3 city:”Indianapolis name:“Fire Eatery” numRatings:13 Z0gf6e0CwBcAgu855KGK photo:https://storage.googleapis.com/firestorequickstart juaMWaDPWJzIxnmaX1of price:3 t9Y61uDfIGGhzKXYLXjz zJt16mY2F83v2upnRU3

任务3:从Cloud Firestore显示数据 在这个任务中,我们将学习如何从Cloud Firestore检索数据并在我们的应用中显示它。 1. 从Cloud Firestore读取数据的第一步是创建一个查询。修改initFirestore()方法: private void initFirestore() { mFirestore = FirebaseFirestore.getInstance(); mQuery = mFirestore.collection(“restaurants”) // 获取50个评分最高的餐厅 .orderBy(“avgRating”, Query.Direction.DESCENDING .limit(LIMIT); 2. 现在我们想要监听这个查询,以便我们获取所有匹配的文档,并实时收到未来更新的通知。因为我们的最终目标是将此数据绑定到RecyclerView,我们需要创建一个RecyclerView.Adapter类来监听数据。虽然这将展示Cloud Firestore的实时功能,但也可以简单地获取数据而无需监听。您可以在任何查询或引用上调用get()来获取数据快照。 打开FirestoreAdapter类,它已经部分实现。首先,让我们使适配器实现EventListener并定义onEvent函数,以便它可以接收Firestore查询的更新:

计算机科学学院

第4页,共13页

COMP5216移动计算

LabW04

public abstract class FirestoreAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter{ //… public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) { if // 处理错误 (e!= null) { Log.w(TAG, “onEvent:error”, e); return; // 分发事件 for (DocumentChange change : documentSnapshots.getDocumentChanges()) { // 更改文档的快照 DocumentSnapshot snapshot = change.getDocument(); switch (change.getType()) { case ADDED: // TODO: 处理添加的文档 break; case MODIFIED: break; // TODO: 处理修改的文档 case REMOVED: // TODO: 处理删除的文档 break; onDataChanged(); 3. 在初始加载时,监听器将为每个新文档接收一个ADDED事件。随着查询结果集随时间变化,监听器将接收更多包含更改的事件。现在让我们完成监听器的实现。首先添加三个新方法:onDocumentAdded、onDocumentModified和onDocumentRemoved: protected void onDocumentAdded(DocumentChange change) { mSnapshots.add(change.getNewIndex(), change.getDocument()); notifyItemInserted(change.getNewIndex()); protected void onDocumentModified(DocumentChange change) { if (change.getOldIndex() == change.getNewIndex()) { // 项目更改但位置保持不变 mSnapshots.set(change.getOldIndex(), change.getDocument()); notifyItemChanged(change.getOldIndex()); } else { // 项目更改且位置改变 mSnapshots.remove(change.getOldIndex()); mSnapshots.add(change.getNewIndex(), change.getDocument()); notifyItemMoved(change.getOldIndex(), change.getNewIndex()); protected void onDocumentRemoved(DocumentChange change) { mSnapshots.remove(change.getOldIndex()); notifyItemRemoved(change.getOldIndex()); 4. 从onEvent中调用这三个新方法: @Override public void onEvent(QuerySnapshot documentSnapshots, FirebaseFirestoreException e) { if (e!= null // 处理错误 Log.w(TAG, “onEvent:error”, e); return; // 分发事件 for (DocumentChange change : documentSnapshots.getDocumentChanges()) { // 更改文档的快照 DocumentSnapshot snapshot = change.getDocument(); switch (change.getType()) { case ADDED: onDocumentAdded(change); break; case MODIFIED: onDocumentModified(change); break; case REMOVED: onDocumentRemoved(change); break; onDataChanged(); public void startListening() { 5. 最后实现startListening()方法来附加监听器: if (mQuery!= null && mRegistration == null) { mRegistration = mQuery.addSnapshotListener(this); 6. 现在应用已完全配置为从Cloud Firestore读取数据。再次运行应用,您应该看到在上一步中添加的餐厅。 回到Firebase控制台并编辑其中一个餐厅的名称。您应该几乎立即在应用中看到它的更改!

计算机科学学院

Y1 Friendly Eats

AIR

第5页,共13页

COMP5216移动计算

LabW04

任务4:在Cloud Firestore中对数据进行排序和过滤 该应用目前显示整个集合中评分最高的餐厅,但在实际的餐厅应用中,用户希望对数据进行排序和过滤。例如,应用应该能够显示“纽约最受欢迎的海鲜餐厅”或“最便宜的披萨”。

点击应用顶部的白色栏会弹出一个过滤器对话框。在本节中,我们将使用Firestore查询来使这个对话框工作: Y1Friendly Eats 过滤 所有食物 任何价格 按评分排序 应用 1. 编辑MainActivity.java中的onFilter()方法。这个方法接受一个Filters对象,这是我们创建的一个辅助对象,用于捕获过滤器对话框的输出。我们将更改这个方法,从过滤器构建一个查询。 在下面的代码片段中,我们通过附加where和orderBy子句来构建一个Query对象,以匹配给定的过滤器。

计算机科学学院

第6页,共13页

COMP5216移动计算

LabW04

@Override public void onFilter(Filters filters) { // 构建查询基本查询 Query query = mFirestore.collection(“restaurants”); if (filters.hasCategory()) { // 类别(等式过滤器) query = query.whereEqualTo(“category”, filters.getCategory()); if // 城市(等式过滤器) (filters.hasCity()) { query = query.whereEqualTo(“city”, filters.getCity()); // 价格(等式过滤器) if (filters.hasPrice()) { query = query.whereEqualTo(“price”, filters.getPrice()); // 按(orderBy和方向)排序 if (filters.hasSortBy()) { query = query.orderBy(filters.getSortBy(), filters.getSortDirection()); // 限制项目 query = query.limit(LIMIT); mQuery = query; mAdapter.setQuery(query); // 更新查询 // 设置标题 mCurrentSearchView.setText(Html.fromHtml(filters.getSearchDescription(this))); mCurrentSortByView.setText(filters.getOrderDescription(this)); mViewModel.setFilters(filters); // 保存过滤器 2. 运行应用并选择以下过滤器以显示最受欢迎的低价餐厅: 过滤 所有食物 任何地方 按受欢迎程度排序 取消 应用 3. 如果您使用adb logcat或Android Studio中的Logcat面板查看应用日志,您会注意到以下警告:

计算机科学学院

Computer Science Tutoring

第7页,共13页

COMP5216移动计算

LabW04

W/Firestore Adapter: onEvent:error com.google.firebase.firestore.FirebaseFirestoreException: FAILED_PRECONDITION