Để thực hiện hoạt động mạng trong ứng dụng của bạn, tệp kê khai phải bao gồm các quyền sau đây:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Các phương pháp hay nhất để giao tiếp an toàn qua mạng
Trước khi thêm chức năng kết nối mạng vào ứng dụng của mình, bạn cần đảm bảo rằng dữ liệu và thông tin trong ứng dụng luôn an toàn khi truyền tải qua một mạng. Để làm vậy, hãy thực hiện theo các phương pháp hay nhất về bảo mật mạng sau đây:
- Giảm thiểu lượng dữ liệu người dùng có tính chất nhạy cảm hoặc cá nhân mà bạn truyền tải qua mạng.
- Gửi tất cả lưu lượng truy cập mạng từ ứng dụng của bạn qua SSL.
- Cân nhắc tạo một cấu hình bảo mật mạng, cho phép ứng dụng của bạn tin tưởng các tổ chức phát hành chứng chỉ (CA) tuỳ chỉnh hoặc hạn chế nhóm CA trong hệ thống mà ứng dụng đó tin tưởng để giao tiếp an toàn.
Để biết thêm thông tin về cách áp dụng các nguyên tắc kết nối mạng an toàn, hãy xem mẹo bảo mật mạng.
Chọn một ứng dụng HTTP
Hầu hết các ứng dụng kết nối mạng đều dùng HTTP để gửi và nhận dữ liệu. Nền tảng
Android có ứng dụng
HttpsURLConnection
.
Ứng dụng này hỗ trợ TLS (Bảo mật tầng truyền tải), tính năng tải lên và tải xuống trực tuyến, thời gian chờ có thể định cấu hình,
IPv6 và vùng kết nối. Trong chủ đề này, chúng tôi dùng Thư viện ứng dụng HTTP Retrofit.
Thư viện này cho phép bạn tạo một ứng dụng HTTP theo cách khai báo. Retrofit cũng hỗ trợ
tự động tuần tự hoá nội dung yêu cầu và huỷ tuần tự hoá nội dung
phản hồi.
Phân giải truy vấn DNS
Các thiết bị chạy Android 10 (API cấp 29) trở lên có dịch vụ hỗ trợ gốc cho hoạt động
tra cứu DNS chuyên dụng thông qua cả tra cứu văn bản thô lẫn chế độ DNS qua TLS.
API DnsResolver
cung cấp hoạt động phân giải chung
không đồng bộ để bạn có thể tra cứu SRV
, NAPTR
và
các loại bản ghi khác. Lưu ý rằng việc phân tích cú pháp phản hồi sẽ do ứng dụng thực hiện.
Trên các thiết bị chạy Android 9 (API cấp 28) trở xuống, trình phân giải
hệ thống tên miền (DNS) của nền tảng chỉ hỗ trợ các bản ghi A
và AAAA
. Việc này cho phép bạn tra cứu địa chỉ
IP liên kết với tên, nhưng không hỗ trợ loại bản ghi nào khác.
Đối với các ứng dụng dựa trên NDK, hãy xem
android_res_nsend
.
Đóng gói hoạt động mạng bằng kho lưu trữ
Để đơn giản hoá quá trình thực hiện hoạt động mạng và giảm hiện tượng trùng lặp mã trong nhiều phần của ứng dụng, bạn có thể dùng mẫu thiết kế kho lưu trữ. Kho lưu trữ là một lớp xử lý hoạt động dữ liệu và cung cấp bản tóm tắt API rõ ràng đối với một số dữ liệu hoặc tài nguyên cụ thể.
Bạn có thể dùng Retrofit để khai báo một giao diện chỉ định phương thức HTTP, URL, đối số và loại phản hồi cho hoạt động mạng, như trong ví dụ sau:
Kotlin
interface UserService { @GET("/users/{id}") suspend fun getUser(@Path("id") id: String): User }
Java
public interface UserService { @GET("/user/{id}") Call<User> getUserById(@Path("id") String id); }
Trong lớp kho lưu trữ, các hàm có thể đóng gói hoạt động mạng và hiển thị kết quả tương ứng. Việc đóng gói này đảm bảo rằng các thành phần gọi kho lưu trữ không cần phải biết cách dữ liệu được lưu trữ. Mọi thay đổi sau này về cách dữ liệu được lưu trữ cũng được tách riêng vào lớp kho lưu trữ.
Kotlin
class UserRepository constructor( private val userService: UserService ) { suspend fun getUserById(id: String): User { return userService.getUser(id) } }
Java
class UserRepository { private UserService userService; public UserRepository( UserService userService ) { this.userService = userService; } public Call<User> getUserById(String id) { return userService.getUser(id); } }
Để tránh tạo giao diện người dùng không phản hồi, đừng thực hiện hoạt động mạng trên
luồng chính. Theo mặc định, Android yêu cầu bạn thực hiện hoạt động mạng trên một luồng
không phải luồng giao diện người dùng chính. Nếu không,
hệ thống sẽ trả về NetworkOnMainThreadException
. Trong lớp UserRepository
minh hoạ ở ví dụ về mã nêu trên,
hoạt động mạng không thực sự được kích hoạt. Lệnh gọi của UserRepository
phải triển khai luồng bằng coroutine hoặc sử dụng hàm
enqueue()
.
Vẫn tồn tại sau khi thay đổi về cấu hình
Khi diễn ra thay đổi về cấu hình, chẳng hạn như xoay màn hình, thì mảnh hoặc hoạt động của bạn sẽ bị huỷ bỏ và tạo lại. Mọi dữ liệu không được lưu trong trạng thái thực thể cho mảnh hoặc hoạt động của bạn (chỉ nên chứa lượng nhỏ dữ liệu) sẽ bị mất và bạn có thể cần gửi lại yêu cầu mạng.
Bạn có thể dùng ViewModel
để đảm bảo
rằng dữ liệu của mình vẫn tồn tại sau khi thay đổi về cấu hình. ViewModel
là một thành phần
được thiết kế nhằm lưu trữ và quản lý dữ liệu liên quan đến giao diện người dùng theo cách
có nhận biết vòng đời. Bằng cách sử dụng UserRepository
được tạo ở trên, ViewModel
có thể gửi các yêu cầu mạng
cần thiết và cung cấp kết quả cho mảnh hoặc hoạt động của bạn
thông qua LiveData
:
Kotlin
class MainViewModel constructor( savedStateHandle: SavedStateHandle, userRepository: UserRepository ) : ViewModel() { private val userId: String = savedStateHandle["uid"] ?: throw IllegalArgumentException("Missing user ID") private val _user = MutableLiveData<User>() val user = _user as LiveData<User> init { viewModelScope.launch { try { // Calling the repository is safe as it will move execution off // the main thread val user = userRepository.getUserById(userId) _user.value = user } catch (error: Exception) { // show error message to user } } } }
Java
class MainViewModel extends ViewModel { private final MutableLiveData<User> _user = new MutableLiveData<>(); LiveData<User> user = (LiveData<User>) _user; public MainViewModel( SavedStateHandle savedStateHandle, UserRepository userRepository ) { String userId = savedStateHandle.get("uid"); Call<User> userCall = userRepository.getUserById(userId); userCall.enqueue(new Callback<User>() { @Override public void onResponse(Call<User> call, Response<User> response) { if (response.isSuccessful()) { _user.setValue(response.body()); } } @Override public void onFailure(Call<User> call, Throwable t) { // show error message to user } }); } }
Đọc hướng dẫn liên quan
Để tìm hiểu thêm về chủ đề này, hãy xem các hướng dẫn liên quan sau đây: