Dự án: Ứng dụng Forage

1. Trước khi bắt đầu

Lớp học lập trình này giới thiệu một ứng dụng mới có tên là Forage (Hái lượm hoa quả) mà bạn sẽ tự mình xây dựng. Trong lớp học lập trình này, bạn sẽ được hướng dẫn từng bước để hoàn thành dự án xây dựng ứng dụng Forage, bao gồm cả quá trình thiết lập và kiểm thử dự án trong Android Studio.

Điều kiện tiên quyết

  • Dự án này dành cho những học viên đã hoàn thành Bài 5 trong khoá học Kotlin cơ bản cho Android.

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

  • Sử dụng thư viện cơ sở dữ liệu Room để tăng tính liên tục cho ứng dụng hiện có bằng cách triển khai các thực thể, DAO, ViewModel và lớp cơ sở dữ liệu.

Bạn cần có

  • Một máy tính đã cài đặt Android Studio.

2. Tổng quan về ứng dụng đã hoàn thiện

Ứng dụng Forage (Hái lượm hoa quả) hoàn thiện cho phép người dùng theo dõi đồ vật (như thức ăn) mà họ kiếm được trong tự nhiên. Dữ liệu này vẫn tồn tại giữa các phiên bằng cách dùng Room. Bạn sẽ vận dụng kiến thức học được về Room và thực hiện các thao tác đọc, ghi, cập nhật và xoá trên cơ sở dữ liệu để tăng tính liên tục cho ứng dụng Forage. Dưới đây là thông tin mô tả về ứng dụng hoàn chỉnh và chức năng của ứng dụng.

Trong lần đầu khởi chạy ứng dụng, người dùng sẽ nhìn thấy một màn hình trống chứa khung hiển thị tái sinh (recycler view) cho thấy các mặt hàng kiếm được. Đồng thời, ở góc dưới cùng bên phải của ứng dụng sẽ có một nút tròn nổi, cho phép thêm các mặt hàng mới.

3edd87e63c387d88.png

Khi thêm một mục mới, người dùng có thể chỉ định tên, nơi tìm thấy cũng như ghi chú bổ sung. Ngoài ra, bạn sẽ thấy một hộp đánh dấu cho biết loại thực phẩm đó đang trong vào mùa hay không.

6c0c739569bb3b4f.png

Sau khi bạn thêm một mục, mục đó sẽ xuất hiện trong khung nhìn tái sinh trên màn hình đầu tiên.

bcc75e60b70320e8.png

Nhấn vào một mục, bạn sẽ thấy màn hình chi tiết cho thấy tên, vị trí và ghi chú.

5096995a4921dcac.png

Biểu tượng dấu cộng trên nút tròn nổi cũng chuyển sang biểu tượng chỉnh sửa. Khi nhấn vào nút này, bạn sẽ được dẫn đến một màn hình cho phép chỉnh sửa tên, vị trí, thông tin ghi chú và hộp đánh dấu "in season" ("đang trong mùa"). Khi nhấn vào nút xoá, mục này sẽ bị xoá khỏi cơ sở dữ liệu.

f8c708feed3dede1a.png

Phần giao diện người dùng của ứng dụng này đã hoàn thiện, nhiệm vụ của bạn bây giờ là tăng tính liên tục của ứng dụng trên cơ sở kiến thức bạn nắm được về Room. Ứng dụng sẽ cho phép người dùng đọc, ghi, cập nhật và xoá các loại thực phẩm khỏi cơ sở dữ liệu.

3. Bắt đầu

Tải mã dự án xuống

Lưu ý tên thư mục là android-basics-kotlin-forage-app. Chọn thư mục này khi bạn mở dự án trong Android Studio.

Để lấy mã nguồn cho lớp học lập trình này và mở đoạn mã đó trong Android Studio, hãy thực hiện các bước sau.

Lấy mã

  1. Nhấp vào URL được cung cấp. Thao tác này sẽ mở trang GitHub của dự án trong một trình duyệt.
  2. Trên trang GitHub của dự án, hãy nhấp vào nút Code (Mã), một hộp thoại sẽ xuất hiện.

5b0a76c50478a73f.png

  1. Trong hộp thoại này, hãy nhấp vào nút Download ZIP (Tải tệp ZIP xuống) để lưu dự án vào máy tính. Chờ quá trình tải xuống hoàn tất.
  2. Xác định vị trí của tệp trên máy tính (thường nằm trong thư mục Downloads (Tệp đã tải xuống)).
  3. Nhấp đúp vào tệp ZIP để giải nén. Thao tác này sẽ tạo một thư mục mới chứa các tệp dự án.

Mở dự án trong Android Studio

  1. Khởi động Android Studio.
  2. Trong cửa sổ Welcome to Android Studio (Chào mừng bạn đến với Android Studio), hãy nhấp vào Open an existing Android Studio project (Mở một dự án hiện có trong Android Studio).

36cc44fcf0f89a1d.png

Lưu ý: Nếu Android Studio đã mở sẵn thì thay vào đó, hãy chọn tuỳ chọn sau đây trong trình đơn File > New > Import Project (Tệp > Mới > Nhập dự án).

21f3eec988dcfbe9.png

  1. Trong hộp thoại Import Project (Nhập dự án), hãy chuyển đến nơi chứa thư mục dự án đã giải nén (thường nằm trong thư mục Downloads (Tệp đã tải xuống)).
  2. Nhấp đúp vào thư mục dự án đó.
  3. Chờ Android Studio mở dự án.
  4. Nhấp vào nút Run (Chạy) 11c34fc5e516fb1c.png để xây dựng và chạy ứng dụng. Hãy đảm bảo ứng dụng được dựng như mong đợi.
  5. Duyệt qua các tệp dự án trong cửa sổ công cụ Project (Dự án) để xem cách ứng dụng được thiết lập.

4. Thiết lập dự án để sử dụng thư viện Room

Định nghĩa thực thể Forageable

Dự án đã tạo một lớp Forageable để định nghĩa dữ liệu của ứng dụng (model.Forageable.kt). Lớp này có một số thuộc tính: id, name, address, inSeasonnotes.

data class Forageable(
   val id: Long = 0,
   val name: String,
   val address: String,
   val inSeason: Boolean,
   val notes: String?
)

Tuy nhiên, để có thể lưu trữ dữ liệu cố định bằng lớp này, bạn cần chuyển đổi lớp này thành một thực thể Room.

  1. Chú thích lớp này bằng cú pháp @Entity với tên bảng là "forageable_database".
  2. Đặt thuộc tính id làm khoá chính. Khoá chính sẽ được tạo tự động.
  3. Đặt tên cột cho thuộc tính inSeason thành "in_season".

Triển khai DAO

Có thể đoán được rằng ForageableDao (data.ForageableDao.kt) là nơi định nghĩa phương thức đọc và ghi trên cơ sở dữ liệu bạn sẽ truy cập trong mô hình xem (view model). DAO chỉ là một giao diện do bạn định nghĩa, vì vậy thực ra bạn không cần viết mã nguồn để triển khai các phương thức này. Thay vào đó, bạn nên sử dụng chú thích Room để chỉ định truy vấn SQL khi cần thiết.

Trong giao diện ForageableDao, bạn cần thêm 5 phương thức.

  1. Phương thức getForageables() trả về một Flow<List<Forageable>> cho tất cả các hàng trong cơ sở dữ liệu.
  2. Phương thức getForageable(id: Long) trả về Flow<Forageable> phù hợp với id đã chỉ định.
  3. Phương thức insert(forageable: Forageable) chèn một Forageable mới vào cơ sở dữ liệu.
  4. Phương thức update(forageable: Forageable) lấy Forageable hiện có làm tham số và cập nhật hàng tương ứng.
  5. Phương thức delete(forageable: Forageable) lấy Forageable làm tham số và xoá tham số đó khỏi cơ sở dữ liệu.

Triển khai view model

ForageableViewModel (ui.viewmodel.ForageableViewModel.kt) đã được triển khai một phần, bạn cần thêm chức năng truy cập phương thức DAO để có thể đọc và ghi dữ liệu. Thực hiện các bước sau đây để triển khai ForageableViewModel.

  1. Truyền một thực thể của ForageableDao vào hàm khởi tạo của lớp này dưới dạng tham số.
  2. Tạo biến thuộc kiểu LiveData<List<Forageable>> để lấy toàn bộ danh sách thực thể Forageable bằng DAO, sau đó chuyển đổi kết quả thành LiveData.
  3. Tạo một phương thức với tham số là mã nhận dạng (kiểu Long) rồi gọi phương thức getForageable() trên DAO để trả về LiveData<Forageable>, sau đó chuyển đổi kết quả thành LiveData.
  4. Trong phương thức addForageable(), hãy khởi chạy một coroutine bằng viewModelScope rồi sử dụng DAO để chèn thực thể Forageable vào cơ sở dữ liệu.
  5. Trong phương thức updateForageable(), hãy sử dụng DAO để cập nhật thực thể Forageable.
  6. Trong phương thức deleteForageable(), hãy sử dụng DAO để cập nhật thực thể Forageable.
  7. Tạo một ViewModelFactory để có thể tạo một thực thể của ForageableViewModel với tham số là hàm khởi tạo ForageableDao.

Triển khai lớp Database

Lớp ForageDatabase (data.ForageDatabase.kt) mới thực sự là lớp cho Room thấy các thực thể và DAO. Hãy triển khai lớp ForageDatabase theo mô tả dưới đây.

  1. Thực thể: Forageable
  2. Phiên bản: 1
  3. exportSchema: false
  4. Bên trong lớp ForageDatabase, hãy đưa vào một hàm abstract để trả về một ForageableDao
  5. Bên trong lớp ForageDatabase, hãy định nghĩa một đối tượng đồng hành có biến private tên là INSTANCE và một hàm getDatabase() trả về singleton ForageDatabase.
  1. Trong lớp BaseApplication, hãy tạo một thuộc tính database trả về một thực thể ForageDatabase sử dụng phương pháp khởi tạo từng phần (lazy initialization).

5. Duy trì và đọc dữ liệu qua các phân đoạn

Sau khi thiết lập các thực thể, DAO, mô hình hiển thị và định nghĩa lớp cơ sở dữ liệu để cho Room thấy, bạn chỉ cần sửa đổi các Mảnh (fragment) để truy cập vào mô hình hiển thị. Bạn cần thay đổi ba tệp, mỗi tệp cho một màn hình trong ứng dụng.

Danh sách hoa quả hái lượm được (Forageable)

Màn hình danh sách hoa quả hái lượm được chỉ có hai yêu cầu: tham chiếu đến view model và quyền truy cập danh sách đầy đủ các loại hoa quả đã hái lượm được. Thực hiện các hoạt động sau trong ui.ForageableListFragment.kt.

  1. Lớp này đã có một thuộc tính viewModel. Tuy nhiên, thuộc tính này chưa sử dụng hàm nhà máy (factory) bạn đã định nghĩa ở bước trước. Trước hết, bạn cần tái cấu trúc phần khai báo để sử dụng ForageableViewModelFactory.
private val viewModel: ForageableViewModel by activityViewModels {
   ForageableViewModelFactory(
       (activity?.application as BaseApplication).database.foragableDao()
   )
}
  1. Sau đó, trong onViewCreated(), hãy quan sát thuộc tính allForageables qua viewModel rồi gọi submitList() trên trình chuyển đổi để điền danh sách này vào thời điểm thích hợp.

Màn hình chi tiết hoa quả hái lượm được

Bạn sẽ thực hiện các bước tương tự cho danh sách chi tiết trong ui/ForageableDetailFragment.kt.

  1. Chuyển đổi thuộc tính viewModel để khởi chạy ForageableViewModelFactory cho chính xác.
  2. Trong onViewCreated(), hãy gọi getForageable() trên view model, truyền vào giá trị id để nhận thực thể Forageable. Quan sát dữ liệu trực tiếp rồi thiết lập kết quả cho thuộc tính forageable, sau đó gọi bindForageable() để cập nhật giao diện người dùng.

Thêm và chỉnh sửa màn hình hoa quả hái lượm được

Cuối cùng, bạn cần thực hiện các bước tương tự trong ui.AddForageableFragment.kt. Hãy lưu ý rằng màn hình này cũng chịu trách nhiệm cập nhật và xoá các thực thể. Tuy nhiên, các phương thức này đã được gọi ở nơi phù hợp trong view model. Bạn chỉ cần thực hiện hai thay đổi trong tệp này.

  1. Một lần nữa, hãy tái cấu trúc thuộc tính viewModel để sử dụng ForageableViewModelFactory.
  2. Trong onViewCreated(), trong khối lệnh if trước khi thiết lập chế độ hiển thị cho nút xoá, hãy gọi getForageable() trên mô hình hiển thị, truyền vào giá trị id và đặt kết quả thành thuộc tính forageable.

Đó là tất cả những gì cần làm trong các phân đoạn. Bây giờ, bạn có thể chạy ứng dụng và thấy rằng tất cả chức năng đều hoạt động liên tục.

6. Hướng dẫn kiểm thử

Chạy kiểm thử

Để chạy kiểm thử, bạn có thể làm theo một trong những cách sau.

Đối với một trường hợp kiểm thử duy nhất, hãy mở lớp trường hợp kiểm thử (test case) PersistenceInstrumentationTests.kt rồi nhấp vào mũi tên màu xanh lục ở bên trái phần khai báo lớp. Sau đó, chọn tuỳ chọn Run (Chạy) trên trình đơn. Thao tác này sẽ chạy tất cả chương trình kiểm thử trong trường hợp kiểm thử đó.

3e640ec727599a6d.png

Thường thì bạn chỉ muốn chạy một kiểm thử duy nhất, chẳng hạn như trong tình huống chỉ có một kiểm thử không đạt yêu cầu còn những kiểm thử còn lại thì đạt. Bạn có thể chạy một kiểm thử duy nhất tương tự như cách thực hiện trên toàn bộ trường hợp kiểm thử. Sử dụng mũi tên màu xanh lục rồi chọn tuỳ chọn Run (Chạy).

8647a76419540499.png

Nếu có nhiều trường hợp kiểm thử, bạn cũng có thể chạy toàn bộ bộ kiểm thử. Tương tự như cách chạy ứng dụng, bạn có thể thấy tuỳ chọn này trên trình đơn Run (Chạy).

7a925c5e196725bb.png

Lưu ý rằng theo mặc định, Android Studio sẽ chuyển đến mục tiêu gần nhất mà bạn đã chạy (ứng dụng, mục tiêu kiểm thử, v.v.). Do đó, nếu trình đơn vẫn cho thấy Run > Run 'app' (Chạy > Chạy "ứng dụng"), thì bạn có thể chạy mục tiêu kiểm thử này bằng cách chọn Run > Run (Chạy > Chạy).

90d3ec5ca5928b2a.png

Sau đó, hãy chọn mục tiêu kiểm thử trên trình đơn bật lên.

3b1a7d82a55b5f13.png