برنامه نویسی 120 بازدید

اگر صرفاً با منظور آزمایش تاکنون API سطح 29 را در پروژه‌های اندروید تارگت کرده باشید، در خوش‌بینانه‌ترین حالت صرفاً با برخی هشدارها مواجه می‌شوید که در فرایند بیلد ظاهر می‌شوند و در نهایت بیلد با موفقیت به پایان می‌رسد. اما در صورتی که فکر می‌کنید هیچ مشکلی وجود نخواهد داشت، ممکن است در مورد برخی کلاس‌های خاص مانند NetworkInfo کاملاً شگفت‌زده شوید. در این مقاله در مورد روش‌های بررسی وضعیت اتصال اینترنت در اندروید Q صحبت خواهیم کرد.

چرا باید اندروید Q را تارگت کنیم؟

اندروید 10 یا اندروید Q به طور رسمی در تاریخ 3 سپتامبر 2019 (12 شهریور 1398) منتشر شده است و نسخه بعد از اندروید 9 و جدیدترین نسخه اندروید محسوب می‌شود که از سنت نامگذاری بر اساس نام شیرینی‌ها پیروی نمی‌کند.

اغلب توصیه می‌شود که هر چند برای تست آخرین نسخه اندروید را تارگت کنیم و گوگل پلی استور نیز در این خصوص پیشنهادهایی به شرح زیر دارد:

بررسی وضعیت اتصال اینترنت در اندروید Q

همان طور که می‌بینید هر سال الزام گوگلی پلی به نسخه جدید ارتقا می‌یابد. این بدان معنی است که از تاریخ 1 آگوست 2020 (11 مرداد 1399) اپلیکیشن‌های جدید باید API سطح 29 را تارگت کنند. بنابراین زمان زیادی برای تست این که همه چیز روی نسخه جدید به درستی کار می‌کند ندارید.

قبل از هر چیز باید نسخه جدید را تارگت کنیم تا ببینیم چه اتفاقاتی رخ می‌دهند. به این منظور کافی است گزاره targetSdkVersion را در پیکربندی پیدا کنید و آن را روی 29 تنظیم کنید. سپس پروژه را کامپایل کنید و هشدارها و خطاهای کامپایل را بررسی کنید.

در ادامه نمونه‌ای از هشدارهایی را که ممکن است بگیرید ارائه کرده‌ایم. این هشدارها از پروژه واقعی که سطح 28 به 29 ارتقا یافته ناشی شده‌اند:

Type mismatch: inferred type is MenuItem? but MenuItem was expected
Type mismatch: inferred type is Configuration? but Configuration was expected
Type mismatch: inferred type is String? but String was expected
'setColorFilter(Int, PorterDuff.Mode): Unit' is deprecated. Deprecated in Java
Type mismatch: inferred type is Date? but Date was expected
Unsafe use of a nullable receiver of type Any?
'PreferenceManager' is deprecated. Deprecated in Java
'NetworkInfo' is deprecated. Deprecated in Java
'getter for activeNetworkInfo: NetworkInfo!' is deprecated. Deprecated in Java
'getter for isConnectedOrConnecting: Boolean' is deprecated. Deprecated in Java

در لیست فوق به جز بررسی‌های null و اضافه شدن پکیج androidx.preference چیز زیادی وجود ندارد. همه این موارد در طی 20 دقیقه کاملاً رفع می‌شوند. اما در ادامه با مشکل منسوخ شدن NetworkInfo مواجه می‌شویم.

NetworkInfo در سطح 29 منسوخ شده است:

فراخوانی‌کننده‌ها به جای ConnectivityManager.NetworkCallback باید با تغییراتی که در زمینه اتصال‌پذیری واقع شده آشنا شوند و از ConnectivityManager#getNetworkCapabilities یا ConnectivityManager#getLinkProperties برای دریافت ناهمگام اطلاعات استفاده کنند.

یک روش برای حل این مشکل این است که به راهنمایی‌های ارتقای رسمی (+) روی وب‌سایت اندروید نگاه کنیم. متأسفانه در این وب‌سایت اطلاعات زیادی ارائه نشده است و از این رو باید از قدرت ماهیچه‌های ذهن برای حل این مشکل کمک بگیریم. پیش از این اخبارِ بد در مورد حالت اتصال‌پذیری تنها کاری که باید انجام می‌دادیم به صورت زیر بود:

این کد باید از هر جایی با فرض دسترسی به Context اپلیکیشن فراخوانی می‌شود و به راحتی می‌شد بررسی کرد که آیا دستگاه در حال حاضر به شبکه دسترسی دارد یا نه. برای دریافت تغییرات در اتصال دستگاه به شبکه نیز باید به صورت زیر عمل می‌شد:

  • فایل BaseActivity.kt
  • فایل MyActivity.kt
  • فایل NetworkStateReceiverListener.kt 
اکنون که ConnectivityManager#getActiveNetworkInfo را از دست داده‌ایم تنها روش برای بررسی ناهمگام اتصال دستگاه به شبکه استفاده از API زیر است:
  1. NetworkCallback – این API است که از قبل می‌شناسیم و شاید حتی استفاده کرده‌ایم. این یک اینترفیس برای پیاده‌سازی دریافت رویدادهای اتصال‌پذیری دستگاه است.
  2. ConnectivityManager#getNetworkCapabilities – با توجه به شبکه، ظرفیت‌هایی مانند نوع شبکه (موبایل یا وای فای) پهنای باند و غیره را می‌گیرد.
  3. ConnectivityManager#getLinkProperties – با توجه به شبکه مشخصه‌های لینک شبکه مانند اینترفیس شبکه را عرضه می‌کند.

در لیست فوق موارد 2 و 3 از API سطح 21 وجود داشته‌اند، اما تاکنون زیاد با آن‌ها کار نکرده‌ایم و به نظر می‌رسد که نتیجه‌ای مشابه کد قبلی به دست نمی‌دهد.

هر دو آن‌ها به یک آرگومان Network نیاز دارند که با ConnectivityManager#getActiveNetwork به دست می‌آید، اما در نهایت هیچ کدام معادل NetworkInfo#isConnected یا NetworkInfo#isConnectedOrConencting نیستند.

چاره کار این است که حالت اتصال را جایی در اندروید ذخیره کنیم. به این منظور باید برخی موارد را در پیاده‌سازی NetworkCallback مورد بازنگری قرار دهیم.

جایگزینی برای NetworkInfo

کار خود را با ایجاد یک جایگزین NetworkInfo آغاز می‌کنیم که یک روش ناهمگام برای دریافت حالت اتصال شبکه ایجاد می‌کند. در ادامه مثالی از اینترفیس آن را می‌بینید:

اکنون باید این اینترفیس را پیاده‌سازی کنیم و مطمئن شویم که مقادیر آن اتصال‌پذیری دستگاه را بازتاب می‌دهند:

اکنون می‌توانیم از این پیاده‌سازی درون NetworkCallback استفاده کنیم و حالت شبکه را درون آن ذخیره کرده و هر بار که حالت تغییر می‌یابد به‌روزرسانی کنیم:

بدین ترتیب موفق شدیم مشکل را حل کنیم، اما هنوز مواردی هستند که باید تغییر دهیم:

  1. نخستین مورد این است که باید یک وهله از NetworkState ایجاد کنیم که از هر جایی در اپلیکیشن و نه فقط NetworkStateImp قابل دسترسی باشد. به این منظور یک شیء فقط-خواندنی نیاز داریم.
  2. مورد دوم شیوه اطلاع‌رسانی به هر یک از طرفین ذینفع در مورد تغییر حالت اتصال‌پذیری شبکه است.
  3. در نهایت مورد سوم اتصال همه این موارد به NetworkStateImp است.

نکته دوم و سوم چندین راه‌حل دارند. برخی ممکن است از یک کانتینر DI استفاده کنند که از قبل برای اپلیکیشن تنظیم کرده‌اند، اما اگر بخواهیم همه چیز ساده باشد، می‌توانیم از کد کاتلین زیر استفاده کنیم:

در کد فوق کارهای زیر را انجام می‌دهیم:

  • Object یک syntactic sugar کاتلین برای تنظیم الگوی سینگلتون محسوب می‌شود.
  • NetworkStateHolder یک وهله از NetworkState است، اما مقادیر در مشخصه holder ذخیره شده‌اند. Holder یک NetworkStateImp و قابل ویرایش، اما خصوصی است و نکته اول فوق را برآورده می‌سازد.
  • registerConnectivityBroadcaster یک اکستنشن Application است که holder را به callback اتصال می‌دهد و نکته سوم فوق را برآورده می‌سازد.

برای راه‌اندازی همه معماری نظارتی کافی است registerConnectivityBroadcaster را به صورت زیر فراخوانی کنیم:

اکنون تنها نکته دوم فوق باقی مانده است یعنی تغییرات شبکه را منتشر کنیم. یک روش قدیمی در این مورد می‌تواند استفاده از نوعی الگوی Broadcaster -> Intent -> Receiver -> Function برای دریافت رویدادها در اکتیویتی‌ها باشد.

اما قرار نیست همیشه در گذشته بمانیم و باید شروع به استفاده از راهکارهای جت پک اندروید بکنیم. به این منظور می‌خواهیم از کامپوننت‌های معماری بهره بگیریم.

بررسی وضعیت اتصال اینترنت در اندروید Q

استفاده از نوعی LiveData

ما با استفاده از LiveData نکته فوق فهرست مشکلات فوق را که پیش‌تر ارائه کردیم حل می‌کنیم، یعنی رویدادهای اتصال‌پذیری را به بقیه بخش‌های اپلیکیشن اطلاع‌رسانی می‌کنیم. ابتدا باید برخی رویدادها را تعریف کنیم. در این مورد نیز از کد کاتلین استفاده می‌کنیم:

در کد ساده فوق چند الگو تعریف شده‌اند:

  • Enumeration – کلاس sealed امکان مجموعه محدودی از انواع مبتنی بر Event را فراهم می‌سازد که همه آن‌ها درون event قرار دارند و خواندشان آسان است.
  • Polymorphism – هر وهله از Event یک حالت شبکه را نگهداری می‌کند، اما برخی داده‌های خاص مرتبط با تغییر رخ داده نیز ذخیره کرده است.
  • Abstraction – اگر هیچ داده‌های لازم نباشد، می‌توانیم صرفاً از نوع برای مدیریت رویداد استفاده کنیم که دقیقاً مشابه مدیریت likeException است. این حالت در مورد ConnectivityLost و ConnectivityAvailable مصداق دارد. همچنین برای سادگی بیشتر استفاده از object/singletons استفاده شده است.

به فایل‌های زیر توجه کنید:

  • فایل NetworkEvents.kt
  • فایل NetworkStateImp.kt
اکنون موارد زیر را داریم:

NetworkEvents یک LiveData است و با استفاده از NetworkEvents.observe(lifecycleowner, observer) می‌توان آن را از هر LifeCycleOwner مشاهده کرد. همچنین می‌توان از هر مکانی که دارای NetworkEvents.observeForever(observer) باشد آن را مشاهده کرد.

Notify تابعی است که برای ارسال رویدادهای جدید استفاده می‌شود. در NetworkHolderImp که قبلاً ارائه کردیم با افزودن یک setter که NetworkEvents.notify را فراخوانی می‌کند، می‌توانیم هر چیزی که تغییر یافته را به اطلاع همه observer-ها برسانیم.

سخن پایانی

احتمالاً در این مقاله برخی کلیدواژه‌های internal را در ابتدای کلاس‌ها و مشخصه‌ها دیدید. منظور از آن این است که تنها از درون ماژول قابل مشاهده هستند. به بیان خلاصه کد را در ماژول خاص خود قرار می‌دهیم و نمایانی آن را به کلاس‌های خودمان محدود می‌کنیم. برای مشاهده کد کامل این پروژه می‌توانید به این رپیوی گیت‌هاب (+) بروید.

اگر این مطلب برای شما مفید بوده است، آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

میثم لطفی (+)

«میثم لطفی» دانش‌آموخته ریاضیات و شیفته فناوری به خصوص در حوزه رایانه است. وی در حال حاضر علاوه بر پیگیری علاقه‌مندی‌هایش در رشته‌های برنامه‌نویسی، کپی‌رایتینگ و محتوای چندرسانه‌ای، در زمینه نگارش مقالاتی با محوریت نرم‌افزار نیز با مجله فرادرس همکاری دارد.

آیا این مطلب برای شما مفید بود؟

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد. بخش‌های موردنیاز علامت‌گذاری شده‌اند *