Conectar-se à rede

Para realizar operações de rede no seu aplicativo, o manifesto precisa incluir as seguintes permissões:

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

Práticas recomendadas para comunicação de rede segura

Antes de adicionar a funcionalidade de rede ao seu aplicativo, você precisa garantir que os dados e as informações dele permaneçam seguros quando forem transmitidos. Para fazer isso, siga estas práticas recomendadas de segurança de rede:

Para ver mais informações sobre como aplicar princípios de rede seguros, consulte dicas de segurança na rede.

Escolher um cliente HTTP

A maioria dos apps Android conectados à rede usa HTTP para enviar e receber dados. A plataforma Android inclui o cliente HttpsURLConnection, que é compatível com TLS, uploads e downloads em streaming, tempos limite configuráveis, IPv6 e pooling de conexão. Neste tópico, usamos a biblioteca de cliente HTTP Retrofit, que permite criar um cliente HTTP declarativamente. A Retrofit também é compatível com a serialização automática de corpos de solicitação e a desserialização de corpos de resposta.

Resolver consultas DNS

Os dispositivos com o Android 10 (nível 29 da API) e versões mais recentes têm compatibilidade nativa com buscas DNS especializadas com pesquisas de texto não criptografado e com o modo DNS via TLS. A API DnsResolver fornece resolução assíncrona genérica, que permite que você pesquise por SRV, NAPTR e outros tipos de registro. Observe que a análise da resposta é deixada para execução pelo app.

Em dispositivos com o Android 9 (nível 28 da API) e versões anteriores, o resolvedor de DNS da plataforma é compatível apenas com registros A e AAAA. Isso permite pesquisar os endereços IP associados a um nome, mas não é compatível com nenhum outro tipo de registro.

Para apps baseados em NDK, consulte android_res_nsend.

Encapsular operações de rede com um repositório.

Para simplificar o processo de execução de operações de rede e reduzir a duplicação de código em várias partes do app, você pode usar o padrão de design do repositório. Um repositório é uma classe que processa operações de dados e fornece uma abstração limpa da API sobre alguns dados ou recursos específicos.

Você pode usar a Retrofit para declarar uma interface que especifica o método HTTP, o URL, os argumentos e o tipo de resposta para operações de rede, como no exemplo a seguir:

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

Em uma classe de repositório, as funções podem encapsular operações de rede e expor os resultados. Esse encapsulamento garante que os componentes que chamam o repositório não precisem saber como os dados são armazenados. As mudanças futuras na forma como os dados são armazenados também são isoladas na classe do repositório.

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

Para evitar a criação de uma IU não responsiva, não execute operações de rede na linha de execução principal. Por padrão, o Android exige que você execute operações de rede em uma linha de execução diferente da linha de execução de IU principal. Se você não fizer isso, uma NetworkOnMainThreadException será gerada. Na classe UserRepository mostrada no exemplo de código anterior, a operação de rede não é acionada. O autor da chamada do UserRepository precisa implementar as linhas de execução usando corrotinas ou a função enqueue().

Sobreviver às mudanças de configuração

Quando ocorre uma mudança de configuração, como uma rotação de tela, o fragmento, ou a atividade, é destruído e recriado. Todos os dados que não estiverem salvos no estado da instância do seu fragmento ou atividade, que precisam conter apenas pequenas quantidades de dados, são perdidos e pode ser necessário fazer as solicitações de rede novamente.

Use um ViewModel para garantir que os dados sobrevivam às mudanças de configuração. Um ViewModel é um componente projetado para armazenar e gerenciar dados relacionados à IU considerando o ciclo de vida. Usando o UserRepository criado acima, o ViewModel pode fazer as solicitações de rede necessárias e fornecer o resultado para seu fragmento ou atividade 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 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
            }
        });
    }
}

Para saber mais sobre este tópico, consulte estes guias relacionados: