Connettiti alla rete

Per eseguire operazioni di rete nella tua applicazione, il file manifest deve includere le seguenti autorizzazioni:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />

Best practice per una comunicazione di rete sicura

Prima di aggiungere la funzionalità di networking all'app, devi assicurarti che i dati e le informazioni all'interno dell'app siano protetti quando trasmetti tramite in ogni rete. Per farlo, segui queste best practice per la sicurezza della rete:

  • Riduci al minimo la quantità di utenti sensibili o personali dati che trasmetti tramite in ogni rete.
  • Invia tutto il traffico di rete dalla tua app SSL.
  • Valuta l'opportunità di creare una sicurezza di rete configurazione, che consente alla tua app considerare attendibile le autorità di certificazione (CA) personalizzate o limitare l'insieme di CA del sistema affidabile per le comunicazioni sicure.

Per ulteriori informazioni su come applicare i principi di rete sicura, consulta suggerimenti per la sicurezza del networking.

Scegli un client HTTP

La maggior parte delle app connesse alla rete utilizza HTTP per inviare e ricevere dati. Android include HttpsURLConnection, che supporta TLS, flussi di caricamenti e download, timeout configurabili, IPv6 e pooling di connessioni.

Le librerie di terze parti che offrono API di livello superiore per le operazioni di networking disponibili. Supportano varie funzionalità utili, come serializzazione dei corpi di richiesta e deserializzazione dei corpi di risposta.

  • Retrofit: un protocollo HTTP a prova di tipo per la JVM di Square, basato su OkHttp. Retrofit ti consente creano un'interfaccia client in modo dichiarativo e supporta diverse librerie di serializzazione.
  • Ktor: un client HTTP di JetBrains, creato interamente per Kotlin e con coroutine. Ktor supporta vari motori, serializzatori e piattaforme.

Risolvi le query DNS

I dispositivi con Android 10 (livello API 29) e versioni successive dispongono del supporto integrato per ricerche DNS specializzate sia tramite ricerche in chiaro sia in modalità DNS over-TLS. L'API DnsResolver fornisce informazioni generiche, una risoluzione asincrona, che ti consente di cercare SRV, NAPTR e altri tipi di record. L'analisi della risposta viene lasciata all'app per eseguire l'analisi.

Sui dispositivi con Android 9 (livello API 28) e versioni precedenti, il DNS della piattaforma resolver supporta solo i record A e AAAA. Questo ti consente di cercare indirizzi associati a un nome, ma non supporta altri tipi di record.

Per le app basate su NDK, vedi android_res_nsend

Incapsulare le operazioni di rete con un repository

Semplificare il processo di esecuzione delle operazioni di rete e ridurre il codice in varie parti della tua app, puoi usare la progettazione in repository pattern. Un repository è una classe che gestisce le operazioni sui dati e fornisce una un'astrazione API semplice su dati o risorse specifici.

Puoi utilizzare Retrofit per dichiarare un'interfaccia che specifica il metodo HTTP, URL, argomenti e tipo di risposta per le operazioni di rete, come di seguito esempio:

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);
}

All'interno di una classe di repository, le funzioni possono incapsulare le operazioni di rete espongono i propri risultati. Questa incapsulamento assicura che i componenti che chiamano non è necessario che il repository sappia come vengono archiviati i dati. Eventuali modifiche future a il modo in cui i dati vengono archiviati sono isolati anche nella classe del repository. Per Ad esempio, potresti avere una modifica remota, come un aggiornamento agli endpoint API, puoi implementare la memorizzazione nella cache locale.

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);
    }
}

Per evitare di creare una UI che non risponde, non eseguire operazioni di rete sulla thread principale. Per impostazione predefinita, Android richiede di eseguire operazioni di rete su un con un thread diverso da quello principale dell'interfaccia utente. Se provi a eseguire operazioni di rete sul thread principale, NetworkOnMainThreadException viene lanciato.

Nell'esempio di codice precedente, il valore l'operazione di rete non viene effettivamente attivata. Il chiamante di UserRepository deve implementare l'organizzazione in thread utilizzando coroutine o l'enqueue() personalizzata. Per ulteriori informazioni, consulta il codelab Ottenere dati dalla internet, che illustra come implementare il threading utilizzando le coroutine Kotlin.

Sopravvivi alle modifiche di configurazione

Quando si verifica una modifica alla configurazione, ad esempio una rotazione dello schermo, il frammento o l'attività viene eliminata e ricreata. Tutti i dati non salvati nell'istanza per l'attività dei frammenti, che può contenere solo piccole quantità di dati, è perduto. In questo caso, potrebbe essere necessario effettuare di nuovo le richieste di rete.

Puoi utilizzare una ViewModel per consentire e mantenere i dati dopo le modifiche di configurazione. Il componente ViewModel è progettato per archiviare e gestire i dati relativi all'interfaccia utente in un ambiente attento al ciclo di vita in molti modi diversi. Utilizzando il valore UserRepository precedente, ViewModel può rendere richieste di rete necessarie e fornire il risultato al frammento o all'attività usando 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
            }
        });
    }
}

Per saperne di più su questo argomento, consulta le seguenti guide correlate: