Lấy dữ liệu trên Internet

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

Hầu hết ứng dụng Android trên thị trường đều kết nối với Internet để thực hiện một số thao tác mạng. Chẳng hạn như truy xuất email, tin nhắn hoặc thông tin tương tự qua một máy chủ phụ trợ. Ví dụ: Gmail, YouTube và Google Photos là một số ứng dụng có kết nối với Internet để hiển thị dữ liệu của người dùng.

Trong lớp học lập trình này, bạn sẽ sử dụng các thư viện nguồn mở được phát triển để xây dựng lớp mạng (network layer) và nhận dữ liệu qua một máy chủ phụ trợ. Việc này giúp đơn giản hoá quá trình tìm nạp dữ liệu, đồng thời hỗ trợ ứng dụng tuân thủ các phương pháp hay nhất của Android, chẳng hạn như thực hiện thao tác trên một luồng ở chế độ nền. Bạn cũng sẽ cập nhật giao diện người dùng của ứng dụng nếu Internet bị chậm hoặc không hoạt động. Việc này giúp người dùng luôn nắm được mọi vấn đề về kết nối mạng.

Kiến thức bạn cần có

  • Cách tạo và sử dụng các mảnh.
  • Cách sử dụng các thành phần cấu trúc Android ViewModelLiveData.
  • Cách thêm phần phụ thuộc vào tệp gradle.

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

  • Dịch vụ web REST là gì.
  • Sử dụng thư viện Retrofit để kết nối với một dịch vụ web REST trên Internet và nhận phản hồi.
  • Sử dụng thư viện Moshi để phân tích cú pháp phản hồi JSON thành đối tượng dữ liệu.

Bạn sẽ thực hiện

  • Sửa đổi ứng dụng bắt đầu (starter app) để tạo yêu cầu API dịch vụ web và xử lý phản hồi.
  • Triển khai lớp mạng (network layer) cho ứng dụng bằng cách sử dụng thư viện Retrofit.
  • Phân tích cú pháp phản hồi JSON qua dịch vụ web vào các đối tượng LiveData của ứng dụng bằng thư viện Moshi.
  • Sử dụng tính năng hỗ trợ của Retrofit dành cho coroutine để đơn giản hoá mã.

Bạn cần có

  • Một máy tính đã cài đặt Android Studio.
  • Mã bắt đầu cho ứng dụng MarsPhotos.

2. Tổng quan về ứng dụng

Trong lộ trình học tập này, bạn sẽ xử lý một ứng dụng bắt đầu có tên là MarsPhotos, cho thấy hình ảnh bề mặt sao Hoả. Ứng dụng này kết nối với một dịch vụ web để truy xuất và hiển thị ảnh sao Hoả. Những bức ảnh này là ảnh chụp sao Hoả thực tế được chụp qua thiết bị thám hiểm sao Hoả của NASA. Dưới đây là ảnh chụp màn hình của ứng dụng hoàn thiện, hiện các hình ảnh thuộc tính thu nhỏ (thumbnail) theo bố cục lưới được xây dựng bằng RecyclerView.

ea967f35fa98d72b.png

Phiên bản ứng dụng bạn xây dựng trong lớp học lập trình này sẽ không có nhiều hình ảnh trực quan, vì phiên bản này tập trung vào lớp kết nối mạng của ứng dụng để kết nối Internet và tải dữ liệu thuộc tính thô bằng dịch vụ web. Để đảm bảo dữ liệu được truy xuất và phân tích cú pháp chính xác, bạn sẽ chỉ in số lượng ảnh nhận được qua máy chủ phụ trợ trong chế độ xem văn bản:

1a7e99791caf8d96.png

3. Khám phá ứng dụng bắt đầu MarsPhotos

Tải mã khởi động

Lớp học lập trình này cung cấp mã khởi động để bạn mở rộng bằng các tính năng được dạy ở đây. Mã khởi động có thể chứa mã mà bạn đã biết và chưa biết từ các lớp học lập trình trước. Bạn sẽ tìm hiểu thêm về mã mới trong các lớp học lập trình về sau.

Nếu bạn sử dụng mã khởi động lấy trên GitHub, hãy lưu ý tên thư mục là android-basics-kotlin-mars-photos-app. Hãy chọn thư mục này khi bạn mở dự án trong Android Studio.

  1. Chuyển đến trang kho lưu trữ GitHub được cung cấp cho dự án.
  2. Xác minh rằng tên chi nhánh khớp với tên chi nhánh được chỉ định trong lớp học lập trình. Ví dụ: trong ảnh chụp màn hình sau đây, tên nhánh là main (chính).

1e4c0d2c081a8fd2.png

  1. Trên trang GitHub cho dự án này, nhấp vào nút Code (Mã). Thao tác này sẽ khiến một cửa sổ bật lên.

1debcf330fd04c7b.png

  1. Trong cửa sổ bật lên, 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 (Mở).

d8e9dbdeafe9038a.png

Lưu ý: Nếu Android Studio đã mở thì chuyển sang chọn tuỳ chọn File (Tệp) > Open (Mở) trong trình đơn.

8d1fda7396afe8e5.png

  1. Trong trình duyệt tệp, hãy chuyển đến vị trí của thư mục dự án chưa giải nén (có thể 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) 8de56cba7583251f.png để tạo và chạy ứng dụng. Đảm bảo ứng dụng được xây dựng như dự kiến.

Chạy mã bắt đầu

  1. Mở dự án đã tải xuống trong Android Studio. Tên thư mục của dự án là android-basics-kotlin-mars-photos-app. Cấu trúc của thư mục mã bắt đầu sẽ có dạng như dưới đây.
  2. Trong ngăn Android, hãy mở rộng app > java. Hãy lưu ý rằng ứng dụng có thư mục gói tên là overview. Đây là lớp giao diện người dùng (UI layer) của ứng dụng.

d428a16d480349d1.png

  1. Chạy ứng dụng. Khi biên dịch và chạy ứng dụng này, bạn sẽ thấy màn hình sau đây với văn bản phần giữ chỗ ở giữa. Vào cuối lớp học lập trình này, bạn sẽ cập nhật văn bản phần giữ chỗ này bằng số lượng ảnh đã truy xuất.

406c7bcc0f07267c.png

  1. Duyệt qua các tệp để tìm hiểu mã khởi động. Đối với tệp bố cục, bạn có thể sử dụng tuỳ chọn Split (Chia tách) ở góc trên cùng bên phải để xem trước bố cục và mã XML cùng lúc.

Hướng dẫn từng bước về mã khởi động

Trong nhiệm vụ này, bạn sẽ tự làm quen với cấu trúc của dự án. Sau đây là hướng dẫn từng bước về các tệp và thư mục quan trọng trong dự án.

OverviewFragment:

  • Đây là mảnh (fragment) hiện bên trong MainActivity. Văn bản phần giữ chỗ bạn thấy trong bước trước đó sẽ hiện trong mảnh này.
  • Trong lớp học lập trình tiếp theo, mảnh này sẽ cho thấy dữ liệu nhận được qua máy chủ phụ trợ ảnh sao Hoả.
  • Lớp này giữ một tham chiếu đến đối tượng OverviewViewModel.
  • OverviewFragment có hàm onCreateView() giúp tăng cường bố cục fragment_overview bằng cách sử dụng chế độ Liên kết dữ liệu (Data Binding), thiết lập chủ sở hữu vòng đời liên kết với chính nó và đặt giá trị cho biến viewModel trong đối tượng liên kết với hàm này.
  • Vì chủ sở hữu vòng đời là giá trị được gán nên mọi LiveData sử dụng trong chế độ Liên kết dữ liệu sẽ tự động quan sát thấy mọi thay đổi và giao diện người dùng cũng được cập nhật theo đó.

OverviewViewModel:

  • Đây là mô hình chế độ xem tương ứng cho OverviewFragment.
  • Lớp này chứa một thuộc tính MutableLiveData có tên _status cùng với thuộc tính hỗ trợ. Việc cập nhật giá trị của thuộc tính này cũng sẽ cập nhật văn bản phần giữ chỗ hiện trên màn hình.
  • Phương thức getMarsPhotos() cập nhật phản hồi của phần giữ chỗ. Sau này trong lớp học lập trình, bạn sẽ sử dụng lớp này để cho thấy dữ liệu đã tìm nạp qua máy chủ. Mục tiêu của lớp học lập trình này là cập nhật status LiveData trong ViewModel bằng cách sử dụng dữ liệu thực mà bạn lấy được trên Internet.

res/layout/fragment_overview.xml:

  • Bố cục này được thiết lập để sử dụng tính năng liên kết dữ liệu và bao gồm một TextView duy nhất.
  • Bố cục này khai báo biến OverviewViewModel, sau đó liên kết status từ ViewModel với TextView.

MainActivity.kt: nhiệm vụ duy nhất của hoạt động (activity) này là tải bố cục của hoạt động, activity_main.

layout/activity_main.xml: đây là bố cục hoạt động chính với một FragmentContainerView duy nhất trỏ đến fragment_overview, mảnh tổng quan (overview fragment) sẽ được tạo thực thể khi ứng dụng mở ra.

4. Tổng quan về ứng dụng

Trong lớp học lập trình này, bạn tạo một lớp dành cho dịch vụ mạng giao tiếp với máy chủ phụ trợ và tìm nạp dữ liệu cần thiết. Bạn sẽ sử dụng một thư viện của bên thứ ba để triển khai quy trình này, thư viện này có tên Retrofit. Bạn sẽ tìm hiểu thêm về thư viện này sau. ViewModel trực tiếp giao tiếp với lớp mạng đó, phần còn lại của ứng dụng sẽ tự điều chỉnh theo trong quá trình triển khai.

1c2493b9e9e1eef.png

OverviewViewModel chịu trách nhiệm thực hiện lệnh gọi qua mạng để nhận dữ liệu ảnh sao Hoả. Trong ViewModel, bạn sử dụng LiveData với tính năng liên kết dữ liệu có nhận biết vòng đời để cập nhật giao diện người dùng ứng dụng khi dữ liệu thay đổi.

5. Dịch vụ web và Retrofit

Dữ liệu ảnh sao Hoả được lưu trữ trên máy chủ web. Để đưa dữ liệu này vào ứng dụng, bạn cần thiết lập kết nối và giao tiếp với máy chủ trên Internet.

37f7c367e182b4f9.png

d99aca47f5947a78.png

Ngày nay, hầu hết máy chủ web chạy dịch vụ web bằng cách sử dụng một kiến trúc web không có trạng thái (stateless) phổ biến được gọi là REST, viết tắt của REpresentational State Transfer (Kiến trúc chuyển trạng thái đại diện). Các dịch vụ web cung cấp kiến trúc này được gọi là các dịch vụ RESTful.

Các yêu cầu được gửi đến dịch vụ web RESTful theo một cách được chuẩn hoá thông qua các URI. Mỗi URI (Uniform Resource Identifier – mã nhận dạng tài nguyên thống nhất) xác định một tài nguyên trong máy chủ theo tên mà không bao hàm vị trí của tài nguyên đó hay cách truy cập vào tài nguyên đó. Ví dụ: trong ứng dụng dành cho bài học này, bạn truy xuất URL hình ảnh bằng cách sử dụng URI máy chủ sau (Máy chủ này lưu trữ cả ảnh sao Hoả và ảnh bất động sản trên sao Hoả):

android-kotlin-fun-mars-server.appspot.com

Bộ định vị tài nguyên thống nhất (Uniform Resource Locator – URL) là một URI chỉ định phương thức hành động hay yêu cầu biểu diễn tài nguyên, tức là xác định cả cơ chế truy cập chính và vị trí mạng.

Ví dụ:

URL sau đây liệt kê danh sách tất cả tài sản bất động sản hiện có trên sao Hoả!

https://android-kotlin-fun-mars-server.appspot.com/realestate

URL sau đây liệt kê danh sách ảnh sao Hoả:

https://android-kotlin-fun-mars-server.appspot.com/photos

Các URL này tham chiếu đến một tài nguyên đã xác định (chẳng hạn như /realestate hoặc /photos) có thể truy cập được bằng Giao thức truyền siêu văn bản (http: – Hypertext Transfer Protocol) qua mạng. Bạn sẽ dùng điểm cuối /photos trong lớp học lập trình này.

Yêu cầu dịch vụ web

Mỗi yêu cầu dịch vụ web chứa một URI và được chuyển tới máy chủ bằng chính giao thức HTTP mà các trình duyệt web (như Chrome) sử dụng. Các yêu cầu HTTP chứa một thao tác để cho máy chủ biết cần làm gì.

Có thể kể đến một số thao tác HTTP phổ biến như sau:

  • GET để truy xuất dữ liệu máy chủ
  • POST hoặc PUT để thêm/tạo/cập nhật máy chủ bằng dữ liệu mới
  • DELETE để xoá dữ liệu khỏi máy chủ

Ứng dụng của bạn sẽ gửi một yêu cầu HTTP GET tới máy chủ về việc cung cấp thông tin liên quan đến ảnh sao Hoả, sau đó máy chủ trả về phản hồi cho ứng dụng, bao gồm cả URL hình ảnh.

bcd50e389186fa98.png

1e08dbc82558a7cd.png

Phản hồi của một dịch vụ web thường được định dạng bằng một trong các định dạng web phổ biến như XML hoặc JSON, dùng để trình bày dữ liệu có cấu trúc trong các cặp khoá-giá trị. Bạn có thể tìm hiểu thêm về JSON trong một nhiệm vụ sắp tới.

Trong nhiệm vụ này, bạn thiết lập kết nối mạng với máy chủ, giao tiếp với máy chủ và nhận phản hồi JSON. Bạn sẽ sử dụng một máy chủ phụ trợ đã được viết sẵn cho bạn. Trong lớp học lập trình này, bạn sẽ dùng thư viện Retrofit, một thư viện bên thứ ba để giao tiếp với máy chủ phụ trợ.

Thư viện bên ngoài

Thư viện bên ngoài hoặc thư viện bên thứ ba giống như phần mở rộng cho các API Android cốt lõi. Các thư viện này chủ yếu là nguồn mở, do cộng đồng phát triển và duy trì nhờ đóng góp tập thể của cộng đồng Android lớn mạnh trên toàn thế giới. Các thư viện này giúp các nhà phát triển Android như bạn xây dựng ứng dụng tốt hơn.

Thư viện Retrofit

Thư viện Retrofit mà bạn sắp sử dụng trong lớp học lập trình này để trao đổi với dịch vụ web RESTful sao Hoả là một ví dụ điển hình về thư viện được hỗ trợ và duy trì tốt. Bạn có thể nhận thấy điều này bằng cách xem trang GitHub của thư viện, kiểm tra các vấn đề còn tồn đọng (một vài trong số đó là yêu cầu về tính năng) và các vấn đề đã đóng. Nếu các nhà phát triển thường xuyên giải quyết những vấn đề này và phản hồi yêu cầu về tính năng, có nghĩa thư viện được duy trì tốt và là một ứng viên phù hợp để dùng trong ứng dụng. Những nhà phát triển này còn soạn cả trang tài liệu Retrofit để giúp bạn tìm hiểu thêm.

Thư viện Retrofit sẽ giao tiếp với phần phụ trợ. Thư viện này tạo URI cho dịch vụ web dựa trên các tham số mà chúng ta truyền vào. Bạn sẽ biết thêm thông tin về việc này trong các phần sau.

a8f10b735ad998ac.png

Thêm phần phụ thuộc Retrofit

Android Gradle cho phép bạn thêm các thư viện bên ngoài vào dự án của mình. Ngoài phần phụ thuộc thư viện, bạn cũng cần đưa vào kho lưu trữ nơi lưu trữ thư viện. Các thư viện của Google như ViewModelLiveData trong thư viện Jetpack được lưu trữ trong kho lưu trữ của Google. Phần lớn thư viện cộng đồng như Retrofit đều được lưu trữ trên kho lưu trữ Google và MavenCentral.

  1. Mở tệp build.gradle(Project: MarsPhotos) cấp cao nhất của dự án. Lưu ý các kho lưu trữ được liệt kê trong khối repositories. Bạn sẽ thấy hai kho lưu trữ, google()mavenCentral().
repositories {
   google()
   mavenCentral()
}
  1. Mở tệp gradle cấp mô-đun, build.gradle (Module: MarsPhots.app).
  2. Trong phần dependencies, hãy thêm các dòng sau cho các thư viện của Retrofit:
// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
// Retrofit with Scalar Converter
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"

Phần phụ thuộc đầu tiên là dành cho chính thư viện Retrofit2 và phần phụ thuộc thứ hai là dành cho bộ chuyển đổi vô hướng Retrofit (Retrofit scalar converter). Bộ chuyển đổi này cho phép Retrofit trả về kết quả JSON dưới dạng String. Cả hai thư viện hoạt động cùng nhau.

  1. Nhấp vào Sync Now (Đồng bộ hoá ngay) để xây dựng lại dự án với phần phụ thuộc mới.

Thêm khả năng hỗ trợ các tính năng ngôn ngữ Java 8

Nhiều thư viện của bên thứ ba (bao gồm cả Retrofit2) sử dụng các tính năng ngôn ngữ Java 8. Trình bổ trợ Android cho Gradle cung cấp khả năng hỗ trợ tích hợp để sử dụng một số tính năng ngôn ngữ Java 8 nhất định.

  1. Để sử dụng các tính năng tích hợp sẵn, bạn cần có mã sau trong tệp build.gradle của mô-đun. Bước này đã được thực hiện cho bạn, hãy đảm bảo bạn đã có mã sau trong build.gradle(Module: MarsPhotos.app).
android {
  ...

  compileOptions {
    sourceCompatibility JavaVersion.VERSION_1_8
    targetCompatibility JavaVersion.VERSION_1_8
  }

  kotlinOptions {
    jvmTarget = '1.8'
  }
}

6. Kết nối với Internet

Bạn sẽ sử dụng thư viện Retrofit để trao đổi với dịch vụ web sao Hoả và hiện phản hồi JSON thô dưới dạng String. Phần giữ chỗ TextView sẽ cho thấy chuỗi phản hồi JSON được trả về hoặc thông báo lỗi kết nối.

Retrofit tạo một API mạng cho ứng dụng dựa trên nội dung từ dịch vụ web. Retrofit tìm nạp dữ liệu qua dịch vụ web và định tuyến dữ liệu này thông qua một thư viện chuyển đổi riêng biệt biết cách giải mã và trả lại dữ liệu dưới dạng các đối tượng như String. Retrofit có tính năng hỗ trợ tích hợp cho các định dạng dữ liệu phổ biến như XML và JSON. Cuối cùng, Retrofit sẽ tạo mã để gọi và sử dụng dịch vụ này cho bạn, bao gồm cả các thông tin chi tiết quan trọng như chạy yêu cầu trên các luồng ở chế độ nền.

deb437805232f6a.png

Trong nhiệm vụ này, bạn sẽ thêm một lớp mạng vào dự án MarsPhotosViewModel sẽ sử dụng để giao tiếp với dịch vụ web. Bạn sẽ triển khai API của dịch vụ Retrofit, qua các bước sau.

  • Tạo một lớp mạng (network layer), lớp (class) MarsApiService.
  • Tạo đối tượng Retrofit với URL cơ sở và nhà máy chuyển đổi (converter factory).
  • Tạo một giao diện giải thích cách Retrofit trao đổi với máy chủ web của chúng ta.
  • Tạo dịch vụ Retrofit và hiện thực thể của dịch vụ API cho phần còn lại của ứng dụng.

Triển khai các bước trên:

  1. Tạo một gói mới có tên là network. Trong ngăn dự án Android, hãy nhấp chuột phải vào gói đó, com.example.android.marsphotos. Chọn New (Mới) > Package (Gói). Trong cửa sổ bật lên, hãy nối thêm network vào cuối tên gói được đề xuất.
  2. Tạo một tệp Kotlin mới trong network của gói mới. Đặt tên cho tệp là MarsApiService.
  3. Mở network/MarsApiService.kt. Thêm hằng số sau đây cho URL cơ sở dành cho dịch vụ web.
private const val BASE_URL =
   "https://android-kotlin-fun-mars-server.appspot.com"
  1. Ngay bên dưới hằng số đó, hãy thêm trình tạo Retrofit để tạo và xây dựng một đối tượng Retrofit.
private val retrofit = Retrofit.Builder()

Nhập retrofit2.Retrofit, khi được nhắc.

  1. Retrofit cần URI cơ sở dành cho dịch vụ web và nhà máy chuyển đổi để xây dựng API dịch vụ web. Bộ chuyển đổi sẽ cho Retrofit biết cần làm gì với dữ liệu nhận được qua dịch vụ web. Trong trường hợp này, bạn muốn Retrofit tìm nạp phản hồi JSON qua dịch vụ web và trả về dưới dạng String. Retrofit có một ScalarsConverter hỗ trợ chuỗi và các kiểu dữ liệu chính khác, vì vậy bạn có thể gọi addConverterFactory() trên trình tạo bằng một thực thể của ScalarsConverterFactory.
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())

Nhập retrofit2.converter.scalars.ScalarsConverterFactory khi được nhắc.

  1. Thêm URI cơ sở cho dịch vụ web bằng cách sử dụng phương thức baseUrl(). Cuối cùng, hãy gọi build() để tạo đối tượng Retrofit.
private val retrofit = Retrofit.Builder()
   .addConverterFactory(ScalarsConverterFactory.create())
   .baseUrl(BASE_URL)
   .build()
  1. Bên dưới lệnh gọi tới trình tạo Retrofit, hãy định nghĩa một giao diện có tên là MarsApiService. Giao diện này xác định cách Retrofit giao tiếp với máy chủ web bằng cách sử dụng các yêu cầu HTTP.
interface MarsApiService {
}
  1. Bên trong giao diện MarsApiService, hãy thêm một hàm có tên là getPhotos() để nhận chuỗi phản hồi qua dịch vụ web.
interface MarsApiService {
    fun getPhotos()
}
  1. Sử dụng chú thích @GET để cho Retrofit biết rằng đây là yêu cầu GET và chỉ định điểm cuối cho phương thức dịch vụ web đó. Trong trường hợp này, điểm cuối có tên là photos. Như đã đề cập trong nhiệm vụ trước, bạn sẽ sử dụng điểm cuối /photos trong lớp học lập trình này.
interface MarsApiService {
    @GET("photos")
    fun getPhotos()
}

Nhập retrofit2.http.GET khi có yêu cầu.

  1. Khi phương thức getPhotos() được gọi, Retrofit sẽ thêm điểm cuối photos vào URL cơ sở (mà bạn đã khai báo trong trình tạo Retrofit) dùng để bắt đầu yêu cầu. Thêm loại dữ liệu trả về của hàm vào String.
interface MarsApiService {
    @GET("photos")
    fun getPhotos(): String
}

Khai báo đối tượng

Trong Kotlin, phần khai báo đối tượng được dùng để khai báo các đối tượng singleton. Mẫu singleton đảm bảo tạo một và chỉ một thực thể của đối tượng và thực thể này sẽ có một điểm truy cập toàn cục đến đối tượng đó. Hoạt động khởi chạy của quá trình khai báo đối tượng sẽ an toàn theo luồng và được thực hiện ở lần truy cập đầu tiên.

Kotlin giúp bạn dễ dàng khai báo các singleton. Sau đây là ví dụ về nội dung khai báo cũng như quyền truy cập của đối tượng. Nội dung khai báo đối tượng luôn có tên đứng sau từ khoá object.

Ví dụ:

// Object declaration
object DataProviderManager {
    fun registerDataProvider(provider: DataProvider) {
        // ...
    }
​
    val allDataProviders: Collection<DataProvider>
        get() = // ...
}

// To refer to the object, use its name directly.
DataProviderManager.registerDataProvider(...)

Lệnh gọi hàm Create() trên đối tượng Retrofit rất tốn kém và ứng dụng chỉ cần duy nhất một thực thể của dịch vụ API Retrofit. Vì vậy, bạn sẽ cho thấy dịch vụ ở các phần còn lại của ứng dụng bằng cách khai báo đối tượng.

  1. Bên ngoài phần khai báo giao diện MarsApiService, hãy khai báo một đối tượng công khai (public) có tên là MarsApi để khởi chạy dịch vụ Retrofit. Đây là đối tượng singleton công khai có thể truy cập được qua phần còn lại của ứng dụng.
object MarsApi {

}
  1. Bên trong phần khai báo đối tượng MarsApi, thêm một thuộc tính đối tượng Retrofit khởi chạy từng phần (lazy initialize) với tên gọi là retrofitService thuộc kiểu MarsApiService. Bạn thực hiện việc khởi chạy từng phần này để đảm bảo khởi chạy thành công trong lần sử dụng đầu tiên. Bạn sẽ khắc phục lỗi trong các bước tiếp theo.
object MarsApi {
    val retrofitService : MarsApiService by lazy {
       }
}
  1. Khởi chạy biến retrofitService bằng phương thức retrofit.create() với giao diện MarsApiService.
object MarsApi {
    val retrofitService : MarsApiService by lazy {
       retrofit.create(MarsApiService::class.java) }
}

Đã thiết lập xong Retrofit! Mỗi khi ứng dụng của bạn gọi MarsApi.retrofitService, phương thức gọi sẽ truy cập vào chính đối tượng singleton Retrofit triển khai MarsApiService được tạo trong lần truy cập đầu tiên. Trong nhiệm vụ tiếp theo, bạn sẽ sử dụng đối tượng Retrofit mà bạn đã triển khai.

Gọi dịch vụ web trong OverviewViewModel

Ở bước này, bạn sẽ triển khai phương thức getMarsPhotos() gọi dịch vụ Retrofit, sau đó xử lý chuỗi JSON trả về.

ViewModelScope

ViewModelScope là phạm vi coroutine tích hợp sẵn được khai báo cho mỗi ViewModel trong ứng dụng. Mọi coroutine chạy trong phạm vi này sẽ tự động bị huỷ nếu ViewModel bị xoá.

Bạn sẽ sử dụng ViewModelScope để chạy coroutine và thực hiện lệnh gọi mạng Retrofit ở chế độ nền.

  1. Trong MarsApiService, hãy đặt getPhotos() làm một hàm tạm ngưng (suspend function). Nhờ vậy bạn có thể gọi phương thức này từ bên trong coroutine.
@GET("photos")
suspend fun getPhotos(): String
  1. Mở overview/OverviewViewModel. Cuộn xuống phương thức getMarsPhotos(). Xoá dòng thiết lập phản hồi trạng thái thành "Set the Mars API Response here!". Phương thức getMarsPhotos() hiện đang trống.
private fun getMarsPhotos() {

}
  1. Bên trong getMarsPhotos(), hãy mở coroutine bằng viewModelScope.launch.
private fun getMarsPhotos() {
    viewModelScope.launch {
    }
}

Nhập androidx.lifecycle.viewModelScopekotlinx.coroutines.launch khi được nhắc.

  1. Bên trong viewModelScope, hãy sử dụng đối tượng singleton MarsApi, để gọi phương thức getPhotos() qua giao diện retrofitService. Lưu phản hồi được trả về trong một val có tên là listResult.
viewModelScope.launch {
    val listResult = MarsApi.retrofitService.getPhotos()
}

Nhập com.example.android.marsphotos.network.MarsApi khi được nhắc.

  1. Gán kết quả mà chúng tôi vừa nhận được qua máy chủ phụ trợ cho _status.value.
 val listResult = MarsApi.retrofitService.getPhotos()
 _status.value = listResult
  1. Chạy ứng dụng. Lưu ý rằng ứng dụng đóng ngay lập tức và có thể hiện cửa sổ bật lên báo lỗi.
  2. Nhấp vào thẻ Logcat trong Android Studio rồi ghi chú lỗi trong nhật ký, bắt đầu bằng một dòng như thế này, "------- beginning of crash"
    --------- beginning of crash
22803-22865/com.example.android.marsphotos E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
    Process: com.example.android.marsphotos, PID: 22803
    java.lang.SecurityException: Permission denied (missing INTERNET permission?)
...

Thông báo lỗi này cho biết có thể ứng dụng bị thiếu các quyền INTERNET. Bạn sẽ giải quyết vấn đề này bằng cách thêm quyền truy cập Internet cho ứng dụng trong nhiệm vụ tiếp theo.

7. Thêm quyền Internet và xử lý ngoại lệ

Quyền trên Android

Mục đích của các quyền (permission) trên Android là bảo vệ quyền riêng tư của người dùng Android. Ứng dụng Android phải khai báo hoặc yêu cầu cấp quyền nếu muốn truy cập vào dữ liệu nhạy cảm của người dùng, chẳng hạn như danh bạ, nhật ký cuộc gọi và một số tính năng nhất định của hệ thống, chẳng hạn như máy ảnh hoặc Internet.

Để có thể truy cập Internet, ứng dụng của bạn cần có quyền INTERNET. Kết nối Internet dẫn đến nhiều mối lo ngại về bảo mật, do đó, các ứng dụng đều không có kết nối Internet theo mặc định. Bạn cần phải khai báo rõ ràng rằng ứng dụng cần truy cập Internet. Đây được coi là một quyền thông thường. Để tìm hiểu thêm về các quyền trên Android cũng như các loại quyền, vui lòng tham khảo tài liệu này.

Ở bước này, ứng dụng của bạn khai báo (các) quyền mà ứng dụng cần bằng cách đưa các thẻ <uses-permission> vào tệp AndroidManifest.

  1. Mở manifests/AndroidManifest.xml. Thêm dòng này ngay trước thẻ <application>:
<uses-permission android:name="android.permission.INTERNET" />
  1. Biên dịch và chạy lại ứng dụng. Nếu có kết nối Internet đang hoạt động, bạn sẽ thấy văn bản JSON chứa dữ liệu liên quan đến các bức ảnh sao Hoả. Bạn sẽ tìm hiểu thêm về định dạng JSON ở một phần sau trong lớp học lập trình này.

205710014543679a.png

  1. Nhấn vào nút Back (Quay lại) trong thiết bị hoặc trình mô phỏng để đóng ứng dụng.
  2. Đặt thiết bị hoặc trình mô phỏng của bạn vào chế độ trên máy bay (airplane mode) để mô phỏng lỗi kết nối mạng. Mở lại ứng dụng trong trình đơn gần đây (recents) hoặc khởi động lại ứng dụng trong Android Studio.
  3. Nhấp vào thẻ Logcat trong Android Studio và ghi chú về trường hợp ngoại lệ nghiêm trọng trong nhật ký như sau:
3302-3302/com.example.android.marsphotos E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.android.marsphotos, PID: 3302
    java.net.SocketTimeoutException: timeout
...

Thông báo lỗi này cho biết ứng dụng đã cố gắng kết nối nhưng hết thời gian chờ. Những ngoại lệ như thế này rất phổ biến trong thời gian thực. Trong bước tiếp theo, bạn sẽ tìm hiểu cách xử lý các trường hợp ngoại lệ như vậy.

Xử lý ngoại lệ

Ngoại lệ (Exception) là các lỗi có thể xảy ra trong thời gian chạy (không phải thời gian biên dịch) và làm ứng dụng chấm dứt đột ngột mà không thông báo cho người dùng. Tình trạng này có thể đem lại trải nghiệm không tốt cho người dùng. Xử lý ngoại lệ là một cơ chế giúp bạn ngăn chặn tình trạng ứng dụng chấm dứt đột ngột và xử lý theo cách thân thiện với người dùng.

Lý do của các ngoại lệ có thể đơn giản như phép chia cho không hoặc lỗi kết nối mạng. Các ngoại lệ này tương tự như NumberFormatException mà bạn từng tìm hiểu trong một lớp học lập trình trước đây.

Ví dụ về một số vấn đề có thể xảy ra khi kết nối với máy chủ:

  • URL hoặc URI sử dụng trong API không chính xác.
  • Máy chủ không truy cập được và ứng dụng không kết nối được với máy chủ đó.
  • Vấn đề về độ trễ mạng.
  • Kết nối Internet kém hoặc không có kết nối Internet trên thiết bị.

Bạn không thể phát hiện các ngoại lệ này trong quá trình biên dịch. Bạn có thể sử dụng một khối try-catch để xử lý ngoại lệ trong thời gian chạy. Để tìm hiểu thêm, vui lòng tham khảo tài liệu.

Cú pháp mẫu cho khối try-catch

try {
    // some code that can cause an exception.
}
catch (e: SomeException) {
    // handle the exception to avoid abrupt termination.
}

Trong khối try, bạn thực hiện mã này trong trường hợp mà bạn dự đoán là sẽ xảy ra ngoại lệ. Trong ứng dụng của bạn, đây sẽ là một lệnh gọi mạng. Trong khối catch, bạn sẽ triển khai mã ngăn chặn tình trạng chấm dứt ứng dụng đột ngột. Nếu có một ngoại lệ thì khối catch sẽ được thực thi để khôi phục lỗi thay vì chấm dứt ứng dụng đột ngột.

  1. Mở overview/OverviewViewModel.kt. Cuộn xuống phương thức getMarsPhotos(). Bên trong khối khởi chạy, hãy thêm một khối try xung quanh lệnh gọi MarsApi để xử lý các ngoại lệ. Thêm khối catch sau khối try:
viewModelScope.launch {
   try {
       val listResult = MarsApi.retrofitService.getPhotos()
       _status.value = listResult
   } catch (e: Exception) {

   }
}
  1. Bên trong khối catch {}, hãy xử lý phản hồi lỗi. Hiện thông báo lỗi cho người dùng bằng cách đặt e.message thành _status.value.
catch (e: Exception) {
   _status.value = "Failure: ${e.message}"
}
  1. Chạy lại ứng dụng khi đang bật chế độ trên máy bay. Lần này ứng dụng không đóng đột ngột mà hiện một thông báo lỗi.

368c746124f57a93.png

  1. Tắt chế độ trên máy bay trên điện thoại hoặc trình mô phỏng. Chạy và kiểm thử ứng dụng, đảm bảo rằng mọi thứ đều hoạt động tốt và bạn thấy được chuỗi JSON.

8. Phân tích cú pháp phản hồi JSON bằng Moshi

JSON

Dữ liệu yêu cầu thường được định dạng theo một trong những định dạng dữ liệu phổ biến như XML hoặc JSON. Mỗi lệnh gọi đều trả về dữ liệu có cấu trúc và ứng dụng của bạn cần biết cấu trúc đó là gì để đọc được dữ liệu trong phản hồi.

Ví dụ: trong ứng dụng này, bạn sẽ truy xuất dữ liệu qua máy chủ này: https:// android-kotlin-fun-mars-server.appspot.com/photos. Nếu nhập URL này vào trình duyệt, bạn sẽ thấy danh sách mã và URL ảnh bề mặt sao Hoả ở định dạng JSON!

Cấu trúc của phản hồi JSON mẫu:

fde4f6f199990ae8.png

  • Phản hồi JSON là một mảng (array) được biểu thị bằng các dấu ngoặc vuông. Mảng này chứa các đối tượng JSON.
  • Các đối tượng JSON được đặt trong các dấu ngoặc nhọn.
  • Mỗi đối tượng JSON chứa một tập hợp cặp tên-giá trị được phân tách bằng dấu phẩy.
  • Tên và giá trị trong một cặp được phân tách bằng dấu hai chấm.
  • Tên được đặt trong dấu ngoặc kép.
  • Giá trị có thể là số, chuỗi (string), boolean, mảng (array), đối tượng (đối tượng JSON) hoặc giá trị rỗng (null).

Ví dụ: img_src là một URL và đó cũng là một chuỗi. Nếu dán URL vào một trình duyệt web, bạn sẽ thấy hình ảnh bề mặt sao Hoả.

b4f9f196c64f02c3.png

Lúc này, bạn đã nhận được phản hồi JSON qua dịch vụ web sao Hoả. Đây là một khởi đầu tuyệt vời. Nhưng những gì bạn thực sự cần là các đối tượng Kotlin, không phải là một chuỗi JSON lớn. Có một thư viện bên ngoài có tên Moshi, là một trình phân tích cú pháp JSON Android để chuyển đổi chuỗi JSON thành các đối tượng Kotlin. Retrofit có một bộ chuyển đổi dùng được với Moshi, vì vậy đây là thư viện tuyệt vời dành cho mục đích hiện tại của bạn.

Trong nhiệm vụ này, bạn sử dụng thư viện Moshi cùng Retrofit để phân tích cú pháp phản hồi JSON qua dịch vụ web thành các đối tượng Kotlin hữu ích biểu thị ảnh sao Hoả. Bạn sẽ thay đổi ứng dụng để hiện số lượng ảnh sao Hoả được trả về (thay vì cho thấy JSON thô).

Thêm phần phụ thuộc của thư viện Moshi

  1. Mở build.gradle (Module: app).
  2. Trong phần phụ thuộc, hãy thêm mã dưới đây để thêm phần phụ thuộc Moshi. Phần phụ thuộc này hỗ trợ thêm cho thư viện Moshi JSON có hỗ trợ Kotlin.
// Moshi
implementation 'com.squareup.moshi:moshi-kotlin:1.13.0'
  1. Xác định vị trí các dòng dành cho bộ chuyển đổi vô hướng Retrofit trong khối dependencies và thay đổi các phần phụ thuộc này để sử dụng converter-moshi:

Thay thế đoạn mã này

// Retrofit
implementation "com.squareup.retrofit2:retrofit:2.9.0"
// Retrofit with scalar Converter
implementation "com.squareup.retrofit2:converter-scalars:2.9.0"

bằng đoạn mã này

// Retrofit with Moshi Converter
implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'
  1. Nhấp vào Sync Now (Đồng bộ hoá ngay) để xây dựng lại dự án với phần phụ thuộc mới.

Triển khai lớp dữ liệu Mars Photos

Mục mẫu của phản hồi JSON mà bạn nhận được qua dịch vụ web có dạng như sau, tương tự như những gì bạn từng thấy:

[{
    "id":"424906",
    "img_src":"http://mars.jpl.nasa.gov/msl-raw-images/msss/01000/mcam/1000ML0044631300305227E03_DXXX.jpg"
},
...]

Trong ví dụ trên, lưu ý rằng mỗi mục ảnh sao Hoả có các cặp giá trị và khoá JSON như sau:

  • id: mã nhận dạng thuộc tính ở dạng chuỗi. Vì được bao bọc (wrap) trong " ", thuộc tính này thuộc loại String chứ không phải Integer.
  • img_src: URL của hình ảnh dưới dạng chuỗi.

Moshi phân tích cú pháp dữ liệu JSON này và chuyển đổi thành các đối tượng Kotlin. Để làm điều này, Moshi cần có một lớp dữ liệu Kotlin để lưu trữ các kết quả đã phân tích cú pháp. Do đó, ở bước này, bạn sẽ tạo lớp dữ liệu MarsPhoto.

  1. Nhấp chuột phải vào gói network (mạng) rồi chọn New (Mới) > Kotlin File/Class (Lớp/tệp Kotlin).
  2. Trong cửa sổ bật lên, chọn Class (Lớp) rồi nhập MarsPhoto làm tên của lớp đó. Thao tác này sẽ tạo một tệp mới có tên là MarsPhoto.kt trong gói network.
  3. Đặt MarsPhoto làm lớp dữ liệu bằng cách thêm từ khoá data trước phần định nghĩa lớp. Thay đổi các dấu ngoặc nhọn {} thành dấu ngoặc tròn (). Bạn sẽ thấy lỗi xuất hiện vì phải có ít nhất một thuộc tính được định nghĩa trong một lớp dữ liệu.
data class MarsPhoto(
)
  1. Thêm các thuộc tính sau vào phần định nghĩa lớp MarsPhoto.
data class MarsPhoto(
   val id: String, val img_src: String
)

Hãy lưu ý rằng mỗi biến trong lớp MarsPhoto tương ứng với một tên khoá trong đối tượng JSON. Để khớp với các kiểu trong phản hồi JSON cụ thể, bạn sử dụng đối tượng String cho mọi giá trị.

Khi phân tích cú pháp JSON, Moshi sẽ so khớp các khoá theo tên rồi điền thông tin thích hợp vào đối tượng dữ liệu.

Chú thích @Json

Đôi khi tên khoá trong phản hồi JSON có thể làm cho các thuộc tính Kotlin nhầm lẫn hoặc không khớp với kiểu mã hoá đề xuất. Ví dụ: trong tệp JSON, khoá img_src sử dụng dấu gạch dưới, trong khi quy ước Kotlin cho các thuộc tính sử dụng chữ hoa và chữ thường ("kiểu lạc đà").

Để sử dụng tên biến trong lớp dữ liệu khác với tên khoá trong phản hồi JSON, hãy sử dụng chú thích @Json. Trong ví dụ này, tên của biến trong lớp dữ liệu là imgSrcUrl. Bạn có thể liên kết biến này với thuộc tính JSON img_src bằng @Json(name = "img_src").

  1. Thay thế dòng dành cho khoá img_src bằng dòng dưới đây. Nhập com.squareup.moshi.Json khi có yêu cầu.
@Json(name = "img_src") val imgSrcUrl: String

Cập nhật MarsApiService và OverviewViewModel

Trong nhiệm vụ này, bạn sẽ tạo một đối tượng Moshi bằng cách sử dụng Moshi Builder (trình tạo Moshi), tương tự như trình tạo Retrofit.

Bạn sẽ thay thế ScalarsConverterFactory bằng KotlinJsonAdapterFactory để cho Retrofit biết rằng ứng dụng có thể sử dụng Moshi để chuyển đổi phản hồi JSON thành đối tượng Kotlin. Sau đó, bạn sẽ cập nhật API mạng và ViewModel để sử dụng đối tượng Moshi.

  1. Mở network/MarsApiService.kt. Hãy lưu ý những lỗi tham chiếu chưa được giải quyết cho ScalarsConverterFactory. Lý do chính là sự thay đổi về phần phụ thuộc Retrofit mà bạn thực hiện ở bước trước. Xoá dữ liệu đã nhập cho ScalarConverterFactory. Bạn sẽ sớm khắc phục được lỗi còn lại.

Xoá:

import retrofit2.converter.scalars.ScalarsConverterFactory
  1. Ở đầu tệp, ngay trước trình tạo Retrofit, hãy thêm mã sau để tạo đối tượng Moshi, tương tự như đối tượng Retrofit.
private val moshi = Moshi.Builder()

Nhập com.squareup.moshi.Moshicom.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory khi được yêu cầu.

  1. Để chú thích của Moshi dùng được với Kotlin, trong trình tạo Moshi, hãy thêm KotlinJsonAdapterFactory, sau đó gọi build().
private val moshi = Moshi.Builder()
   .add(KotlinJsonAdapterFactory())
   .build()
  1. Trong phần khai báo đối tượng retrofit, hãy thay đổi trình tạo Retrofit để dùng MoshiConverterFactory thay vì ScalarConverterFactory rồi truyền vào thực thể moshi mà bạn vừa tạo.
private val retrofit = Retrofit.Builder()
   .addConverterFactory(MoshiConverterFactory.create(moshi))
   .baseUrl(BASE_URL)
   .build()

Nhập retrofit2.converter.moshi.MoshiConverterFactory khi có yêu cầu.

  1. Giờ khi đã có MoshiConverterFactory, bạn có thể yêu cầu Retrofit trả về danh sách đối tượng MarsPhoto qua mảng JSON thay vì trả về một chuỗi JSON. Hãy cập nhật giao diện MarsApiService để Retrofit trả về một danh sách đối tượng MarsPhoto, thay vì trả về String.
interface MarsApiService {
   @GET("photos")
   suspend fun getPhotos(): List<MarsPhoto>
}
  1. Thực hiện các thay đổi tương tự đối với viewModel, mở OverviewViewModel.kt. Cuộn xuống phương thức getMarsPhotos().
  2. Trong phương thức getMarsPhotos(), listResult sẽ là List<MarsPhoto> chứ không phải String nữa. Kích thước của danh sách đó là số lượng ảnh đã nhận và phân tích cú pháp. Để in số lượng ảnh đã truy xuất, hãy cập nhật _status.value như sau.
_status.value = "Success: ${listResult.size} Mars photos retrieved"

Nhập com.example.android.marsphotos.network.MarsPhoto khi được nhắc.

  1. Đảm bảo rằng bạn đã tắt chế độ trên máy bay trong trình mô phỏng hoặc thiết bị. Biên dịch và chạy ứng dụng. Lần này thông báo sẽ cho thấy số lượng thuộc tính được trả về qua dịch vụ web chứ không phải là một chuỗi JSON lớn:

8f47f004c7f91394.png

9. Mã giải pháp

build.gradle(Module: MarsPhotos.app)

Đây là các phần phụ thuộc mới cần đưa vào.

dependencies {
    ...
    // Moshi
    implementation 'com.squareup.moshi:moshi-kotlin:1.13.0'

    // Retrofit with Moshi Converter
    implementation 'com.squareup.retrofit2:converter-moshi:2.9.0'

    ...
}

Manifests/AndroidManifest.xml

Thêm quyền truy cập Internet, mã <uses-permission..> trong đoạn mã dưới đây.

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.android.marsphotos">

    <!-- In order for our app to access the Internet, we need to define this permission. -->
    <uses-permission android:name="android.permission.INTERNET" />

    <application
        ...
    </application>

</manifest>

network/MarsPhoto.kt

package com.example.android.marsphotos.network

import com.squareup.moshi.Json

/**
* This data class defines a Mars photo which includes an ID, and the image URL.
* The property names of this data class are used by Moshi to match the names of values in JSON.
*/
data class MarsPhoto(
   val id: String,
   @Json(name = "img_src") val imgSrcUrl: String
)

network/MarsApiService.kt

package com.example.android.marsphotos.network

import com.squareup.moshi.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
import retrofit2.http.GET

private const val BASE_URL =
   "https://android-kotlin-fun-mars-server.appspot.com"

/**
* Build the Moshi object with Kotlin adapter factory that Retrofit will be using.
*/
private val moshi = Moshi.Builder()
   .add(KotlinJsonAdapterFactory())
   .build()

/**
* The Retrofit object with the Moshi converter.
*/
private val retrofit = Retrofit.Builder()
   .addConverterFactory(MoshiConverterFactory.create(moshi))
   .baseUrl(BASE_URL)
   .build()

/**
* A public interface that exposes the [getPhotos] method
*/
interface MarsApiService {
   /**
    * Returns a [List] of [MarsPhoto] and this method can be called from a Coroutine.
    * The @GET annotation indicates that the "photos" endpoint will be requested with the GET
    * HTTP method
    */
   @GET("photos")
   suspend fun getPhotos() : List<MarsPhoto>
}

/**
* A public Api object that exposes the lazy-initialized Retrofit service
*/
object MarsApi {
   val retrofitService: MarsApiService by lazy { retrofit.create(MarsApiService::class.java) }
}

Overview/OverviewViewModel.kt

package com.example.android.marsphotos.overview

import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.example.android.marsphotos.network.MarsApi
import kotlinx.coroutines.launch

/**
* The [ViewModel] that is attached to the [OverviewFragment].
*/
class OverviewViewModel : ViewModel() {

   // The internal MutableLiveData that stores the status of the most recent request
   private val _status = MutableLiveData<String>()

   // The external immutable LiveData for the request status
   val status: LiveData<String> = _status
   /**
    * Call getMarsPhotos() on init so we can display status immediately.
    */
   init {
       getMarsPhotos()
   }

   /**
    * Gets Mars photos information from the Mars API Retrofit service and updates the
    * [MarsPhoto] [List] [LiveData].
    */
   private fun getMarsPhotos() {
       viewModelScope.launch {
           try {
               val listResult = MarsApi.retrofitService.getPhotos()
               _status.value = "Success: ${listResult.size} Mars photos retrieved"
           } catch (e: Exception) {
               _status.value = "Failure: ${e.message}"
           }
       }
   }
}

10. Tóm tắt

Dịch vụ web REST

  • Dịch vụ web là chức năng dựa trên phần mềm được cung cấp qua Internet nhằm giúp ứng dụng của bạn đưa ra yêu cầu và nhận về dữ liệu.
  • Các dịch vụ web phổ biến sử dụng kiến trúc REST. Các dịch vụ web cung cấp kiến trúc REST được gọi là dịch vụ RESTful. Các dịch vụ web RESTful được xây dựng bằng các giao thức và thành phần web tiêu chuẩn.
  • Thông qua URI, bạn sẽ đưa ra yêu cầu cho một dịch vụ web REST theo cách chuẩn hoá.
  • Để sử dụng dịch vụ web, ứng dụng phải thiết lập một kết nối mạng và kết nối với dịch vụ này. Sau đó, ứng dụng phải nhận và phân tích cú pháp dữ liệu phản hồi thành định dạng mà ứng dụng sử dụng được.
  • Thư viện Retrofit là một thư viện ứng dụng khách (client library) cho phép ứng dụng của bạn đưa ra yêu cầu đến một dịch vụ web REST.
  • Sử dụng các bộ chuyển đổi để cho Retrofit biết cần làm gì với dữ liệu mà ứng dụng gửi đến và nhận về qua dịch vụ web. Ví dụ: trình chuyển đổi ScalarsConverter coi dữ liệu dịch vụ web là String hoặc một kiểu dữ liệu gốc khác.
  • Để cho phép ứng dụng tạo kết nối Internet, hãy thêm quyền "android.permission.INTERNET" vào tệp kê khai Android.

Phân tích cú pháp JSON

  • Phản hồi qua dịch vụ web thường có định dạng JSON, một định dạng phổ biến để trình bày dữ liệu có cấu trúc.
  • Mỗi đối tượng JSON là một tập hợp cặp khoá-giá trị.
  • Một tập hợp đối tượng JSON là một mảng JSON. Bạn nhận được một mảng JSON dưới dạng phản hồi qua một dịch vụ web.
  • Khoá trong cặp khoá-giá trị được đặt trong dấu ngoặc kép. Giá trị có thể là số hoặc chuỗi.
  • Thư viện Moshi là một trình phân tích cú pháp JSON cho Android để chuyển đổi chuỗi JSON thành các đối tượng Kotlin. Retrofit có một bộ chuyển đổi dành cho Moshi.
  • Moshi đối sánh các khoá trong phản hồi JSON với các thuộc tính trong đối tượng dữ liệu cùng tên.
  • Để sử dụng tên thuộc tính khác cho một khoá, hãy chú thích thuộc tính đó bằng chú thích @Json và tên khoá JSON.

11. Tìm hiểu thêm

Tài liệu dành cho nhà phát triển Android:

Tài liệu về Kotlin:

Khác: