Google 致力于为黑人社区推动种族平等。查看具体举措

Firebase Android Codelab - 建立友好的聊天

截屏

图片:工作友好的聊天应用程序。

欢迎来到友好聊天代码实验室。在此 Codelab 中,您将学习如何使用 Firebase 平台在 Android 上创建聊天应用。

你会学到什么

  • 如何使用 Firebase 身份验证来允许用户登录。
  • 如何使用 Firebase 实时数据库同步数据。
  • 如何在 Cloud Storage for Firebase 中存储二进制文件。
  • 如何使用 Firebase 本地模拟器套件通过 Firebase 开发 Android 应用。

你需要什么

  • Android Studio中4.2以上版本。
  • 一个Android模拟器与Android 5.0以上版本。
  • Java 7 或更高版本。安装Java使用这些指令;检查您的版本,运行java -version
  • 熟悉 Kotlin 编程语言。

克隆存储库

从命令行克隆 GitHub 存储库:

$ git clone https://github.com/firebase/codelab-friendlychat-android

导入到 Android Studio

在Android Studio中,选择文件>打开,然后选择build-android-start目录( android_studio_folder ) 来自您下载示例代码的目录。

您现在应该有build-android-start项目Android Studio中打开。如果你看到一个关于警告google-services.json文件丢失,也不用担心。将在后面的步骤中添加。

检查依赖项

在此代码实验室中,您需要的所有依赖项都已为您添加,但了解如何将 Firebase SDK 添加到您的应用程序很重要:

构建.gradle

buildscript {
    // ...

    dependencies {
        classpath 'com.android.tools.build:gradle:4.1.2'

        // The google-services plugin is required to parse the google-services.json file
        classpath 'com.google.gms:google-services:4.3.5'
    }
}

应用程序/build.gradle

plugins {
    id 'com.android.application'
    id 'kotlin-android'
    id 'com.google.gms.google-services'
}

android {
    // ...
}

dependencies {
    // ...

    // Google Sign In SDK
    implementation 'com.google.android.gms:play-services-auth:19.0.0'

    // Firebase SDK
    implementation platform('com.google.firebase:firebase-bom:26.6.0')
    implementation 'com.google.firebase:firebase-database-ktx'
    implementation 'com.google.firebase:firebase-storage-ktx'
    implementation 'com.google.firebase:firebase-auth-ktx'

    // Firebase UI Library
    implementation 'com.firebaseui:firebase-ui-auth:7.2.0'
    implementation 'com.firebaseui:firebase-ui-database:7.2.0'
}

为了运行火力地堡仿真器套件,您需要安装和使用火力地堡CLI

安装 CLI

选项 1 - 使用 npm 安装

如果您的机器上已经安装了 Node.js 和 npm,您可以使用以下命令安装 CLI:

npm install -g firebase-tools@latest

选项 2 - 安装独立二进制文件

如果你没有的Node.js / NPM或者你是新的应用程序开发,您可以安装CLI如下的一个独立的二进制为您的平台说明这里

检查安装

一旦你安装了火力地堡CLI,运行以下命令,以确保您有版本9.11.0或更高版本:

firebase --version

启动模拟器

在你的终端,从当地的根目录中运行以下命令codelab-friendlychat-android目录:

firebase emulators:start --project=demo-friendlychat-android

您应该会看到一些这样的日志。端口值在所定义firebase.json文件,该文件被包含在克隆的样本代码。

$ firebase emulators:start --project=demo-friendlychat-android
i  emulators: Starting emulators: auth, database, storage
i  emulators: Detected demo project ID "demo-friendlychat-android", emulated services will use a demo configuration and attempts to access non-emulated services for this project will fail.
i  database: Database Emulator logging to database-debug.log
i  ui: Emulator UI logging to ui-debug.log

┌─────────────────────────────────────────────────────────────┐
│ ✔  All emulators ready! It is now safe to connect your app. │
│ i  View Emulator UI at http://localhost:4000                │
└─────────────────────────────────────────────────────────────┘

┌────────────────┬────────────────┬────────────────────────────────┐
│ Emulator       │ Host:Port      │ View in Emulator UI            │
├────────────────┼────────────────┼────────────────────────────────┤
│ Authentication │ localhost:9099 │ http://localhost:4000/auth     │
├────────────────┼────────────────┼────────────────────────────────┤
│ Database       │ localhost:9000 │ http://localhost:4000/database │
├────────────────┼────────────────┼────────────────────────────────┤
│ Storage        │ localhost:9199 │ http://localhost:4000/storage  │
└────────────────┴────────────────┴────────────────────────────────┘
  Emulator Hub running at localhost:4400
  Other reserved ports: 4500

Issues? Report them at https://github.com/firebase/firebase-tools/issues and attach the *-debug.log files.

导航到HTTP://本地主机:4000在Web浏览器中查看火力地堡模拟器套房UI:

模拟器套件 UI 主页

离开emulators:start命令运行的代码实验室的其余部分。

连接您的应用

在Android Studio中,打开MainActivity.kt ,然后添加里面的下面的代码onCreate方法:

// When running in debug mode, connect to the Firebase Emulator Suite.
// "10.0.2.2" is a special IP address which allows the Android Emulator
// to connect to "localhost" on the host computer. The port values (9xxx)
// must match the values defined in the firebase.json file.
if (BuildConfig.DEBUG) {
    Firebase.database.useEmulator("10.0.2.2", 9000)
    Firebase.auth.useEmulator("10.0.2.2", 9099)
    Firebase.storage.useEmulator("10.0.2.2", 9199)
}

添加 google-services.json

为了让你的Android应用程序连接到火力地堡,你必须添加一个google-services.json内部文件app的Android项目的文件夹中。出于本 Codelab 的目的,我们提供了一个模拟 JSON 文件,可让您连接到 Firebase 模拟器套件。

复制mock-google-services.json文件到build-android-start/app文件夹中google-services.json

cp mock-google-services.json build-android-start/app/google-services.json

在此 Codelab 的最后一步中,您将学习如何创建真正的 Firebase 项目和 Firebase Android 应用,以便您可以使用自己的配置替换此模拟 JSON 文件。

运行应用程序

现在您已将项目导入 Android Studio 并添加了 Firebase 配置 JSON 文件,您已准备好首次运行该应用程序。

  1. 启动你的安卓模拟器。
  2. 在Android Studio中,单击运行执行 ) 在工具栏中。

该应用程序应在您的 Android 模拟器上启动。此时,您应该看到一个空的消息列表,并且无法发送和接收消息。在此代码实验室的下一步中,您将对用户进行身份验证,以便他们可以使用友好聊天。

此应用将使用 Firebase 实时数据库来存储所有聊天消息。但是,在我们添加数据之前,我们应该确保应用程序是安全的,并且只有经过身份验证的用户才能发布消息。在这一步中,我们将启用 Firebase 身份验证并配置实时数据库安全规则。

添加基本​​登录功能

接下来,我们将向应用添加一些基本的 Firebase 身份验证代码,以检测用户并实现登录屏幕。

检查当前用户

首先将以下实例变量添加到MainActivity.kt类:

主活动.kt

// Firebase instance variables
private lateinit var auth: FirebaseAuth

现在让我们修改MainActivity用户发送到登录屏幕,每当他们打开该应用,是未经验证。以下添加到onCreate()方法的binding附着到视图:

主活动.kt

// Initialize Firebase Auth and check if the user is signed in
auth = Firebase.auth
if (auth.currentUser == null) {
    // Not signed in, launch the Sign In activity
    startActivity(Intent(this, SignInActivity::class.java))
    finish()
    return
}

我们还需要检查如果在用户已登录onStart()

主活动.kt

public override fun onStart() {
    super.onStart()
    // Check if user is signed in.
    if (auth.currentUser == null) {
        // Not signed in, launch the Sign In activity
        startActivity(Intent(this, SignInActivity::class.java))
        finish()
        return
    }
}

然后实现getUserPhotoUrl()getUserName()方法返回有关当前认证的火力地堡用户的相应信息:

主活动.kt

private fun getPhotoUrl(): String? {
    val user = auth.currentUser
    return user?.photoUrl?.toString()
}

private fun getUserName(): String? {
    val user = auth.currentUser
    return if (user != null) {
        user.displayName
    } else ANONYMOUS
}

然后实现signOut()方法来处理登出按钮:

主活动.kt

private fun signOut() {
    AuthUI.getInstance().signOut()
    startActivity(Intent(this, SignInActivity::class.java))
    finish()
}

现在,我们已具备在必要时将用户发送到登录屏幕的所有逻辑。接下来,我们需要实现登录屏幕以正确验证用户。

实现登录屏幕

打开文件SignInActivity.kt 。这里使用一个简单的登录按钮来启动身份验证。在本节中,您将使用 FirebaseUI 来实现登录逻辑。

在添加验证实例变量SignInActivity下类// Firebase instance variables评论:

登录活动.kt

// Firebase instance variables
private lateinit var auth: FirebaseAuth

然后,编辑onCreate()方法中,你在做同样的方式初始化火力地堡MainActivity

登录活动.kt

// Initialize FirebaseAuth
auth = Firebase.auth

在加入ActivityResultLauncherSignInActivity

登录活动.kt

// ADD THIS
private val signIn: ActivityResultLauncher<Intent> =
        registerForActivityResult(FirebaseAuthUIActivityResultContract(), this::onSignInResult)

override fun onCreate(savedInstanceState: Bundle?) {
    // ...
}

接着,编辑onStart()方法开球的FirebaseUI标志在流:

登录活动.kt

public override fun onStart() {
    super.onStart()

    // If there is no signed in user, launch FirebaseUI
    // Otherwise head to MainActivity
    if (Firebase.auth.currentUser == null) {
        // Sign in with FirebaseUI, see docs for more details:
        // https://firebase.google.com/docs/auth/android/firebaseui
        val signInIntent = AuthUI.getInstance()
                .createSignInIntentBuilder()
                .setLogo(R.mipmap.ic_launcher)
                .setAvailableProviders(listOf(
                        AuthUI.IdpConfig.EmailBuilder().build(),
                        AuthUI.IdpConfig.GoogleBuilder().build(),
                ))
                .build()

        signIn.launch(signInIntent)
    } else {
        goToMainActivity()
    }
}

接下来,实施onSignInResult方法以处理结果的标志。如果登入的结果是成功的,继续MainActivity

登录活动.kt

private fun onSignInResult(result: FirebaseAuthUIAuthenticationResult) {
    if (result.resultCode == RESULT_OK) {
        Log.d(TAG, "Sign in successful!")
        goToMainActivity()
    } else {
        Toast.makeText(
                this,
                "There was an error signing in",
                Toast.LENGTH_LONG).show()

        val response = result.idpResponse
        if (response == null) {
            Log.w(TAG, "Sign in canceled")
        } else {
            Log.w(TAG, "Sign in error", response.error)
        }
    }
}

就是这样!您仅通过几​​个方法调用就使用 FirebaseUI 实现了身份验证,无需管理任何服务器端配置。

测试你的工作

在您的 Android 模拟器上运行该应用程序。您应该立即被发送到登录屏幕。点击登录与电子邮件按钮,然后创建一个帐户。如果一切都正确实施,您应该被发送到消息屏幕。

登录后,在浏览器中打开火力地堡模拟器套房UI,然后单击身份验证选项卡,看看这第一个登入的用户帐户。

在此步骤中,我们将添加读取和显示存储在实时数据库中的消息的功能。

导入示例消息

  1. 在火力地堡模拟器套房UI,选择实时数据库选项卡。
  2. 拖放initial_messages.json从本地的代码实验室资源库的副本文件下载到数据浏览器。

你现在应该有下几个邮件messages数据库的节点。

读取数据

同步消息

在本节中,我们添加了通过以下方式将新添加的消息同步到应用程序 UI 的代码:

  • 初始化 Firebase 实时数据库并添加一个侦听器来处理对数据所做的更改。
  • 更新RecyclerView适配器,使新的信息将被显示。
  • 与添加在你的其他火力地堡的实例变量的数据库实例变量MainActivity类:

主活动.kt

// Firebase instance variables
// ...
private lateinit var db: FirebaseDatabase
private lateinit var adapter: FriendlyMessageAdapter

修改您的MainActivity的onCreate()在注释方法// Initialize Realtime Database and FirebaseRecyclerAdapter下面定义的代码。此代码将从实时数据库的所有现有的邮件,然后侦听下新的子项messages在你的火力地堡实时数据库路径。它为每条消息向 UI 添加一个新元素:

主活动.kt

// Initialize Realtime Database
db = Firebase.database
val messagesRef = db.reference.child(MESSAGES_CHILD)

// The FirebaseRecyclerAdapter class and options come from the FirebaseUI library
// See: https://github.com/firebase/FirebaseUI-Android
val options = FirebaseRecyclerOptions.Builder<FriendlyMessage>()
    .setQuery(messagesRef, FriendlyMessage::class.java)
    .build()
adapter = FriendlyMessageAdapter(options, getUserName())
binding.progressBar.visibility = ProgressBar.INVISIBLE
manager = LinearLayoutManager(this)
manager.stackFromEnd = true
binding.messageRecyclerView.layoutManager = manager
binding.messageRecyclerView.adapter = adapter

// Scroll down when a new message arrives
// See MyScrollToBottomObserver for details
adapter.registerAdapterDataObserver(
    MyScrollToBottomObserver(binding.messageRecyclerView, adapter, manager)
)

接着在FriendlyMessageAdapter.kt类实现bind()的内部类方法MessageViewHolder()

友好消息适配器.kt

inner class MessageViewHolder(private val binding: MessageBinding) : ViewHolder(binding.root) {
    fun bind(item: FriendlyMessage) {
        binding.messageTextView.text = item.text
        setTextColor(item.name, binding.messageTextView)

        binding.messengerTextView.text = if (item.name == null) ANONYMOUS else item.name
        if (item.photoUrl != null) {
            loadImageIntoView(binding.messengerImageView, item.photoUrl!!)
        } else {
            binding.messengerImageView.setImageResource(R.drawable.ic_account_circle_black_36dp)
        }
    }
    ...
}

我们还需要是图像的显示消息,所以也实现了bind()的内部类方法ImageMessageViewHolder()

友好消息适配器.kt

inner class ImageMessageViewHolder(private val binding: ImageMessageBinding) :
    ViewHolder(binding.root) {
    fun bind(item: FriendlyMessage) {
        loadImageIntoView(binding.messageImageView, item.imageUrl!!)

        binding.messengerTextView.text = if (item.name == null) ANONYMOUS else item.name
        if (item.photoUrl != null) {
            loadImageIntoView(binding.messengerImageView, item.photoUrl!!)
        } else {
            binding.messengerImageView.setImageResource(R.drawable.ic_account_circle_black_36dp)
        }
    }
}

最后,早在MainActivity ,启动和停止监听从火力地堡实时数据库的更新。更新onPause()onResume()的方法MainActivity如下所示:

主活动.kt

public override fun onPause() {
    adapter.stopListening()
    super.onPause()
}

public override fun onResume() {
    super.onResume()
    adapter.startListening()
}

测试同步消息

  1. 单击运行执行 )。
  2. 在模拟器套房UI,返回到实时数据库选项卡,然后手动添加一个新的消息。确认消息显示在您的 Android 应用中:

恭喜,您刚刚为您的应用添加了一个实时数据库!

实现短信发送

在本节中,您将添加应用程序用户发送文本消息的功能。下面侦听发送按钮点击事件的代码片段,创建一个新的FriendlyMessage与消息字段的内容对象,并推动信息到数据库中。所述push()方法增加了一个自动生成的ID到推对象的路径。这些 ID 是连续的,以确保将新消息添加到列表的末尾。

更新的发送按钮的点击监听器onCreate()的方法MainActivity类。这个代码是在底部onCreate()已经方法。更新onClick()的身体,以配合下面的代码:

主活动.kt

// Disable the send button when there's no text in the input field
// See MyButtonObserver for details
binding.messageEditText.addTextChangedListener(MyButtonObserver(binding.sendButton))

// When the send button is clicked, send a text message
binding.sendButton.setOnClickListener {
    val friendlyMessage = FriendlyMessage(
        binding.messageEditText.text.toString(),
        getUserName(),
        getPhotoUrl(),
        null /* no image */
    )
    db.reference.child(MESSAGES_CHILD).push().setValue(friendlyMessage)
    binding.messageEditText.setText("")
}

实现图片消息发送

在本节中,您将添加应用程序用户发送图像消息的功能。通过以下步骤创建图像消息:

  • 选择图片
  • 处理图像选择
  • 将临时图像消息写入实时数据库
  • 开始上传所选图片
  • 上传完成后,将图片消息 URL 更新为上传图片的 URL

选择图像

为了添加图像,此代码实验室使用 Cloud Storage for Firebase。 Cloud Storage 是存储应用程序二进制数据的好地方。

处理图像选择和写入临时消息

一旦用户已经选择的图像,图像选择Intent被启动。这是已经在代码在的端部实现onCreate()方法。当完成它调用MainActivityonImageSelected()方法。使用下面的代码片段,您将向数据库写入一条带有临时图像 url 的消息,指示正在上传图像。

主活动.kt

private fun onImageSelected(uri: Uri) {
    Log.d(TAG, "Uri: $uri")
    val user = auth.currentUser
    val tempMessage = FriendlyMessage(null, getUserName(), getPhotoUrl(), LOADING_IMAGE_URL)
    db.reference
            .child(MESSAGES_CHILD)
            .push()
            .setValue(
                    tempMessage,
                    DatabaseReference.CompletionListener { databaseError, databaseReference ->
                        if (databaseError != null) {
                            Log.w(
                                    TAG, "Unable to write message to database.",
                                    databaseError.toException()
                            )
                            return@CompletionListener
                        }

                        // Build a StorageReference and then upload the file
                        val key = databaseReference.key
                        val storageReference = Firebase.storage
                                .getReference(user!!.uid)
                                .child(key!!)
                                .child(uri.lastPathSegment!!)
                        putImageInStorage(storageReference, uri, key)
                    })
}

上传图片和更新消息

添加方法putImageInStorage()MainActivity 。这就是所谓的onImageSelected()启动所选择的图像的上传。上传完成后,您将更新消息以使用适当的图像。

主活动.kt

private fun putImageInStorage(storageReference: StorageReference, uri: Uri, key: String?) {
    // First upload the image to Cloud Storage
    storageReference.putFile(uri)
        .addOnSuccessListener(
            this
        ) { taskSnapshot -> // After the image loads, get a public downloadUrl for the image
            // and add it to the message.
            taskSnapshot.metadata!!.reference!!.downloadUrl
                .addOnSuccessListener { uri ->
                    val friendlyMessage =
                        FriendlyMessage(null, getUserName(), getPhotoUrl(), uri.toString())
                    db.reference
                        .child(MESSAGES_CHILD)
                        .child(key!!)
                        .setValue(friendlyMessage)
                }
        }
        .addOnFailureListener(this) { e ->
            Log.w(
                TAG,
                "Image upload task was unsuccessful.",
                e
            )
        }
}

测试发送消息

  1. 在 Android Studio 中,单击执行运行按钮。
  2. 在您的 Android 模拟器中,输入一条消息,然后点击发送按钮。新消息应该在应用 UI 和 Firebase Emulator Suite UI 中可见。
  3. 在 Android 模拟器中,点击“+”图像以从您的设备中选择图像。新消息应首先显示为占位符图像,然后在图像上传完成后显示为所选图像。新消息也应该在 Emulator Suite UI 中可见,特别是作为实时数据库选项卡中的对象和存储选项卡中的 blob。

您刚刚使用 Firebase 构建了一个实时聊天应用程序!

你学到了什么

  • Firebase 身份验证
  • Firebase 实时数据库
  • Firebase 云存储

接下来,尝试使用您在此 Codelab 中学到的知识将 Firebase 添加到您自己的 Android 应用中!要了解更多关于火力地堡,请访问firebase.google.com

如果您想了解如何建立一个真正的火力地堡项目和使用真正的火力地堡资源(而不是一个示范项目,模拟资源),继续下一步。

注:即使在您设置了一个真实的火力地堡项目,尤其是当你开始构建一个真正的应用程序,我们建议使用火力地堡本地仿真器套件用于开发和测试。

在此步骤中,您将创建一个真实的 Firebase 项目和一个 Firebase Android 应用以用于此 Codelab。您还将向您的应用添加特定于应用的 Firebase 配置。最后,您将设置真实的 Firebase 资源以与您的应用一起使用。

创建 Firebase 项目

  1. 在浏览器中,进入火力地堡控制台
  2. 选择添加项目
  3. 选择或输入项目名称。您可以使用任何您想要的名称。
  4. 您不需要为此 Codelab 使用 Google Analytics,因此您可以跳过为您的项目启用它。
  5. 单击创建项目。当您的项目已准备就绪,请点击继续

将 Firebase 添加到您的 Android 项目

在开始此步骤之前,请获取应用的 SHA1 哈希值。从本地运行以下命令build-android-start目录,以确定您的调试关键的SHA1:

./gradlew signingReport

Store: /Users/<username>/.android/debug.keystore
Alias: AndroidDebugKey
MD5: A5:88:41:04:8F:06:59:6A:AE:33:76:87:AA:AD:19:23
SHA1: A7:89:F5:06:A8:07:A1:22:EC:90:6A:A6:EA:C3:D4:8B:3A:30:AB:18
SHA-256: 05:A2:2A:35:EE:F2:51:23:72:4D:72:67:A5:6A:8A:58:22:2C:00:A6:AB:F6:45:D5:A1:82:D8:90:A4:69:C8:FE
Valid until: Wednesday, August 10, 2044

您应该会看到一些类似上面的输出。最重要的线是SHA1哈希值。如果您无法找到自己的SHA1哈希,请参阅本页面以获取更多信息。

返回 Firebase 控制台,并按照以下步骤将您的 Android 项目注册到您的 Firebase 项目:

  1. 在新项目的概览屏幕中,单击 Android 图标以启动设置工作流程:添加安卓应用
  2. 在接下来的屏幕上,输入com.google.firebase.codelab.friendlychat为您的应用程序包名称。
  3. 点击注册应用程序,然后点击下载谷歌,services.json下载您的火力地堡的配置文件。
  4. 复制google-services.json文件到app的Android项目的目录。
  5. 跳过在控制台的设置工作流程显示的下一步骤(他们已经被你在完成build-android-start项目)。
  6. 通过将您的项目与 Gradle 文件同步,确保您的应用程序可以使用所有依赖项。从Android Studio的工具栏中,选择文件>同步工程与摇篮文件

配置 Firebase 身份验证

在您的应用可以代表您的用户访问 Firebase 身份验证 API 之前,您需要启用 Firebase 身份验证以及要在应用中使用的登录提供程序。

  1. 火力地堡控制台,从左侧导航面板选择认证
  2. 选择登录方法选项卡。
  3. 单击电子邮件/密码,然后拨动开关,以启用(蓝色)。
  4. 点击谷歌,然后拨动开关来开启(蓝色),并设置一个项目支持电子邮件。

如果您稍后在此 Codelab 中收到错误消息“CONFIGURATION_NOT_FOUND”,请返回此步骤并仔细检查您的工作。

配置实时数据库

此 Codelab 中的应用将聊天消息存储在 Firebase 实时数据库中。在本节中,我们将创建一个数据库并通过称为 Firebase 安全规则的 JSON 配置语言配置其安全性。

  1. 火力地堡控制台,从左侧导航面板中选择实时数据库
  2. 单击创建数据库创建一个新的实时数据库实例。当出现提示时,选择us-central1区域,然后单击下一步
  3. 当提示安全规则,选择锁定模式,然后单击启用
  4. 一旦数据库实例被创建,选择规则标签,然后更新下面的规则配置:
     {
       "rules": {
         "messages": {
           ".read": "auth.uid != null",
           ".write": "auth.uid != null"
         }
       }
     }
    

有关如何安全规则的工作(包括在“身份验证”可变文档)的更多信息,请参阅实时数据库的安全性文档

为 Firebase 配置 Cloud Storage

  1. 火力地堡控制台,从左侧导航面板中选择存储
  2. 点击开始使用,使云存储为您的项目。
  3. 按照对话框中的步骤使用建议的默认值设置您的存储桶。

连接到 Firebase 资源

在本程式码实验室的早期步骤中,您添加了以下到MainActivity.kt 。此条件块将您的 Android 项目连接到 Firebase 模拟器套件。

// REMOVE OR DISABLE THIS
if (BuildConfig.DEBUG) {
    Firebase.database.useEmulator("10.0.2.2", 9000)
    Firebase.auth.useEmulator("10.0.2.2", 9099)
    Firebase.storage.useEmulator("10.0.2.2", 9199)
}

如果您想您的应用程序连接到新的真正的火力地堡的项目和它的真正火力地堡的资源,你可以删除这个块或发布模式下运行应用程序,以便BuildConfig.DEBUGfalse