Aby można było wykonywać operacje sieciowe w aplikacji, plik manifestu musi obejmować te uprawnienia:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Sprawdzone metody bezpiecznej komunikacji sieciowej
Zanim dodasz do swojej aplikacji funkcje sieciowe, upewnij się, że dane i informacje w aplikacji są bezpieczne podczas przesyłania przez sieć. W tym celu postępuj zgodnie z tymi sprawdzonymi metodami zapewniania bezpieczeństwa sieci:
- Ogranicz ilość poufnych lub osobistych danych użytkownika przesyłanych przez sieć.
- Wysyłaj cały ruch w sieci z aplikacji przez SSL.
- Rozważ utworzenie konfiguracji zabezpieczeń sieci, która pozwoli aplikacji ufać niestandardowych urzędom certyfikacji (CA) lub ograniczyć zestaw urzędów certyfikacji, którym ufa w ramach bezpiecznej komunikacji.
Więcej informacji o stosowaniu zasad dotyczących bezpiecznych sieci znajdziesz we wskazówkach dotyczących bezpieczeństwa sieci.
Wybierz klienta HTTP
Większość aplikacji połączonych z siecią używa protokołu HTTP do wysyłania i odbierania danych. Platforma Android obejmuje klienta HttpsURLConnection
, który obsługuje protokół TLS, przesyłanie strumieniowe i pobieranie danych, konfigurowalne limity czasu, IPv6 i pula połączeń.
Dostępne są też biblioteki zewnętrzne, które oferują interfejsy API wyższego poziomu do operacji sieciowych. Obsługują one różne udogodnienia, takie jak serializacja treści żądań i deserializacja treści odpowiedzi.
- Retrofit: bezpieczny klient HTTP dla JVM z Square, zbudowany na bazie OkHttp. Funkcja Retrofit umożliwia deklaratywne utworzenie interfejsu klienta i obsługuje kilka bibliotek serializacji.
- Ktor: klient HTTP od JetBrains zaprojektowany w całości dla Kotlin i obsługiwany przez procesory. Ktor obsługuje różne silniki, serializery i platformy.
Rozwiązywanie zapytań DNS
Urządzenia z Androidem 10 (poziom interfejsu API 29) lub nowszym mają wbudowaną obsługę specjalistycznych wyszukiwań DNS z wykorzystaniem zarówno wyszukiwania zwykłego tekstu, jak i trybu DNS-over-TLS.
Interfejs API DnsResolver
zapewnia ogólną, asynchroniczną rozdzielczość, która umożliwia wyszukiwanie rekordów w rekordach SRV
, NAPTR
i innych. Odpowiedź zostanie przetworzona przez aplikację.
Na urządzeniach z Androidem 9 (poziom interfejsu API 28) lub starszym mechanizm rozpoznawania nazw DNS platformy obsługuje tylko rekordy A
i AAAA
. Umożliwia to wyszukiwanie adresów IP powiązanych z daną nazwą, ale nie obsługuje żadnych innych typów rekordów.
Informacje na temat aplikacji opartych na pakiecie NDK znajdziesz tutaj: android_res_nsend
.
Herbaty operacji sieciowych za pomocą repozytorium
Aby uprościć proces operacji sieciowych i ograniczyć duplikowanie kodu w różnych częściach aplikacji, możesz użyć wzorca projektu repozytorium. Repozytorium to klasa, która obsługuje operacje na danych i zapewnia przejrzystą abstrakcję interfejsu API w przypadku niektórych danych lub zasobów.
Możesz użyć funkcji Retrofit, aby zadeklarować interfejs, który określa metodę HTTP, adres URL, argumenty i typ odpowiedzi dla operacji sieciowych, jak w tym przykładzie:
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); }
W klasie repozytorium funkcje mogą ujmować operacje sieciowe i udostępniać ich wyniki. Dzięki temu komponenty, które wywołują repozytorium, nie muszą wiedzieć, jak przechowywane są dane. Wszystkie przyszłe zmiany sposobu przechowywania danych będą izolowane również od klasy repozytorium. Może to być na przykład zdalna zmiana, taka jak aktualizacja punktów końcowych interfejsu API, lub wdrożenie lokalnego buforowania.
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); } }
Aby uniknąć tworzenia interfejsu użytkownika, który nie odpowiada, nie wykonuj operacji sieciowych w wątku głównym. Domyślnie Android wymaga wykonywania operacji sieciowych w wątku innym niż główny wątek UI. Próba wykonania operacji sieciowych w wątku głównym powoduje zgłoszenie błędu NetworkOnMainThreadException
.
W poprzednim przykładzie kodu operacja sieciowa nie jest aktywowana. Element wywołujący UserRepository
musi wdrożyć podział na wątki za pomocą współprogramów lub funkcji enqueue()
. Więcej informacji znajdziesz w ćwiczeniach z programowania Pobieranie danych z internetu, które pokazują, jak wdrożyć podział na wątki za pomocą współprogramów Kotlin.
Przetrwaj zmiany konfiguracji
Gdy nastąpi zmiana konfiguracji, np. obrót ekranu, fragment lub aktywność jest niszczona i odtwarzana. Wszystkie dane, które nie zostały zapisane w stanie instancji związane z aktywnością związaną z fragmentem, które mogą zawierać tylko niewielkie ilości danych, zostaną utracone. W takim przypadku być może trzeba będzie ponownie wysłać żądania sieciowe.
Aby dane pozostawały bez zmian w konfiguracji, możesz użyć właściwości ViewModel
. Komponent ViewModel
służy do przechowywania danych związanych z interfejsem i zarządzania nimi w sposób uwzględniający cykl życia. Korzystając z poprzedniej funkcji UserRepository
, ViewModel
może wysyłać niezbędne żądania sieciowe i przekazywać wyniki do fragmentu lub działania za pomocą 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 moves 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 } }); } }
Przeczytaj powiązane przewodniki
Więcej informacji na ten temat można znaleźć w następujących powiązanych przewodnikach:
- Zmniejszanie obciążenia sieci przez sieć: omówienie
- Minimalizowanie wpływu regularnych aktualizacji
- Treści internetowe
- Podstawy aplikacji
- Przewodnik po architekturze aplikacji