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ẽ xây dựng một ứng dụng Android có tên Make It So. Giao diện người dùng của ứng dụng này được xây dựng hoàn toàn bằng Jetpack Compose – bộ công cụ hiện đại của Android để xây dựng giao diện người dùng gốc. Tính 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 chúng với Hoạt động, Mảnh hoặc Khung hiển thị.

Bước đầu tiên để hiểu rõ mức độ hiệu quả của Firebase và Jetpack Compose khi hoạt động cùng nhau là hiểu về cấu trúc Android hiện đại. Một cấu trúc tốt giúp hệ thống trở nên dễ hiểu, dễ phát triển và dễ bảo trì, vì nó thể hiện rất 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 gọi là Mô hình – Chế độ xem – ViewModel. Mô hình (Model) đại diện cho lớp truy cập vào Dữ liệu trong ứng dụng. Khung hiển thị là lớp giao diện người dùng và không được biết gì về logic nghiệp vụ. Và ViewModel là nơi áp dụng logic nghiệp vụ, đôi khi yêu cầu ViewModel gọi lớp Mô hình.

Bạn nên đọc bài viết này để hiểu cách áp dụng Mô hình – Chế độ xem – 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ã hơn và dễ dàng hoàn thành các bước tiếp theo hơn.

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. Hình ảnh bên dưới cho thấy hai 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 việc cần làm của Make it So Đặt làm 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 trình nghe vào bộ sưu tậ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 để giám sát hiệu suất của một số mã cụ thể trong ứng dụng
  • Tạo nút bật/tắt tính năng bằng cách sử dụng Cấu hình từ xa và sử dụng phương thức phát hành theo giai đoạn để chạy tính năng đó

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

  • Cách sử dụng tính nă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 API Firebase sao cho phù hợp với kiến trúc MVVM
  • Cách phản ánh các thay đổi được thực hiện bằng API Firebase trong giao diện người dùng Compose

Những gì bạn cần

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

Tải 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

Điều đầu tiên bạn cần làm là chuyển đến bảng điều khiển của Firebase và tạo một dự án Firebase bằng cách nhấp vào nút "+ Thêm dự án", như bạn có thể thấy bên dưới:

bảng điều khiển của Firebase

Làm theo các bước trên màn hình để hoàn tất quá trình tạo dự án.

Thêm một ứng dụng Android vào dự án Firebase của bạn

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 những 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). Đối với lớp học lập trình này, bạn không cần thêm chứng chỉ ký gỡ lỗi.
  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 của bạn xuống rồi lưu tệp đó trong thư mục make-it-so-android/app.
  4. Nhấp vào Tiếp theo. Vì Firebase SDK đã có trong tệp build.gradle trong dự án mẫu, hãy nhấp vào Tiếp theo để chuyển sang 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 hai việc trong Console 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 tính năng Xác thực

Trước tiên, hãy bật tính năng 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. Từ 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 bộ sưu tậ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 của Firebase, hãy mở rộng mục Build (Tạo) rồi chọn Firestore cơ sở dữ liệu.
  2. Nhấp vào Tạo cơ sở dữ liệu.
  3. Đặt Mã nhận dạng cơ sở dữ liệu thành (default).
  4. Chọn vị trí cho cơ sở dữ liệu của bạn, sau đó nhấp vào Tiếp theo.
    Đối với ứng dụng thực tế, bạn muốn chọn vị trí gần 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 tiết lộ ứng dụng công khai khi chưa 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 để tạo 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 /{document=**} {
      allow create: if request.auth != null;
      allow read, update, delete: if request.auth != null && resource.data.userId == request.auth.uid;
    }
  }
}

Về cơ bản, các quy tắc này cho biết rằng bất kỳ người dùng đã đăng nhập nào vào ứng dụng đều có thể tạo tài liệu cho chính mình trong bất kỳ bộ sưu tập nào. Sau đó, khi được 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

Bây giờ, bạn đã sẵn sàng chạy ứng dụng! Mở thư mục make-it-so-android/start trong Android Studio và 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?

Ở 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 phải đăng nhập trước. Phương thức này sử dụng phương thức xác thực ẩn danh để đạt được điều 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 trong tương lai. Mặc dù xác thực ẩn danh rất hữu ích cho quá trình làm quen với ứng dụng, nhưng bạn phải luôn cung cấp tuỳ chọn để người dùng chuyển đổi sang một hình thức đăng nhập khác. Vì vậy, trong lớp học lập trình này, bạn sẽ thêm phương thức xác thực 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 xác thực qua email, sau đó liên kết thông tin xác thực 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 linkAccount của dịch vụ 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, nó sẽ cố gắng xác thực và nếu lệnh gọi thành công, lệnh gọi sẽ chuyển sang màn hình tiếp theo (SettingsScreen). Khi bạn đang thực thi các lệnh gọi này bên trong khối launchCatching, nếu có lỗi xảy ra trên dòng đầu tiên, trường hợp ngoại lệ sẽ được phát hiện và xử lý và bạn sẽ không truy cập được dòng thứ hai.

Ngay khi SettingsScreen được mở lại, bạn cần đảm bảo rằng các tuỳ 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. Để thực hiện việc này, hãy tạo SettingsViewModel để theo dõi trạng thái của người dùng hiện tại (có trong AccountService.kt) nhằm kiểm tra xem tài khoản có ẩn danh hay không. Để thực hiện việc này, hãy cập nhật uiState trong SettingsViewModel.kt như sau:

screen/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ẽ tự kết hợp lại để cho thấy các tuỳ chọn theo trạng thái xác thực mới của người dùng.

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

Chạy Make It So và 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 màn hình. Từ đó, hãy nhấp vào tuỳ 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 email hợp lệ và mật khẩu mạnh để tạo tài khoản của bạn. Thao tác này sẽ hoạt động và bạn nên được chuyển hướng đến trang cài đặt, nơi bạn sẽ thấy hai tuỳ chọn mới: đăng xuất và xoá tài khoản của bạ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 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 bộ sưu tập Firestore, nơi lưu trữ những tài liệu đại diện cho các nhiệm vụ xuất hiện trong phần 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 đối với bộ sưu tập này.

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

Cập nhật Flow có sẵn trong StorageServiceImpl.kt thành 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 trình nghe vào tập hợp tác vụ dựa trên user.id. Mỗi việc cần làm đượ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 trong đó có một trường có tên là userId. Xin lưu ý rằng Flow mới sẽ được phát nếu trạng thái của currentUser thay đổi (ví dụ: khi đă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 là làm cho composable function trong TasksScreens.kt đại diện cho giao diện người dùng, nhận biết về luồng này và thu thập nó dưới dạng 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 đoạn mã 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 (là cấu trúc bạn sử dụng để hiển thị danh sách trên màn hình) 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 ứng dụng 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 của màn hình). Sau khi bạn hoàn tất việc tạo tác vụ, tác vụ đó sẽ xuất hiện trong tập hợp Firestore trong Firestore Console. Nếu đăng nhập vào chương trình Make It So trên các thiết bị khác bằng cùng tài khoản, bạn sẽ có thể chỉnh sửa các mục việc cần làm và xem các mục đó được cập nhật trên tất 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 vấn đề rất quan trọng cần được chú ý vì người dùng rất có thể sẽ bỏ ứ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 tất một thao tác đơn giản là sử dụng ứng dụng đó. Đó là lý do tại sao đôi khi việc thu thập một số chỉ số về hành trình cụ thể của người dùng trong ứng dụng lại hữu ích. Để giúp bạn làm điều đó, 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 nhiều đoạn mã trong bài viết Make It So (Tạo dấu vết tuỳ chỉnh).

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

Nếu mở tệp Performance.kt, bạn sẽ thấy một hàm cùng dòng có tên là trace (dấu vết). Hàm này gọi API Giám sát hiệu suất để tạo một dấu vết tuỳ chỉnh, chuyển tên dấu vết đó dưới dạng thông 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 để chạy hoàn chỉnh:

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 đó. Dưới đâ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 ứng dụng có hoạt động như mong đợi không.

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

Sau khi bạn hoàn tất việc thêm dấu vết tuỳ chỉnh, hãy chạy ứng dụng và đảm bảo rằng bạn 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 chuyển đến Trang tổng quan về hiệu suất. Ở cuối màn hình, bạn sẽ thấy ba thẻ: Yêu cầu mạng, Dấu vết tuỳ chỉnhHiển thị màn hình.

Chuyển đến thẻ Dấu vết tuỳ chỉnh và kiểm tra để đảm bảo các dấu vết bạn đã thêm trong cơ sở mã đang hiển thị ở đó. Bạn cũng có thể biết được thời gian cần để 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ó nhiều trường hợp sử dụng Cấu hình từ xa, từ việc thay đổi giao diện của ứng dụng từ xa đến việc thiết lập các hành vi riêng cho từng phân khúc người dùng. Trong lớp học lập trình này, bạn sẽ sử dụng Remote Config để tạo một nút bật/tắt tính năng sẽ hiển thị 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 việc nà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 thông số. Điền vào các trường theo hình ảnh bên dưới:

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

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. Giờ đây tham số đã được tạo và có sẵn cho cơ sở mã, 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 phương thức triển khai 2 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 trong tất cả màn hình ngay từ đầu. Việc bạn thay đổi giao diện người dùng hoặc hành vi của ứng dụng (khi người dùng đang thực hiện một thao tác nào đó) sau này sẽ gây ra trải nghiệm không tốt cho người dùng!

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

screen/tasks/TasksViewModel.kt

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

Bạn đang truy xuất giá trị trên dòng đầu tiên và sử dụng giá trị đó để tải các lựa chọn trình đơn cho các mục tác vụ trên dòng thứ hai. Nếu giá trị là false, điều này có nghĩa là trình đơn sẽ không chứa lựa chọn chỉnh sửa. Giờ đây, khi đã có danh sách các tuỳ chọn, bạn cần làm cho giao diện người dùng hiển thị chính xác danh sách này. Khi xây dựng ứ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. Vì vậy, hãy mở tệp TasksScreen.kt và cập nhật LazyColum để trỏ đến các tuỳ chọn có sẵn 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ụ. Và mỗi tác vụ đều có một trình đơn với các tuỳ chọn hiển thị 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 thử!

Bây giờ, bạn đã sẵn sàng chạy ứng dụng! Kiểm tra để đảm bảo rằng giá trị mà bạn đã phát hành 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 hai tuỳ chọn khi nhấp vào biểu tượng ba dấu chấm;
  • Nếu trạng thái là true, bạn sẽ thấy 3 tuỳ chọn khi nhấp vào biểu tượng ba dấu chấm;

Hãy thử thay đổi giá trị 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 tính năng 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 và bạn đã điều chỉnh ứng dụng đó cho 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