Ağa bağlanma

Uygulamanızda ağ işlemleri gerçekleştirmek için manifest dosyanız aşağıdaki izinleri içermelidir:

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

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

Uygulamanıza ağ işlevi eklemeden önce, ağ üzerinden veri ve bilgi aktarırken uygulamanızdaki verilerin ve bilgilerin güvende olduğundan emin olmanız gerekir. Bunun için aşağıdaki ağ güvenliğiyle ilgili en iyi uygulamaları uygulayın:

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

Güvenli ağ oluşturma ilkelerinin nasıl uygulanacağı hakkında daha fazla bilgi için ağ güvenliğiyle ilgili ipuçlarına göz atın.

HTTP istemcisi seçme

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

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

  • Retrofit: Square'ın JVM için OkHttp üzerine inşa edilmiş, tür açısından güvenli bir HTTP istemcisidir. Retrofit, istemci arayüzünü açık bir şekilde oluşturmanıza olanak tanır ve çeşitli serileştirme kitaplıklarını destekler.
  • Ktor: JetBrains'den, tamamen Kotlin için oluşturulmuş ve eş yordamlarla 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ümleri çalıştıran cihazlarda hem açık 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üm sunar. Yanıtın ayrıştırılması, uygulamaya bırakılır.

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

NDK tabanlı uygulamalar için android_res_nsend bölümüne bakın.

Ağ işlemlerini bir depoyla kapsülleme

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

Aşağıdaki örnekte gösterildiği 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 tanımlamak ü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);
}

Depo sınıfı içindeki işlevler, ağ işlemlerini kapsülleyip sonuçlarını gösterebilir. Bu kapsülleme, deposu çağıran bileşenlerin verilerin nasıl depolandığını bilmesi gerekmediğinden emin olur. Verilerin depolanma biçiminde yapılacak gelecekteki değişiklikler de depolama alanı sınıfında izole edilir. Örneğin, API uç noktalarında güncelleme gibi uzak bir değişikliğiniz olabilir veya yerel önbelleğe alma özelliğini uygulamak isteyebilirsiniz.

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şturmaktan kaçınmak 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ışında bir iş parçacığında gerçekleştirmenizi gerektirir. Ana iş parçacığında ağ işlemleri yapmaya çalışırsanız bir NetworkOnMainThreadException atılır.

Önceki kod örneğinde, ağ işlemi aslında tetiklenmez. UserRepository işlevini çağıran, iş parçacığı oluşturmayı coroutine'leri veya enqueue() işlevini kullanarak uygulamalıdır. Daha fazla bilgi için Kotlin coroutine'lerini kullanarak iş parçacığı oluşturmanın nasıl uygulandığını gösteren İnternetten veri alma kod laboratuvarına bakın.

Yapılandırma değişikliklerine dayanabilir.

Ekran döndürme gibi bir yapılandırma değişikliği olduğunda, parçanız veya etkinliğiniz yok edilir ve yeniden oluşturulur. Yalnızca küçük miktarlarda veri tutabilen parça etkinliğiniz için örnek durumunda kaydedilmeyen tüm veriler kaybolur. Bu durumda, ağ isteklerinizi tekrar göndermeniz 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 depolayıp yönetmek için tasarlanmıştır. Önceki UserRepository'ü kullanarak ViewModel gerekli ağ isteklerini yapabilir ve sonucu LiveData kullanarak 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 konu hakkında daha fazla bilgi edinmek için aşağıdaki ilgili kılavuzları inceleyin: