Tạo một ứng dụng Android bằng Firebase và Jetpack Compose

1. Giới thiệu

Lần cập nhật gần đây nhất: ngày 16 tháng 11 năm 2022

Tạo ứng dụng Android bằng Firebase và Jetpack Compose

Trong lớp học lập trình này, bạn sẽ tạo một ứng dụng Android có tên là Make It So. Giao diện người dùng của ứng dụng này được tạo hoàn toàn bằng Jetpack Compose, là bộ công cụ hiện đại của Android để tạo giao diện người dùng gốc – bộ công cụ này trực quan và yêu cầu ít mã hơn so với việc viết tệp .xml và liên kết các tệp đó với Hoạt động, Mảnh hoặc Khung hiển thị.

Bước đầu tiên để hiểu rõ mức độ phối hợp giữa Firebase và Jetpack Compose là tìm hiểu về cấu trúc Android hiện đại. Một cấu trúc tốt giúp hệ thống dễ hiểu, dễ phát triển và dễ duy trì, vì cấu trúc này giúp bạn hiểu rõ cách các thành phần được sắp xếp và giao tiếp với nhau. Trong thế giới Android, cấu trúc được đề xuất có tên là Model – View – ViewModel. Mô hình đại diện cho lớp truy cập vào Dữ liệu trong ứng dụng. View là lớp giao diện người dùng và không nên biết gì về logic nghiệp vụ. ViewModel là nơi áp dụng logic nghiệp vụ, đôi khi yêu cầu ViewModel gọi lớp Model.

Bạn nên đọc bài viết này để hiểu cách áp dụng Model – View – ViewModel cho một ứng dụng Android được tạo bằng Jetpack Compose, vì điều này sẽ giúp bạn dễ hiểu cơ sở mã và dễ dàng hoàn thành các bước tiếp theo.

Sản phẩm bạn sẽ tạo ra

Make It So là một ứng dụng danh sách việc cần làm đơn giản, cho phép người dùng thêm và chỉnh sửa việc cần làm, thêm cờ, mức độ ưu tiên và ngày đến hạn, đồng thời đánh dấu việc cần làm là đã hoàn thành. Những hình ảnh bên dưới cho thấy 2 trang chính của ứng dụng này: trang tạo việc cần làm và trang chính có danh sách việc cần làm đã tạo.

Màn hình Thêm công việc của ứng dụng Make it So Đặt thành Màn hình chính

Bạn sẽ thêm một số tính năng còn thiếu trong ứng dụng này:

  • Xác thực người dùng bằng email và mật khẩu
  • Thêm một trình nghe vào một tập hợp Firestore và làm cho giao diện người dùng phản ứng với các thay đổi
  • Thêm dấu vết tuỳ chỉnh để theo dõi hiệu suất của mã cụ thể trong ứng dụng
  • Tạo một nút bật/tắt tính năng bằng Remote Config và sử dụng tính năng phát hành theo giai đoạn để ra mắt nút này

Kiến thức bạn sẽ học được

  • Cách sử dụng Xác thực Firebase, Giám sát hiệu suất, Cấu hình từ xa và Cloud Firestore trong một ứng dụng Android hiện đại
  • Cách điều chỉnh các API của Firebase cho phù hợp với kiến trúc MVVM
  • Cách phản ánh những thay đổi được thực hiện bằng API Firebase trong giao diện người dùng Compose

Bạn cần

2. Tải ứng dụng mẫu và thiết lập Firebase

Lấy mã của ứng dụng mẫu

Sao chép kho lưu trữ GitHub từ dòng lệnh:

git clone https://github.com/FirebaseExtended/make-it-so-android.git

Tạo dự án Firebase

  1. Đăng nhập vào bảng điều khiển của Firebase bằng Tài khoản Google của bạn.
  2. Nhấp vào nút để tạo một dự án mới, sau đó nhập tên dự án (ví dụ: Compose Firebase codelab).
  3. Nhấp vào Tiếp tục.
  4. Nếu được nhắc, hãy xem xét và chấp nhận các điều khoản của Firebase, rồi nhấp vào Tiếp tục.
  5. (Không bắt buộc) Bật tính năng hỗ trợ của AI trong bảng điều khiển của Firebase (còn gọi là "Gemini trong Firebase").
  6. Đối với lớp học lập trình này, bạn cần Google Analytics để sử dụng các lựa chọn nhắm mục tiêu nâng cao với Cấu hình từ xa, vì vậy, hãy bật nút bật/tắt cho lựa chọn Google Analytics. Làm theo hướng dẫn trên màn hình để thiết lập Google Analytics.
  7. Nhấp vào Tạo dự án, đợi dự án được cấp phép rồi nhấp vào Tiếp tục.

Thêm một ứng dụng Android vào dự án Firebase

Trong dự án Firebase, bạn có thể đăng ký nhiều ứng dụng: cho Android, iOS, Web, Flutter và Unity.

Chọn Android như bạn thấy ở đây:

Tổng quan về dự án Firebase

Sau đó, hãy làm theo các bước sau:

  1. Nhập com.example.makeitso làm tên gói và nhập biệt hiệu (không bắt buộc). Trong lớp học lập trình này, bạn không cần thêm chứng chỉ gỡ lỗi để ký.
  2. Nhấp vào Tiếp theo để đăng ký ứng dụng và truy cập vào tệp cấu hình Firebase.
  3. Nhấp vào Tải google-services.json xuống để tải tệp cấu hình xuống và lưu tệp này vào thư mục make-it-so-android/app.
  4. Nhấp vào Tiếp theo. Vì các SDK Firebase đã có trong tệp build.gradle của dự án mẫu, hãy nhấp vào Tiếp theo để chuyển đến phần Các bước tiếp theo.
  5. Nhấp vào Tiếp tục đến bảng điều khiển để hoàn tất.

Để ứng dụng Make it So hoạt động đúng cách, bạn cần làm 2 việc trong Bảng điều khiển trước khi chuyển sang mã: bật trình cung cấp dịch vụ xác thực và tạo cơ sở dữ liệu Firestore.

Thiết lập phương thức xác thực

Trước tiên, hãy bật Xác thực để người dùng có thể đăng nhập vào ứng dụng:

  1. Trong trình đơn Build (Tạo), hãy chọn Authentication (Xác thực), rồi nhấp vào Get Started (Bắt đầu).
  2. Trong thẻ Phương thức đăng nhập, hãy chọn Email/Mật khẩu rồi bật phương thức này.
  3. Tiếp theo, hãy nhấp vào Thêm nhà cung cấp mới rồi chọn và bật Ẩn danh.

Thiết lập Cloud Firestore

Tiếp theo, hãy thiết lập Firestore. Bạn sẽ sử dụng Firestore để lưu trữ các việc cần làm của người dùng đã đăng nhập. Mỗi người dùng sẽ có tài liệu riêng trong một tập hợp của cơ sở dữ liệu.

  1. Trong bảng điều khiển bên trái của bảng điều khiển Firebase, hãy mở rộng mục Tạo rồi chọn Cơ sở dữ liệu Firestore.
  2. Nhấp vào Tạo cơ sở dữ liệu.
  3. Để nguyên Mã cơ sở dữ liệu được đặt thành (default).
  4. Chọn một vị trí cho cơ sở dữ liệu của bạn, rồi nhấp vào Tiếp theo.
    Đối với một ứng dụng thực tế, bạn nên chọn một vị trí gần với người dùng của mình.
  5. Nhấp vào Bắt đầu ở chế độ thử nghiệm. Đọc tuyên bố từ chối trách nhiệm về các quy tắc bảo mật.
    Trong các bước tiếp theo của phần này, bạn sẽ thêm Quy tắc bảo mật để bảo mật dữ liệu của mình. Không phân phối hoặc công khai một ứng dụng mà không thêm Quy tắc bảo mật cho cơ sở dữ liệu của bạn.
  6. Nhấp vào Tạo.

Hãy dành chút thời gian để xây dựng các Quy tắc bảo mật mạnh mẽ cho cơ sở dữ liệu Firestore.

  1. Mở trang tổng quan Firestore rồi chuyển đến thẻ Quy tắc.
  2. Cập nhật Quy tắc bảo mật để có dạng như sau:
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {
    match /tasks/{document} {
      allow create: if request.auth != null;
      allow read, update, delete: if request.auth != null
        && resource.data.userId == request.auth.uid
        && request.data.userId == resource.data.userId;
    }
  }
}

Về cơ bản, những quy tắc này quy định rằng mọi người dùng đã đăng nhập vào ứng dụng đều có thể tự tạo tài liệu trong bất kỳ bộ sưu tập nào. Sau đó, sau khi tạo, chỉ người dùng đã tạo tài liệu đó mới có thể xem, cập nhật hoặc xoá tài liệu đó.

Chạy ứng dụng

Giờ thì bạn đã sẵn sàng chạy ứng dụng! Mở thư mục make-it-so-android/start trong Android Studio rồi chạy ứng dụng (bạn có thể thực hiện việc này bằng Trình mô phỏng Android hoặc thiết bị Android thực).

3. Xác thực Firebase

Bạn sẽ thêm tính năng nào?

Trong trạng thái hiện tại của ứng dụng mẫu Make It So, người dùng có thể bắt đầu sử dụng ứng dụng mà không cần đăng nhập trước. Thư viện này sử dụng phương thức xác thực ẩn danh để thực hiện việc này. Tuy nhiên, tài khoản ẩn danh không cho phép người dùng truy cập vào dữ liệu của họ trên các thiết bị khác hoặc thậm chí trong các phiên sau này. Mặc dù xác thực ẩn danh rất hữu ích cho quá trình làm quen, nhưng bạn phải luôn cung cấp cho người dùng lựa chọn chuyển đổi sang một hình thức đăng nhập khác. Với mục tiêu này, trong lớp học lập trình này, bạn sẽ thêm tính năng xác thực bằng email và mật khẩu vào ứng dụng Make It So.

Đã đến lúc lập trình!

Ngay khi người dùng tạo tài khoản bằng cách nhập email và mật khẩu, bạn cần yêu cầu Firebase Authentication API cung cấp thông tin đăng nhập bằng email, sau đó liên kết thông tin đăng nhập mới với tài khoản ẩn danh. Mở tệp AccountServiceImpl.kt trong Android Studio rồi cập nhật hàm linkAccount để hàm này có dạng như sau:

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String) {
    val credential = EmailAuthProvider.getCredential(email, password)
    auth.currentUser!!.linkWithCredential(credential).await()
}

Bây giờ, hãy mở SignUpViewModel.kt và gọi hàm dịch vụ linkAccount bên trong khối launchCatching của hàm onSignUpClick:

screens/sign_up/SignUpViewModel.kt

launchCatching {
    accountService.linkAccount(email, password)
    openAndPopUp(SETTINGS_SCREEN, SIGN_UP_SCREEN)
}

Trước tiên, ứng dụng sẽ cố gắng xác thực và nếu lệnh gọi thành công, ứng dụng sẽ chuyển sang màn hình tiếp theo (SettingsScreen). Vì bạn đang thực thi các lệnh gọi này trong một khối launchCatching, nên nếu xảy ra lỗi ở dòng đầu tiên, ngoại lệ sẽ được nắm bắt và xử lý, đồng thời dòng thứ hai sẽ không được thực hiện.

Ngay khi SettingsScreen được mở lại, bạn cần đảm bảo rằng các lựa chọn Đăng nhậpTạo tài khoản đã biến mất, vì giờ đây người dùng đã được xác thực. Để làm việc này, hãy để SettingsViewModel theo dõi trạng thái của người dùng hiện tại (có trong AccountService.kt), để kiểm tra xem tài khoản có phải là tài khoản ẩn danh hay không. Để thực hiện việc này, hãy cập nhật uiState trong SettingsViewModel.kt để có dạng như sau:

screens/settings/SettingsViewModel.kt

val uiState = accountService.currentUser.map {
    SettingsUiState(it.isAnonymous)
}

Việc cuối cùng bạn cần làm là cập nhật uiState trong SettingsScreen.kt để thu thập các trạng thái do SettingsViewModel phát ra:

screens/settings/SettingsScreen.kt

val uiState by viewModel.uiState.collectAsState(
    initial = SettingsUiState(false)
)

Giờ đây, mỗi khi người dùng thay đổi, SettingsScreen sẽ kết hợp lại chính nó để hiển thị các lựa chọn theo trạng thái xác thực mới của người dùng.

Đã đến lúc kiểm tra!

Chạy Make it So rồi chuyển đến phần cài đặt bằng cách nhấp vào biểu tượng bánh răng ở góc trên cùng bên phải của màn hình. Sau đó, hãy nhấp vào lựa chọn tạo tài khoản:

Màn hình cài đặt Make it So Màn hình đăng ký Make it So

Nhập một email hợp lệ và mật khẩu mạnh để tạo tài khoản. Thao tác này sẽ hoạt động và bạn sẽ được chuyển hướng đến trang cài đặt, nơi bạn sẽ thấy 2 lựa chọn mới: đăng xuất và xoá tài khoản. Bạn có thể kiểm tra tài khoản mới được tạo trong trang tổng quan Xác thực trên bảng điều khiển của Firebase bằng cách nhấp vào thẻ Người dùng.

4. Cloud Firestore

Bạn sẽ thêm tính năng nào?

Đối với Cloud Firestore, bạn sẽ thêm một trình nghe vào tập hợp Firestore lưu trữ các tài liệu đại diện cho những việc cần làm xuất hiện trong Make it So. Sau khi thêm trình nghe này, bạn sẽ nhận được mọi nội dung cập nhật được thực hiện cho tập hợp này.

Đã đến lúc lập trình!

Cập nhật Flow có trong StorageServiceImpl.kt để có dạng như sau:

model/service/impl/StorageServiceImpl.kt

override val tasks: Flow<List<Task>>
    get() =
      auth.currentUser.flatMapLatest { user ->
        firestore.collection(TASK_COLLECTION).whereEqualTo(USER_ID_FIELD, user.id).dataObjects()
      }

Mã này đang thêm một trình nghe vào tập hợp các việc cần làm dựa trên user.id. Mỗi nhiệm vụ được biểu thị bằng một tài liệu trong một bộ sưu tập có tên là tasks và mỗi tài liệu có một trường có tên là userId. Xin lưu ý rằng một Flow mới sẽ được phát ra nếu trạng thái của currentUser thay đổi (ví dụ: bằng cách đăng xuất).

Bây giờ, bạn cần làm cho Flow trong TasksViewModel.kt phản ánh giống như trong dịch vụ:

screens/tasks/TasksViewModel.kt

val tasks = storageService.tasks

Và điều cuối cùng cần làm là để composable function trong TasksScreens.kt (đại diện cho giao diện người dùng) nhận biết luồng này và thu thập luồng này dưới dạng một trạng thái. Mỗi khi trạng thái thay đổi, hàm có khả năng kết hợp sẽ tự động kết hợp lại và hiển thị trạng thái gần đây nhất cho người dùng. Thêm nội dung này vào TasksScreen composable function:

screens/tasks/TasksScreen.kt

val tasks = viewModel
    .tasks
    .collectAsStateWithLifecycle(emptyList())

Sau khi hàm có khả năng kết hợp có quyền truy cập vào các trạng thái này, bạn có thể cập nhật LazyColumn (đây là cấu trúc bạn dùng để hiển thị danh sách trên màn hình) để có dạng như sau:

screens/tasks/TasksScreen.kt

LazyColumn {
    items(tasks.value, key = { it.id }) { taskItem ->
        TaskItem( [...] )
    }
}

Đã đến lúc kiểm tra!

Để kiểm tra xem có hoạt động hay không, hãy thêm một việc cần làm mới bằng ứng dụng (bằng cách nhấp vào nút thêm ở góc dưới cùng bên phải màn hình). Sau khi bạn hoàn tất việc tạo nhiệm vụ, nhiệm vụ đó sẽ xuất hiện trong tập hợp Firestore trong Bảng điều khiển Firestore. Nếu đăng nhập vào Make it So trên các thiết bị khác bằng cùng một tài khoản, bạn sẽ có thể chỉnh sửa các việc cần làm và xem chúng được cập nhật trên tất cả các thiết bị theo thời gian thực.

5. Giám sát hiệu suất

Bạn sẽ thêm tính năng nào?

Hiệu suất là một yếu tố rất quan trọng cần chú ý vì người dùng rất có thể sẽ bỏ dùng ứng dụng của bạn nếu hiệu suất không tốt và họ mất quá nhiều thời gian để hoàn thành một tác vụ đơn giản khi dùng ứng dụng. Đó là lý do đôi khi bạn nên thu thập một số chỉ số về một hành trình cụ thể mà người dùng thực hiện trong ứng dụng của bạn. Để giúp bạn làm việc đó, tính năng Giám sát hiệu suất Firebase cung cấp dấu vết tuỳ chỉnh. Hãy làm theo các bước tiếp theo để thêm dấu vết tuỳ chỉnh và đo lường hiệu suất trong các đoạn mã khác nhau trong Make it So.

Đã đến lúc lập trình!

Nếu mở tệp Performance.kt, bạn sẽ thấy một hàm nội tuyến có tên là trace. Hàm này gọi Performance Monitoring API để tạo một dấu vết tuỳ chỉnh, truyền tên dấu vết dưới dạng một tham số. Tham số khác mà bạn thấy là khối mã mà bạn muốn theo dõi. Chỉ số mặc định được thu thập cho mỗi dấu vết là thời gian cần thiết để chạy hoàn toàn:

model/service/Performance.kt

inline fun <T> trace(name: String, block: Trace.() -> T): T = Trace.create(name).trace(block)

Bạn có thể chọn những phần của cơ sở mã mà bạn cho là quan trọng để đo lường và thêm dấu vết tuỳ chỉnh vào đó. Sau đây là ví dụ về cách thêm dấu vết tuỳ chỉnh vào hàm linkAccount mà bạn đã thấy trước đó (trong AccountServiceImpl.kt) trong lớp học lập trình này:

model/service/impl/AccountServiceImpl.kt

override suspend fun linkAccount(email: String, password: String): Unit =
  trace(LINK_ACCOUNT_TRACE) {
      val credential = EmailAuthProvider.getCredential(email, password)
      auth.currentUser!!.linkWithCredential(credential).await()
  }

Giờ đến lượt bạn! Thêm một số dấu vết tuỳ chỉnh vào ứng dụng Make it So rồi chuyển sang phần tiếp theo để kiểm thử xem dấu vết có hoạt động như mong đợi hay không.

Đã đến lúc kiểm tra!

Sau khi bạn hoàn tất việc thêm các dấu vết tuỳ chỉnh, hãy chạy ứng dụng và nhớ sử dụng các tính năng mà bạn muốn đo lường một vài lần. Sau đó, hãy chuyển đến bảng điều khiển của Firebase rồi truy cập vào Trang tổng quan về hiệu suất. Ở cuối màn hình, bạn sẽ thấy 3 thẻ: Yêu cầu mạng, Dấu vết tuỳ chỉnhKết xuất màn hình.

Chuyển đến thẻ Dấu vết tuỳ chỉnh và kiểm tra để đảm bảo rằng các dấu vết mà bạn đã thêm vào cơ sở mã đang hiển thị ở đó và bạn có thể biết thường mất bao nhiêu thời gian để thực thi các đoạn mã này.

6. Cấu hình từ xa

Bạn sẽ thêm tính năng nào?

Có rất nhiều trường hợp sử dụng Cấu hình từ xa, từ việc thay đổi giao diện ứng dụng từ xa cho đến việc định cấu hình các hành vi khác nhau cho các phân khúc người dùng khác nhau. Trong lớp học lập trình này, bạn sẽ sử dụng Remote Config để tạo một công tắc bật/tắt tính năng. Công tắc này sẽ hiện hoặc ẩn tính năng chỉnh sửa việc cần làm mới trên ứng dụng Make it So.

Đã đến lúc lập trình!

Việc đầu tiên bạn cần làm là tạo cấu hình trong bảng điều khiển của Firebase. Để làm như vậy, bạn cần chuyển đến trang tổng quan Cấu hình từ xa rồi nhấp vào nút Thêm tham số. Điền thông tin vào các trường theo hình ảnh bên dưới:

Hộp thoại Cấu hình từ xa Tạo thông số

Sau khi điền tất cả các trường, bạn có thể nhấp vào nút Lưu rồi nhấp vào Xuất bản. Bây giờ, tham số đã được tạo và có sẵn cho cơ sở mã của bạn, bạn cần thêm mã sẽ tìm nạp các giá trị mới vào ứng dụng. Mở tệp ConfigurationServiceImpl.kt và cập nhật quá trình triển khai hai hàm này:

model/service/impl/ConfigurationServiceImpl.kt

override suspend fun fetchConfiguration(): Boolean {
  return remoteConfig.fetchAndActivate().await()
}

override val isShowTaskEditButtonConfig: Boolean
  get() = remoteConfig[SHOW_TASK_EDIT_BUTTON_KEY].asBoolean()

Hàm đầu tiên tìm nạp các giá trị từ máy chủ và được gọi ngay khi ứng dụng khởi động, trong SplashViewModel.kt. Đây là cách tốt nhất để đảm bảo rằng các giá trị mới nhất sẽ có sẵn trên tất cả các màn hình ngay từ đầu. Sẽ không mang lại trải nghiệm người dùng tốt nếu bạn thay đổi giao diện người dùng hoặc hành vi của ứng dụng sau đó, khi người dùng đang làm việc gì đó!

Hàm thứ hai trả về giá trị boolean đã được xuất bản cho tham số mà bạn vừa tạo trong Bảng điều khiển. Bạn sẽ cần truy xuất thông tin này trong TasksViewModel.kt bằng cách thêm nội dung sau vào hàm loadTaskOptions:

screens/tasks/TasksViewModel.kt

fun loadTaskOptions() {
  val hasEditOption = configurationService.isShowTaskEditButtonConfig
  options.value = TaskActionOption.getOptions(hasEditOption)
}

Bạn đang truy xuất giá trị ở dòng đầu tiên và dùng giá trị đó để tải các lựa chọn trong trình đơn cho các mục công việc ở dòng thứ hai. Nếu giá trị là false, tức là trình đơn sẽ không có lựa chọn chỉnh sửa. Giờ đây, khi đã có danh sách các lựa chọn, bạn cần làm cho giao diện người dùng hiển thị danh sách đó một cách chính xác. Khi tạo một ứng dụng bằng Jetpack Compose, bạn cần tìm composable function khai báo giao diện người dùng của TasksScreen sẽ trông như thế nào. Vì vậy, hãy mở tệp TasksScreen.kt rồi cập nhật LazyColum để trỏ đến các lựa chọn có trong TasksViewModel.kt:

screens/tasks/TasksScreen.kt

val options by viewModel.options

LazyColumn {
  items(tasks.value, key = { it.id }) { taskItem ->
    TaskItem(
      options = options,
      [...]
    )
  }
}

TaskItem là một composable function khác khai báo giao diện người dùng của một tác vụ riêng lẻ sẽ trông như thế nào. Mỗi tác vụ đều có một trình đơn với các lựa chọn xuất hiện khi người dùng nhấp vào biểu tượng ba dấu chấm ở cuối tác vụ.

Đã đến lúc kiểm tra!

Giờ thì bạn đã sẵn sàng chạy ứng dụng! Kiểm tra để đảm bảo rằng giá trị mà bạn đã xuất bản bằng bảng điều khiển Firebase khớp với hành vi của ứng dụng:

  • Nếu là false, bạn sẽ chỉ thấy 2 lựa chọn khi nhấp vào biểu tượng ba dấu chấm;
  • Nếu là true, bạn sẽ thấy 3 lựa chọn khi nhấp vào biểu tượng ba dấu chấm;

Hãy thử thay đổi giá trị một vài lần trong Console rồi khởi động lại ứng dụng. Bạn có thể dễ dàng ra mắt các tính năng mới trong ứng dụng bằng Cấu hình từ xa!

7. Xin chúc mừng

Xin chúc mừng, bạn đã tạo thành công một ứng dụng Android bằng Firebase và Jetpack Compose!

Bạn đã thêm Xác thực Firebase, Giám sát hiệu suất, Cấu hình từ xa và Cloud Firestore vào một ứng dụng Android được tạo hoàn toàn bằng Jetpack Compose cho giao diện người dùng, đồng thời điều chỉnh để ứng dụng đó phù hợp với cấu trúc MVVM được đề xuất!

Tài liệu đọc thêm

Tài liệu tham khảo