برای انجام عملیات شبکه در برنامه خود، مانیفست شما باید دارای مجوزهای زیر باشد:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
بهترین روش ها برای ارتباطات شبکه ایمن
قبل از اینکه عملکرد شبکه را به برنامه خود اضافه کنید، باید مطمئن شوید که داده ها و اطلاعات درون برنامه شما هنگام انتقال از طریق شبکه ایمن می مانند. برای انجام این کار، بهترین شیوه های امنیت شبکه را دنبال کنید:
- مقدار داده های حساس یا شخصی کاربر را که از طریق شبکه منتقل می کنید به حداقل برسانید.
- تمام ترافیک شبکه را از برنامه خود از طریق SSL ارسال کنید.
- ایجاد یک پیکربندی امنیتی شبکه را در نظر بگیرید که به برنامه شما امکان میدهد به مقامات گواهی سفارشی (CA) اعتماد کند یا مجموعه CAهای سیستمی را که برای برقراری ارتباط امن به آنها اعتماد دارد محدود کند.
برای اطلاعات بیشتر در مورد نحوه اعمال اصول شبکه ایمن، به نکات امنیتی شبکه مراجعه کنید.
یک کلاینت HTTP را انتخاب کنید
اکثر برنامه های متصل به شبکه از HTTP برای ارسال و دریافت داده استفاده می کنند. پلتفرم اندروید شامل سرویس گیرنده HttpsURLConnection
است که از TLS، آپلود و بارگیری جریانی، وقفه های زمانی قابل تنظیم، IPv6 و ادغام اتصال پشتیبانی می کند.
کتابخانه های شخص ثالث که API های سطح بالاتری را برای عملیات شبکه ارائه می دهند نیز در دسترس هستند. اینها از ویژگیهای راحتی مختلف مانند سریالسازی بدنههای درخواستی و سریالزدایی بدنههای پاسخدهنده پشتیبانی میکنند.
- Retrofit : یک کلاینت HTTP ایمن برای JVM از Square، ساخته شده در بالای OkHttp. Retrofit به شما این امکان را میدهد که یک رابط کلاینت بهصورت اعلامی ایجاد کنید و از چندین کتابخانه سریالسازی پشتیبانی میکند.
- Ktor : یک کلاینت HTTP از JetBrains، که کاملاً برای Kotlin ساخته شده است و توسط برنامههای کاربردی پشتیبانی میشود. Ktor از موتورها، سریالسازها و پلتفرمهای مختلف پشتیبانی میکند.
پرس و جوهای DNS را حل کنید
دستگاههایی که Android 10 (سطح API 29) و بالاتر را اجرا میکنند، از جستجوی تخصصی DNS از طریق جستجوی متن شفاف و حالت DNS-over-TLS پشتیبانی داخلی دارند. DnsResolver
API وضوح عمومی و ناهمزمان را ارائه می دهد که به شما امکان می دهد SRV
، NAPTR
و سایر انواع رکورد را جستجو کنید. تجزیه پاسخ به برنامه واگذار می شود تا انجام دهد.
در دستگاههایی که اندروید 9 (سطح API 28) و پایینتر را اجرا میکنند، حلکننده DNS پلتفرم فقط از رکوردهای A
و AAAA
پشتیبانی میکند. این به شما امکان می دهد آدرس های IP مرتبط با یک نام را جستجو کنید اما هیچ نوع رکورد دیگری را پشتیبانی نمی کند.
برای برنامههای مبتنی بر NDK، android_res_nsend
ببینید.
عملیات شبکه را با یک مخزن کپسوله کنید
برای ساده سازی فرآیند انجام عملیات شبکه و کاهش تکرار کد در قسمت های مختلف اپلیکیشن خود، می توانید از الگوی طراحی مخزن استفاده کنید. مخزن کلاسی است که عملیات داده را مدیریت می کند و یک انتزاع API تمیز را بر روی برخی از داده ها یا منابع خاص ارائه می دهد.
میتوانید از Retrofit برای اعلام رابطی استفاده کنید که روش HTTP، URL، آرگومانها و نوع پاسخ را برای عملیات شبکه مشخص میکند، مانند مثال زیر:
کاتلین
interface UserService { @GET("/users/{id}") suspend fun getUser(@Path("id") id: String): User }
جاوا
public interface UserService { @GET("/user/{id}") Call<User> getUserById(@Path("id") String id); }
در یک کلاس مخزن، توابع می توانند عملیات شبکه را کپسوله کرده و نتایج آنها را به نمایش بگذارند. این کپسولهسازی تضمین میکند که اجزایی که مخزن را فراخوانی میکنند نیازی به دانستن نحوه ذخیره دادهها ندارند. هر گونه تغییر آتی در نحوه ذخیره سازی داده ها در کلاس مخزن نیز جدا می شود. به عنوان مثال، ممکن است یک تغییر از راه دور مانند به روز رسانی نقاط پایانی API داشته باشید، یا ممکن است بخواهید حافظه پنهان محلی را پیاده سازی کنید.
کاتلین
class UserRepository constructor( private val userService: UserService ) { suspend fun getUserById(id: String): User { return userService.getUser(id) } }
جاوا
class UserRepository { private UserService userService; public UserRepository( UserService userService ) { this.userService = userService; } public Call<User> getUserById(String id) { return userService.getUser(id); } }
برای جلوگیری از ایجاد یک رابط کاربری بدون پاسخ، عملیات شبکه را روی رشته اصلی انجام ندهید. بهطور پیشفرض، Android از شما میخواهد که عملیات شبکه را روی رشتهای غیر از رشته رابط کاربری اصلی انجام دهید. اگر بخواهید عملیات شبکه را روی رشته اصلی انجام دهید، یک NetworkOnMainThreadException
پرتاب می شود.
در مثال کد قبلی، عملیات شبکه در واقع راه اندازی نشده است. فراخواننده UserRepository
باید threading را با استفاده از coroutines یا با استفاده از تابع enqueue()
پیاده سازی کند. برای اطلاعات بیشتر، به بخش کد دریافت دادهها از اینترنت مراجعه کنید، که نحوه پیادهسازی threading با استفاده از کوروتینهای Kotlin را نشان میدهد.
زنده ماندن تغییرات پیکربندی
هنگامی که یک تغییر پیکربندی رخ می دهد، مانند چرخش صفحه، قطعه یا فعالیت شما از بین می رود و دوباره ایجاد می شود. هر داده ای که در حالت نمونه برای فعالیت قطعه شما ذخیره نشده باشد، که فقط می تواند مقادیر کمی از داده ها را در خود نگه دارد، از بین می رود. اگر این اتفاق افتاد، ممکن است لازم باشد درخواست های شبکه خود را دوباره انجام دهید.
می توانید از ViewModel
استفاده کنید تا به داده های خود اجازه دهید از تغییرات پیکربندی جان سالم به در ببرند. مؤلفه ViewModel
برای ذخیره و مدیریت داده های مرتبط با رابط کاربری به روشی مبتنی بر چرخه حیات طراحی شده است. با استفاده از UserRepository
قبلی، ViewModel
میتواند درخواستهای شبکه لازم را انجام دهد و نتیجه را با استفاده از LiveData
به قطعه یا فعالیت شما ارائه دهد:
کاتلین
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 } } } }
جاوا
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 } }); } }
راهنماهای مرتبط را بخوانید
برای کسب اطلاعات بیشتر در مورد این موضوع، به راهنمای مرتبط زیر مراجعه کنید:
- کاهش تخلیه باتری شبکه: بررسی اجمالی
- تأثیر به روز رسانی های منظم را به حداقل برسانید
- محتوای مبتنی بر وب
- اصول کاربردی
- راهنمای معماری اپلیکیشن