انتخاب بین یک سرویس و یک ترد
یک سرویس صرفاً مؤلفهای است که میتواند در پسزمینه اجرا شود. اجرای یک سرویس از طریق متدهای مختلف انجام میشود که در ادامه درباره آنها توضیح داده ایم. حتی زمانی که کاربر با برنامه شما در تعامل نیست. بنابراین فقط در صورتی باید سرویسی را اجرا کنید که به آن نیاز دارید. اگر باید کاری را خارج از ترد اصلی خود برنامه انجام دهید، اما فقط زمانی که کاربر با برنامه شما تعامل دارد، باید در عوض یک ترد جدید در context یک برنامه دیگر ایجاد کنید. برای مثال، اگر میخواهید موسیقی پخش کنید، اما فقط زمانی که اکتیویتی شما در حال اجرا است، میتوانید یک ترد در متد onCreate() ایجاد کنید، آن را در متد onStart() اجرا کنید و آن را در متد onStop() متوقف کنید.
همچنین به جای کلاس Thread سنتی، از Thread Pool و اجراکنندههای بسته java.util.concurrent یا Kotlin coroutines استفاده کنید. به یاد داشته باشید که اگر از یک سرویس استفاده می کنید، که همچنان به طور پیش فرض در ترد اصلی برنامه شما اجرا می شود، بنابراین اگر یک عملیات فشرده یا یک عملیات مسدود کننده را انجام می دهد، باید یک ترد جدید در داخل سرویس برای انجام آن کار ایجاد کنید.
متدهای اولیه سرویس
برای ایجاد یک سرویس، باید یک زیر کلاس از کلاس Service ایجاد کنید یا از یکی از زیر کلاس های موجود آن استفاده کنید. در پیادهسازی یک سرویس، باید برخی از متدهای بازگشتی را override کنید تا بتواند جنبههای کلیدی چرخه عمر سرویس را کنترل کنید و بتواند مکانیزمی را ارائه کند که در صورت لزوم به کامپوننت های دیگری مانند اکتیویتی ها اجازه دهد به سرویس متصل شوند. اینها مهمترین متدهای یک سرویس هستند که باید آنها را override کنید:
onStartCommand()
هنگامی که مؤلفه دیگری (مانند یک اکتیویتی) درخواست راه اندازی سرویس را می دهد، سیستم با فراخوانی startService() این متد را فراخوانی می کند. هنگامی که این متد اجرا می شود، سرویس راه اندازی می شود و می تواند به طور نامحدود در پس زمینه اجرا شود. اگر این متد را اجرا کنید، این مسئولیت شماست که با فراخوانی stopSelf() یا stopService() سرویس را پس از اتمام کار آن متوقف کنید. اگر فقط می خواهید binding را ارائه دهید یعنی تنها نیاز دارید که به آن متصل شود و در هنگام بالا بودن برنامه از آن استفاده کنید، نیازی به پیاده سازی این متد ندارید.
onBind()
هنگامی که مؤلفه دیگری میخواهد به سرویس متصل شود (مانند اجرای RPC) سیستم این متد را با فراخوانی bindService() فراخوانی میکند. در اجرای این متد، باید رابطی ارائه کنید که کلاینت ها از آن برای برقراری ارتباط با سرویس با بازگرداندن یک IBinder استفاده کنند. همیشه باید این متد را اجرا کنید. با این حال، اگر نمیخواهید اتصال را مجاز کنید، باید null را در نتیجه برگردانید.
onCreate()
هنگامی که سرویس در ابتدا ایجاد شدن است (قبل از فراخوانی متدهای onStartCommand() یا onBind()) سیستم این متد را برای انجام مراحل راه اندازی یکباره فراخوانی می کند. اگر سرویس از قبل در حال اجرا باشد، این متد فراخوانی نمی شود.
onDestroy()
سیستم زمانی این متد را فراخوانی می کند که سرویس دیگر استفاده نمی شود و در حال نابودی است. سرویس شما باید از این متد برای پاکسازی منابعی مانند رشته ها، لیسینر های ثبت شده یا گیرنده ها استفاده کنید. این آخرین متدی است که سرویس صدا می زند.
نتیجه فراخوانی متدهای اجرا کننده
اگر کامپوننتی سرویس را با فراخوانی متد ()startService (که منجر به فراخوانی متد ()onStartCommand میشود)شروع کند، سرویس به کار خود ادامه میدهد تا زمانی که با stopSelf() متوقف شود یا مؤلفه دیگری با فراخوانی stopService() آن را متوقف کند. اگر کامپوننتی متد ()bindService را برای ایجاد سرویس فراخوانی کند و ()onStartCommand فراخوانی نشود، سرویس فقط تا زمانی اجرا می شود که کامپوننت به آن متصل باشد. پس از اینکه سرویس از همه کلاینت ها جدا شد، سیستم آن را از بین می برد. از این نظر باید دقت داشته باشید متدهایی که از یک سرویس اجرا میکنید بسیار مهم است.
سیستم اندروید یک سرویس را فقط زمانی متوقف می کند که حافظه کم باشد و باید منابع سیستم را برای فعالیتی که تمرکز کاربر روی آن است بازیابی کند. اگر سرویس به اکتیویتی محدود شود که تمرکز کاربر روی آن است، احتمال kill شدن آن کمتر است. سرویس هایی که در پیش زمینه اجرا شود، به ندرت از بین می رود. یک سرویسی اگر راهاندازی شود و بطور طولانی مدت در حال اجرا باشد، سیستم به مرور زمان جایگاه آن سرویس را در لیست وظایف پسزمینه کاهش میدهد. بعد کاهش جایگاه سرویس به شدت حساس به از بین بردن آن میشود.
باید سرویس را طوری طراحی کنید که بعد از اجرا شدن بهخوبی راهاندازی مجدد را توسط سیستم انجام دهد. سیستم اگر سرویس شما را از بین ببرد، به محض در دسترس قرار گرفتن منابع، آن را مجددا راه اندازی می کند. این راه اندازی مجدد به مقداری که در نتیجه متد ()onStartCommand برمی گردانید نیز بستگی دارد.
اعلام سرویس در مانیفست
شما باید تمام سرویس ها را در فایل مانیفست برنامه خود اعلام کنید، همانطور که برای اکتیویتی ها و سایر مؤلفه ها اینکار را انجام می دهید. برای اعلام سرویس خود، یک عنصر <service> را به عنوان فرزند عنصر <application> اضافه کنید. به عنوان مثال:
<manifest ... > ... <application ... > <service android:name=".ExampleService" /> ... </application> </manifest>
ویژگیهای دیگری نیز وجود دارد که میتوانید در عنصر <service> برای تعریف ویژگیهایی مانند مجوزهای لازم برای شروع سرویس و فرآیندی که سرویس باید در آن اجرا شود، قرار دهید. ویژگی android:name تنها ویژگی مورد نیاز است که باید نام کلاس سرویس را مشخص می کند. پس از انتشار برنامه خود، این نام را باید بدون تغییر بگذارید. تا از خطر شکستن کد به دلیل وابستگی به اهداف صریح برای شروع یا اتصال سرویس جلوگیری کنید.
با قرار دادن ویژگی android:exported و تنظیم آن بر روی false می توانید اطمینان حاصل کنید که سرویس شما فقط برای برنامه شما در دسترس است. این به طور مؤثری دیگر برنامهها را از راهاندازی سرویس شما باز میدارد، حتی زمانی که از یک intent صریح استفاده میکنید.
نکات ایمینی برای اجرا سرویس
احتیاط: برای اطمینان از ایمن بودن برنامهتان، همیشه هنگام راهاندازی یک سرویس از یک Intent صریح استفاده کنید و فیلترهای Intent را برای سرویس خود اعلام نکنید. استفاده از یک Intent ضمنی برای شروع یک سرویس یک خطر امنیتی است. زیرا نمی توانید از سرویسی که به Intent پاسخ می دهد مطمئن باشید و کاربر نمی تواند ببیند کدام سرویس شروع می شود. با شروع Android 5.0 (سطح API 21)، اگر شما ()bindService را با یک هدف ضمنی فراخوانی کنید، سیستم یک استثنا ایجاد می کند.
نکته : کاربران می توانند ببینند که چه سرویس هایی در دستگاهشان اجرا می شود. اگر سرویسی را ببینند که نمی شناسند یا به آن اعتماد ندارند، می توانند سرویس را متوقف کنند. برای جلوگیری از توقف تصادفی سرویس شما توسط کاربران، باید ویژگی android:description را به عنصر <service> در مانیفست برنامه خود اضافه کنید. در توضیحات سرویس، جمله کوتاهی ارائه دهید که توضیح دهید این سرویس چه کاری انجام می دهد و چه مزایایی ارائه می دهد.