Ağa bağlanma

Uygulamanızda ağ işlemleri gerçekleştirmek için manifestinizde aşağıdaki izinler bulunmalıdır:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
Bu nedenle, yükleme sırasında verilirler ve çalışma zamanında istenmeleri gerekmez.

Güvenli ağ iletişimi için en iyi uygulamalar

Uygulamanıza ağ işlevi eklemeden önce, ağ üzerinden iletirken uygulamanızdaki veri ve bilgilerin güvende kalmasını sağlamanız gerekir. Bunun için aşağıdaki ağ güvenliğiyle ilgili en iyi uygulamaları takip edin:

  • Ağ üzerinden ilettiğiniz hassas veya kişisel kullanıcı verilerinin miktarını en aza indirin.
  • Uygulamanızdaki tüm ağ trafiğini SSL üzerinden gönderin.
  • Uygulamanızın özel sertifika yetkililerine (CA'lar) güvenmesine veya güvenli iletişim için güvendiği sistem CA'larının kümesini kısıtlamasına olanak tanıyan bir ağ güvenliği yapılandırması oluşturmayı düşünebilirsiniz.

Güvenli ağ iletişimi ilkelerini uygulama hakkında daha fazla bilgi için Ağ güvenliği ipuçları başlıklı makaleyi inceleyin.

Bir HTTP istemcisi seçin

Ağa bağlı uygulamaların çoğu, veri göndermek ve almak için HTTP'yi kullanır. Android platformu, TLS, akış yüklemeleri ve indirmeleri, yapılandırılabilir zaman aşımları, IPv6 ve bağlantı havuzunu destekleyen HttpsURLConnection istemcisini içerir.

Ağ işlemleri için daha üst düzey API'ler sunan üçüncü taraf kitaplıklar da mevcuttur. Bunlar, istek gövdelerinin serileştirilmesi ve yanıt gövdelerinin seri durumdan çıkarılması gibi çeşitli kolaylık özelliklerini destekler.

  • Retrofit: OkHttp'nin üzerine inşa edilmiş, Square'in JVM için tür güvenli bir HTTP istemcisi. Retrofit, istemci arayüzünü bildirimsel olarak oluşturmanıza olanak tanır ve çeşitli serileştirme kitaplıklarını destekler.
  • Ktor: JetBrains'in tamamen Kotlin için oluşturduğu ve eş yordamlar tarafından desteklenen bir HTTP istemcisi. Ktor çeşitli motorları, serileştiricileri ve platformları destekler.

DNS sorgularını çözme

Android 10 (API düzeyi 29) ve sonraki sürümlerin yüklü olduğu cihazlarda hem düz metin aramaları hem de DNS-over-TLS modu aracılığıyla özel DNS aramaları için yerleşik destek bulunur. DnsResolver API, SRV, NAPTR ve diğer kayıt türlerini aramanıza olanak tanıyan genel, eşzamansız çözümleme sağlar. Yanıtı ayrıştırma işlemi uygulamaya bırakılır.

Android 9 (API düzeyi 28) ve önceki sürümlerini çalıştıran cihazlarda platform DNS çözümleyici yalnızca A ve AAAA kayıtlarını destekler. Bu araç, bir adla ilişkili IP adreslerini aramanıza olanak tanır ancak diğer kayıt türlerini desteklemez.

NDK tabanlı uygulamalar için android_res_nsend başlıklı makaleyi inceleyin.

Ağ işlemlerini bir depo ile kapsama

Ağ işlemlerini gerçekleştirme sürecini basitleştirmek ve uygulamanızın çeşitli bölümlerinde kod tekrarını azaltmak için depo tasarım kalıbını kullanabilirsiniz. Depo, veri işlemlerini işleyen ve belirli veriler veya kaynaklar üzerinde temiz bir API soyutlaması sağlayan bir sınıftır.

Aşağıdaki örnekte olduğu gibi, ağ işlemleri için HTTP yöntemini, URL'yi, bağımsız değişkenleri ve yanıt türünü belirten bir arayüz bildirmek üzere Retrofit'i kullanabilirsiniz:

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

Bir depo sınıfında işlevler, ağ işlemlerini kapsayabilir ve sonuçlarını gösterebilir. Bu kapsülleme, depoyu çağıran bileşenlerin verilerin nasıl depolandığını bilmesine gerek kalmamasını sağlar. Verilerin depolanma şekliyle ilgili gelecekte yapılacak değişiklikler de yalnızca depo sınıfıyla sınırlı kalır. Örneğin, API uç noktalarında güncelleme gibi uzaktan bir değişiklik yapmanız veya yerel önbelleğe alma uygulamak istemeniz gerekebilir.

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

Yanıt vermeyen bir kullanıcı arayüzü oluşturmamak için ana iş parçacığında ağ işlemleri gerçekleştirmeyin. Android, varsayılan olarak ağ işlemlerini ana kullanıcı arayüzü iş parçacığı dışındaki bir iş parçacığında gerçekleştirmenizi gerektirir. Ana iş parçacığında ağ işlemleri gerçekleştirmeye çalışırsanız NetworkOnMainThreadException oluşturulur.

Önceki kod örneğinde, ağ işlemi aslında tetiklenmez. UserRepository işlevini çağıran, iş parçacığını eş yordamlar veya enqueue() işlevini kullanarak uygulamalıdır. Kotlin coroutine'leri kullanarak iş parçacığı oluşturmanın nasıl uygulanacağını gösteren İnternetten veri alma adlı codelab'de daha fazla bilgi bulabilirsiniz.

Yapılandırma değişikliklerinden etkilenmeme

Ekran döndürme gibi bir yapılandırma değişikliği olduğunda parçanız veya etkinliğiniz yok edilip yeniden oluşturulur. Yalnızca az miktarda veri tutabilen parça etkinliğinizin örnek durumunda kaydedilmeyen tüm veriler kaybolur. Bu durumda, ağ isteklerinizi tekrar yapmanız gerekebilir.

Verilerinizin yapılandırma değişikliklerinden etkilenmemesi için ViewModel kullanabilirsiniz. ViewModel bileşeni, kullanıcı arayüzüyle ilgili verileri yaşam döngüsüne duyarlı bir şekilde depolamak ve yönetmek için tasarlanmıştır. Yukarıdaki UserRepository kullanılarak ViewModel, gerekli ağ isteklerini yapabilir ve LiveData kullanarak sonucu parçanıza veya etkinliğinize sağlayabilir:

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

Bu konuyla ilgili daha fazla bilgi edinmek için aşağıdaki ilgili kılavuzlara bakın: