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:
- Minimize a quantidade de dados confidenciais ou pessoais do usuário transmitidos pela rede.
- Envie todo o tráfego de rede do seu app por SSL.
- Crie uma configuração de segurança de rede, que permite que seu app confie em autoridades de certificação (CAs, na sigla em inglês) personalizadas ou restrinja o conjunto de CAs do sistema em que ele confia para uma comunicação segura.
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 } }); } }
Guias relacionados
Para saber mais sobre este tópico, consulte estes guias relacionados: