Verbindung zum Netzwerk herstellen

Damit in Ihrer Anwendung Netzwerkvorgänge ausgeführt werden können, muss das Manifest Folgendes enthalten: die folgenden Berechtigungen:

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

Best Practices für eine sichere Netzwerkkommunikation

Bevor Sie Ihrer App Netzwerkfunktionen hinzufügen, müssen Sie sicherstellen, Daten und Informationen in Ihrer App auch dann sicher sind, wenn Sie Daten Netzwerk. Befolgen Sie dazu die folgenden Best Practices für die Netzwerksicherheit:

  • Minimieren Sie die Anzahl vertraulicher oder persönlicher Nutzer Daten, die Sie über den Netzwerk.
  • Gesamten Netzwerkverkehr von Ihrer App senden über SSL.
  • Erwägen Sie die Erstellung einer Netzwerksicherheit Konfiguration, mit der Ihre App benutzerdefinierten Zertifizierungsstellen (CAs) vertrauen oder die Gruppe von System-CAs einschränken für eine sichere Kommunikation.

Weitere Informationen zur Anwendung von Prinzipien für sichere Netzwerke finden Sie in den Tipps zur Netzwerksicherheit.

HTTP-Client auswählen

Die meisten mit dem Netzwerk verbundenen Apps verwenden HTTP zum Senden und Empfangen von Daten. Das Android- umfasst die HttpsURLConnection-Client, unterstützt TLS, Streaming-Uploads und -Downloads, konfigurierbare Zeitüberschreitungen, IPv6 und Verbindungs-Pooling.

Bibliotheken von Drittanbietern, die übergeordnete APIs für Netzwerkvorgänge anbieten, sind ebenfalls verfügbar. Diese unterstützen verschiedene praktische Funktionen, z. B. die Serialisierung von Anfragetexten und Deserialisierung von Antworttexten

  • Retrofit: ein typsicheres HTTP für die JVM von Square, die auf OkHttp basiert. Mit Retrofit können Sie erstellt eine Client-Schnittstelle deklarativ und bietet Unterstützung für Serialisierungsbibliotheken.
  • Ktor: ein HTTP-Client von JetBrains, erstellt für Kotlin und unterstützt Koroutinen. Ktor unterstützt verschiedene Motoren, Serialisatoren und Plattformen.

DNS-Abfragen auflösen

Geräte mit Android 10 (API-Level 29) und höher unterstützen für spezielle DNS-Lookups sowohl im Klartextmodus als auch im DNS-over-TLS-Modus. Die DnsResolver API bietet generische, asynchrone Auflösung, bei der Sie SRV, NAPTR und andere Eintragstypen. Das Parsen der Antwort bleibt der Anwendung überlassen.

Auf Geräten mit Android 9 (API-Level 28) und niedriger wird das Plattform-DNS Resolver unterstützt nur A- und AAAA-Einträge. So können Sie die IP-Adresse ermitteln, Adressen, die einem Namen zugeordnet sind, unterstützt jedoch keine anderen Eintragstypen.

Informationen zu NDK-basierten Anwendungen finden Sie unter android_res_nsend

Netzwerkvorgänge mit einem Repository kapseln

Um die Durchführung von Netzwerkvorgängen zu vereinfachen und den Code zu reduzieren in verschiedenen Teilen Ihrer App dupliziert werden, können Sie das Repository-Design Muster zu ändern. Ein Repository ist eine Klasse, die Datenvorgänge abwickelt und eine eine saubere API-Abstraktion über bestimmte Daten oder Ressourcen.

Sie können mit Retrofit eine Schnittstelle deklarieren, in der die HTTP-Methode, URL, Argumente und Antworttyp für Netzwerkvorgänge wie im folgenden Beispiel: Beispiel:

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

Innerhalb einer Repository-Klasse können Funktionen Netzwerkvorgänge kapseln und ihre Ergebnisse offenzulegen. Durch diese Datenkapselung wird sichergestellt, dass das Repository nicht wissen muss, wie die Daten gespeichert werden. Alle zukünftigen Änderungen an wie die Daten gespeichert werden, unabhängig von der Repository-Klasse. Für z. B. eine Remote-Änderung wie eine Aktualisierung der API-Endpunkte oder können Sie lokales Caching implementieren.

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

Um zu vermeiden, dass die Benutzeroberfläche reagiert, aber keine Netzwerkvorgänge auf dem im Hauptthread. Standardmäßig erfordert Android, dass Sie Netzwerkvorgänge auf einem als der UI-Hauptthread. Bei Netzwerkvorgängen im Hauptthread NetworkOnMainThreadException geworfen wird.

Im vorherigen Codebeispiel hat der nicht tatsächlich ausgelöst wird. Der Aufrufer von UserRepository muss das Threading entweder mithilfe von Koroutinen oder mithilfe der enqueue() implementieren . Weitere Informationen finden Sie im Codelab Daten aus dem Internet, zeigt, wie Threading mit Kotlin-Koroutinen implementiert wird.

Konfigurationsänderungen beibehalten

Bei einer Konfigurationsänderung, z. B. einer Bildschirmdrehung, Ihres Fragments oder Aktivitäten gelöscht und neu erstellt werden. Alle Daten, die nicht in der Instanz gespeichert sind für die Fragmentaktivität, die nur kleine Datenmengen enthalten kann, geht verloren. In diesem Fall müssen Sie Ihre Netzwerkanfragen möglicherweise noch einmal senden.

Mit einem ViewModel können Sie Ihre Daten auch nach Konfigurationsänderungen bleiben. Die Komponente „ViewModel“ ist wurde entwickelt, um UI-bezogene Daten in einem Mithilfe der vorangehenden UserRepository kann der ViewModel notwendige Netzwerkanfragen und liefern das Ergebnis zu Ihrem Fragment oder Ihrer Aktivität mit 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
            }
        });
    }
}

Weitere Informationen zu diesem Thema finden Sie in den folgenden verwandten Leitfäden: