نمای کلی سرویس‌ها

Service یک مؤلفه برنامه است که می‌تواند عملیات طولانی‌مدت را در پس‌زمینه اجرا کند. این برنامه میانای کاربر ارائه نمی‌دهد. پس‌از شروع، سرویس ممکن است برای مدتی به اجرا ادامه دهد، حتی پس‌از اینکه کاربر به برنامه دیگری برود. علاوه‌براین، یک مؤلفه می‌تواند به سرویسی متصل شود تا با آن تعامل داشته باشد و حتی ارتباط میان پردازشی (IPC) برقرار کند. برای مثال، یک سرویس می‌تواند تراکنش‌های شبکه را مدیریت کند، موسیقی پخش کند، ورودی/خروجی فایل را انجام دهد، یا با ارائه‌دهنده محتوا تعامل داشته باشد، همه این‌ها از پس‌زمینه انجام می‌شود.

احتیاط: سرویس در رشته اصلی فرایند میزبانی‌اش اجرا می‌شود؛ سرویس رشته خودش را ایجاد نمی‌کند و در فرایند جداگانه‌ای اجرا نمی‌شود، مگراینکه شما چیز دیگری مشخص کنید. برای جلوگیری از خطاهای «برنامه پاسخ نمی‌دهد» (ANR)، باید هرگونه عملیات مسدودسازی را در رشته‌ای جداگانه در سرویس اجرا کنید.

انواع خدمات

این سه نوع مختلف خدمات عبارت‌اند از:

پیش‌نما

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

وقتی از سرویس پیش‌زمینه‌ای استفاده می‌کنید، باید اعلانی نمایش دهید تا کاربران به‌طور فعال از اجرای سرویس مطلع شوند. این اعلان را نمی‌توان بست مگراینکه سرویس متوقف شود یا از پیش‌زمینه برداشته شود.

درباره نحوه پیکربندی سرویس‌های پیش‌زمینه‌ای در برنامه‌تان بیشتر بدانید.

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

پس‌زمینه
سرویس پس‌زمینه‌ای عملیاتی را انجام می‌دهد که مستقیماً توسط کاربر مشاهده نمی‌شود. برای مثال، اگر برنامه‌ای از سرویسی برای فشرده‌سازی فضای ذخیره‌سازی‌اش استفاده کند، این سرویس معمولاً سرویس پس‌زمینه‌ای است.

توجه: اگر برنامه شما سطح میانای برنامه‌سازی کاربردی ۲۶ یا بالاتر را هدف قرار دهد، سیستم محدودیت‌هایی را برای اجرای سرویس‌های پس‌زمینه‌ای اعمال می‌کند، زمانی که خود برنامه در پیش‌زمینه نباشد. برای مثال، در اکثر شرایط نباید از پس‌زمینه به اطلاعات مکان دسترسی داشته باشید. درعوض، تکالیف را بااستفاده از WorkManager زمان‌بندی کنید.

محدود
وقتی مؤلفه برنامه با فراخوانی bindService() به سرویس متصل می‌شود، سرویس متصل می‌شود. خدمات محدودکننده یک میانای سرویس‌گیرنده-سرویس‌دهنده ارائه می‌دهد که به مؤلفه‌ها اجازه می‌دهد با سرویس تعامل داشته باشند، درخواست ارسال کنند، نتایج را دریافت کنند، و حتی این کار را در سراسر فرایندها با ارتباط میان‌فرایندی (IPC) انجام دهند. سرویس محدودکننده فقط تا زمانی اجرا می‌شود که مؤلفه برنامه دیگری به آن محدود شده باشد. چندین مؤلفه می‌توانند به‌طور هم‌زمان به سرویس متصل شوند، اما وقتی همه آن‌ها اتصالشان را قطع کنند، سرویس ازبین می‌رود.

اگرچه این مستندات به‌طور کلی خدمات شروع‌شده و محدودکننده را به‌طور جداگانه مورد بحث قرار می‌دهد، سرویس شما می‌تواند به هر دو روش کار کند—می‌تواند شروع شود (تا به‌طور نامحدود اجرا شود) و همچنین اجازه دهد اتصال. این موضوع صرفاً به این بستگی دارد که آیا چند روش فراخوانی برگشتی را پیاده‌سازی می‌کنید یا نه: onStartCommand() برای اینکه به مؤلفه‌ها اجازه دهید آن را شروع کنند و onBind() برای اینکه اتصال را مجاز کنید.

صرف‌نظر از اینکه سرویس شما شروع شده، مقید شده، یا هر دو، هر مؤلفه برنامه می‌تواند از سرویس (حتی از برنامه‌ای جداگانه) به همان روشی که هر مؤلفه می‌تواند از فعالیت استفاده کند، استفاده کند—با شروع آن با Intent. بااین‌حال، می‌توانید سرویس را در فایل مانیفست به‌عنوان خصوصی اعلام کنید و دسترسی سایر برنامه‌ها را مسدود کنید. این موضوع در بخش مربوط به اعلام سرویس در مانیفست بیشتر مورد بحث قرار گرفته است.

انتخاب بین سرویس و رشته

سرویس صرفاً مؤلفه‌ای است که می‌تواند در پس‌زمینه اجرا شود، حتی زمانی که کاربر با برنامه شما تعامل ندارد، بنابراین فقط درصورتی باید سرویس ایجاد کنید که به آن نیاز داشته باشید.

اگر باید کاری را خارج از رشته اصلی‌تان انجام دهید، اما فقط زمانی‌که کاربر با برنامه‌تان تعامل دارد، باید به‌جای آن رشته جدیدی در زمینه مؤلفه برنامه دیگری ایجاد کنید. برای مثال، اگر می‌خواهید موسیقی پخش کنید، اما فقط زمانی‌که فعالیتتان درحال اجرا است، می‌توانید رشته‌ای در onCreate() ایجاد کنید، اجرای آن را در onStart() شروع کنید، و آن را در onStop() متوقف کنید. همچنین، استفاده از استخرهای رشته و اجراکننده‌ها از بسته java.util.concurrent یا روال‌های هم‌زمان Kotlin را به‌جای کلاس سنتی Thread درنظر بگیرید. برای کسب اطلاعات بیشتر درباره انتقال اجرا به رشته‌های پس‌زمینه، به سند رشته‌بندی در Android مراجعه کنید.

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

اصول اولیه

برای ایجاد سرویس، باید زیرکلاسی از Service ایجاد کنید یا از یکی از زیرکلاس‌های موجود آن استفاده کنید. در پیاده‌سازی خود، باید برخی‌از روش‌های callback را که جنبه‌های کلیدی چرخه حیات سرویس را مدیریت می‌کنند لغو کنید و سازوکاری ارائه دهید که به مؤلفه‌ها اجازه می‌دهد درصورت لزوم به سرویس متصل شوند. این‌ها مهم‌ترین روش‌های فراخوانی هستند که باید لغو کنید:

onStartCommand()
وقتی مؤلفه دیگری (مثل فعالیت) درخواست می‌کند که سرویس شروع شود، سیستم این روش را با فراخوانی startService() اجرا می‌کند. وقتی این روش اجرا می‌شود، سرویس شروع می‌شود و می‌تواند به‌طور نامحدود در پس‌زمینه اجرا شود. اگر این را پیاده‌سازی کنید، مسئولیت توقف سرویس پس‌از تکمیل کار آن با فراخوانی stopSelf() یا stopService() برعهده شما است. اگر فقط می‌خواهید اتصال را ارائه دهید، نیازی نیست این روش را پیاده‌سازی کنید.
onBind()
وقتی مؤلفه دیگری می‌خواهد به سرویس متصل شود (مثلاً برای اجرای RPC)، سیستم این روش را با فراخوانی bindService() اجرا می‌کند. در پیاده‌سازی این روش، باید واسطی ارائه دهید که کلاینت‌ها با برگرداندن IBinder از آن برای برقراری ارتباط با سرویس استفاده کنند. همیشه باید این روش را پیاده‌سازی کنید؛ اما اگر نمی‌خواهید اتصال را مجاز کنید، باید null را برگردانید.
onCreate()
سیستم این روش را برای انجام رویه‌های راه‌اندازی یک‌باره هنگام ایجاد اولیه سرویس (قبل‌از فراخوانی onStartCommand() یا onBind()) فراخوانی می‌کند. اگر سرویس ازقبل درحال اجرا باشد، این روش فراخوانی نمی‌شود.
onDestroy()
وقتی دیگر از سرویس استفاده نشود و سرویس درحال ازبین رفتن باشد، سیستم این روش را فراخوانی می‌کند. سرویس شما باید این را برای پاک کردن منابعی مثل رشته‌ها، شنوندگان ثبت‌شده، یا گیرنده‌ها پیاده‌سازی کند. این آخرین تماسی است که سرویس دریافت می‌کند.

اگر مؤلفه‌ای سرویس را با فراخوانی startService() شروع کند (که منجر به فراخوانی onStartCommand() می‌شود)، سرویس به اجرا ادامه می‌دهد تا زمانی که خودش را با stopSelf() متوقف کند یا مؤلفه دیگری آن را با فراخوانی stopService() متوقف کند.

اگر مؤلفه‌ای bindService() را برای ایجاد سرویس فراخوانی کند و onStartCommand() فراخوانی نشود، سرویس فقط تا زمانی اجرا می‌شود که مؤلفه به آن مقید باشد. پس‌از اینکه سرویس از همه کلاینت‌هایش جدا شد، سیستم آن را ازبین می‌برد.

سیستم Android فقط زمانی سرویسی را متوقف می‌کند که حافظه کم باشد و باید منابع سیستم را برای فعالیتی که تمرکز کاربر روی آن است بازیابی کند. اگر سرویس به فعالیتی محدود شود که کاربر روی آن تمرکز دارد، احتمال ازبین رفتن آن کمتر است؛ اگر سرویس برای اجرا در پیش‌زمینه اعلام شود، به‌ندرت ازبین می‌رود. اگر سرویس شروع شده باشد و طولانی‌مدت باشد، سیستم به‌مرور زمان جایگاه آن را در فهرست تکالیف پس‌زمینه پایین می‌آورد و سرویس به‌شدت درمعرض توقف اجباری قرار می‌گیرد. اگر سرویس شما شروع شده است، باید آن را طوری طراحی کنید که بازراه‌اندازی‌های سیستم را به‌خوبی مدیریت کند. اگر سیستم سرویس شما را متوقف کند، به‌محض دردسترس قرار گرفتن منابع، آن را بازراه‌اندازی می‌کند، اما این امر به مقداری که از onStartCommand() برمی‌گردانید نیز بستگی دارد. برای اطلاعات بیشتر درباره اینکه سیستم چه زمانی ممکن است سرویسی را ازبین ببرد، به سند فرایندها و رشته‌ها مراجعه کنید.

در بخش‌های زیر، خواهید دید که چگونه می‌توانید روش‌های سرویس startService() و bindService() را ایجاد کنید و همچنین چگونه از آن‌ها در سایر مؤلفه‌های برنامه استفاده کنید.

اعلام سرویس در مانیفست

باید همه سرویس‌ها را در فایل مانیفست برنامه‌تان اعلام کنید، درست همان‌طور که برای فعالیت‌ها و مؤلفه‌های دیگر انجام می‌دهید.

برای اعلام سرویس، عنصر <service> را به‌عنوان فرزند عنصر <application> اضافه کنید. به‌عنوان مثال:

<manifest ... >
  ...
  <application ... >
      <service android:name=".ExampleService" />
      ...
  </application>
</manifest>

برای اطلاعات بیشتر درباره اعلام سرویس در مانیفست، به مرجع عنصر <service> مراجعه کنید.

مشخصه‌های دیگری وجود دارد که می‌توانید در عنصر <service> بگنجانید تا ویژگی‌هایی مانند اجازه‌های لازم برای شروع سرویس و فرایندی را که سرویس باید در آن اجرا شود تعریف کنید. مشخصه android:name تنها مشخصه الزامی است—این مشخصه نام کلاس سرویس را تعیین می‌کند. پس‌از انتشار برنامه‌تان، این نام را تغییر ندهید تا از خطر خراب شدن کد به‌دلیل وابستگی به هدف‌های صریح برای شروع یا مقید کردن سرویس جلوگیری شود (پست وبلاگ مواردی که نمی‌توانند تغییر کنند را بخوانید).

احتیاط: برای اطمینان از ایمن بودن برنامه‌تان، همیشه هنگام شروع Service از قصد صریح استفاده کنید و فیلترهای قصد را برای سرویس‌هایتان اعلام نکنید. استفاده از هدف غیرصریح برای شروع سرویس خطری امنیتی است زیرا نمی‌توانید از سرویسی که به هدف پاسخ می‌دهد مطمئن باشید و کاربر نمی‌تواند ببیند کدام سرویس شروع می‌شود. از Android 5.0 (سطح میانای برنامه‌سازی کاربردی ۲۱)، اگر bindService() را با هدف ضمنی فراخوانی کنید، سیستم استثنایی را ایجاد می‌کند.

با گنجاندن مشخصه android:exported و تنظیم آن روی false می‌توانید مطمئن شوید که سرویس شما فقط برای برنامه شما دردسترس است. این کار به‌طور مؤثری مانع از راه‌اندازی سرویس شما توسط برنامه‌های دیگر می‌شود، حتی هنگام استفاده از هدف صریح.

توجه: کاربران می‌توانند ببینند چه سرویس‌هایی در دستگاهشان درحال اجرا است. اگر سرویسی را ببینند که نمی‌شناسند یا به آن اعتماد ندارند، می‌توانند آن سرویس را متوقف کنند. برای جلوگیری از توقف تصادفی سرویس توسط کاربران، باید ویژگی android:description را به عنصر <service> در مانیفست برنامه‌تان اضافه کنید. در شرح، جمله کوتاهی ارائه دهید که توضیح دهد این سرویس چه کاری انجام می‌دهد و چه مزایایی ارائه می‌دهد.

ایجاد سرویس شروع‌شده

سرویس شروع‌شده سرویسی است که مؤلفه دیگری با فراخوانی startService() آن را شروع می‌کند، که منجر به فراخوانی روش onStartCommand() سرویس می‌شود.

وقتی سرویسی شروع می‌شود، چرخه حیاتی دارد که مستقل از مؤلفه‌ای است که آن را شروع کرده است. این سرویس می‌تواند به‌طور نامحدود در پس‌زمینه اجرا شود، حتی اگر مؤلفه‌ای که آن را راه‌اندازی کرده است ازبین برود. به همین دلیل، سرویس باید پس‌از تکمیل کارش با فراخوانی stopSelf() خودش را متوقف کند، یا مؤلفه دیگری می‌تواند با فراخوانی stopService() آن را متوقف کند.

مؤلفه برنامه، مثل فعالیت، می‌تواند با فراخوانی startService() و ارسال Intent که سرویس را مشخص می‌کند و شامل داده‌هایی برای استفاده سرویس است، سرویس را شروع کند. سرویس این Intent را در روش onStartCommand() دریافت می‌کند.

برای مثال، فرض کنید فعالیتی باید برخی‌از داده‌ها را در پایگاه داده آنلاین ذخیره کند. فعالیت می‌تواند سرویس همراهی را شروع کند و داده‌ها را برای ذخیره کردن با ارسال هدف به startService() به آن تحویل دهد. سرویس قصد را در onStartCommand() دریافت می‌کند، به اینترنت متصل می‌شود، و تراکنش پایگاه داده را انجام می‌دهد. وقتی تراکنش کامل شد، سرویس خودش متوقف می‌شود و ازبین می‌رود.

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

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

چارچوب Android همچنین زیرکلاس IntentService از Service را ارائه می‌دهد که از یک رشته عامل برای رسیدگی به همه درخواست‌های شروع، یکی پس‌از دیگری، استفاده می‌کند. استفاده از این کلاس برای برنامه‌های جدید توصیه نمی‌شود زیرا به‌دلیل معرفی محدودیت‌های اجرای پس‌زمینه، از Android 8 Oreo به‌بعد به‌خوبی کار نخواهد کرد. علاوه‌براین، از Android 11 منسوخ شده است. می‌توانید از JobIntentService به‌عنوان جایگزینی برای IntentService استفاده کنید که با نسخه‌های جدیدتر Android سازگار است.

بخش‌های زیر نحوه پیاده‌سازی سرویس سفارشی خودتان را شرح می‌دهند، اما برای اکثر موارد استفاده، باید به‌طور جدی استفاده از WorkManager را به‌جای آن درنظر بگیرید. راهنمای پردازش پس‌زمینه در Android را بررسی کنید تا ببینید آیا راه‌حلی متناسب با نیازهایتان وجود دارد یا نه.

گسترش کلاس سرویس

می‌توانید کلاس Service را گسترش دهید تا هر قصد ورودی را مدیریت کنید. در اینجا نحوه اجرای اولیه نشان داده شده است:

کاتلین

class HelloService : Service() {

    private var serviceLooper: Looper? = null
    private var serviceHandler: ServiceHandler? = null

    // Handler that receives messages from the thread
    private inner class ServiceHandler(looper: Looper) : Handler(looper) {

        override fun handleMessage(msg: Message) {
            // Normally we would do some work here, like download a file.
            // For our sample, we just sleep for 5 seconds.
            try {
                Thread.sleep(5000)
            } catch (e: InterruptedException) {
                // Restore interrupt status.
                Thread.currentThread().interrupt()
            }

            // Stop the service using the startId, so that we don't stop
            // the service in the middle of handling another job
            stopSelf(msg.arg1)
        }
    }

    override fun onCreate() {
        // Start up the thread running the service.  Note that we create a
        // separate thread because the service normally runs in the process's
        // main thread, which we don't want to block.  We also make it
        // background priority so CPU-intensive work will not disrupt our UI.
        HandlerThread("ServiceStartArguments", Process.THREAD_PRIORITY_BACKGROUND).apply {
            start()

            // Get the HandlerThread's Looper and use it for our Handler
            serviceLooper = looper
            serviceHandler = ServiceHandler(looper)
        }
    }

    override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
        Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show()

        // For each start request, send a message to start a job and deliver the
        // start ID so we know which request we're stopping when we finish the job
        serviceHandler?.obtainMessage()?.also { msg ->
            msg.arg1 = startId
            serviceHandler?.sendMessage(msg)
        }

        // If we get killed, after returning from here, restart
        return START_STICKY
    }

    override fun onBind(intent: Intent): IBinder? {
        // We don't provide binding, so return null
        return null
    }

    override fun onDestroy() {
        Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show()
    }
}

جاوا

public class HelloService extends Service {
  private Looper serviceLooper;
  private ServiceHandler serviceHandler;

  // Handler that receives messages from the thread
  private final class ServiceHandler extends Handler {
      public ServiceHandler(Looper looper) {
          super(looper);
      }
      @Override
      public void handleMessage(Message msg) {
          // Normally we would do some work here, like download a file.
          // For our sample, we just sleep for 5 seconds.
          try {
              Thread.sleep(5000);
          } catch (InterruptedException e) {
              // Restore interrupt status.
              Thread.currentThread().interrupt();
          }
          // Stop the service using the startId, so that we don't stop
          // the service in the middle of handling another job
          stopSelf(msg.arg1);
      }
  }

  @Override
  public void onCreate() {
    // Start up the thread running the service. Note that we create a
    // separate thread because the service normally runs in the process's
    // main thread, which we don't want to block. We also make it
    // background priority so CPU-intensive work doesn't disrupt our UI.
    HandlerThread thread = new HandlerThread("ServiceStartArguments",
            Process.THREAD_PRIORITY_BACKGROUND);
    thread.start();

    // Get the HandlerThread's Looper and use it for our Handler
    serviceLooper = thread.getLooper();
    serviceHandler = new ServiceHandler(serviceLooper);
  }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId) {
      Toast.makeText(this, "service starting", Toast.LENGTH_SHORT).show();

      // For each start request, send a message to start a job and deliver the
      // start ID so we know which request we're stopping when we finish the job
      Message msg = serviceHandler.obtainMessage();
      msg.arg1 = startId;
      serviceHandler.sendMessage(msg);

      // If we get killed, after returning from here, restart
      return START_STICKY;
  }

  @Override
  public IBinder onBind(Intent intent) {
      // We don't provide binding, so return null
      return null;
  }

  @Override
  public void onDestroy() {
    Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
  }
}

کد نمونه همه تماس‌های ورودی را در onStartCommand() مدیریت می‌کند و کار را به Handler که در رشته پس‌زمینه اجرا می‌شود پست می‌کند. این مدل درست مثل IntentService کار می‌کند و همه درخواست‌ها را به‌صورت سریالی، یکی پس‌از دیگری، پردازش می‌کند. برای مثال، اگر می‌خواهید چندین درخواست را به‌طور هم‌زمان اجرا کنید، می‌توانید کد را تغییر دهید تا کار را روی یک استخر رشته اجرا کنید.

توجه داشته باشید که روش onStartCommand() باید یک عدد صحیح برگرداند. عدد صحیح مقداری است که نحوه ادامه سرویس توسط سیستم را درصورت ازبین رفتن آن توسط سیستم شرح می‌دهد. مقدار برگشتی از onStartCommand() باید یکی از ثابت‌های زیر باشد:

START_NOT_STICKY
اگر سیستم سرویس را پس‌از بازگشت onStartCommand() متوقف کند، سرویس را دوباره ایجاد نکنید، مگراینکه قصد معوقی برای ارائه وجود داشته باشد. این ایمن‌ترین گزینه برای جلوگیری از اجرای سرویس شما در مواقع غیرضروری است و زمانی که برنامه شما می‌تواند به‌سادگی هر کار ناتمامی را بازراه‌اندازی کند.
START_STICKY
اگر سیستم سرویس را پس‌از بازگشت onStartCommand() متوقف کند، سرویس را دوباره ایجاد کنید و onStartCommand() را فراخوانی کنید، اما آخرین هدف را دوباره تحویل ندهید. درعوض، سیستم onStartCommand() را با قصد پوچ فراخوانی می‌کند، مگراینکه قصد معوقی برای شروع سرویس وجود داشته باشد. در این صورت، آن هدف‌ها ارائه می‌شوند. این برای پخش‌کننده‌های رسانه (یا سرویس‌های مشابه) که فرمانی را اجرا نمی‌کنند اما به‌طور نامحدود درحال اجرا هستند و منتظر کاری هستند مناسب است.
START_REDELIVER_INTENT
اگر سیستم سرویس را پس‌از بازگشت onStartCommand() ازبین ببرد، سرویس را دوباره ایجاد کنید و onStartCommand() را با آخرین قصدی که به سرویس تحویل داده شده است فراخوانی کنید. هر قصد معوقی به‌نوبت تحویل داده می‌شود. این برای سرویس‌هایی مناسب است که فعالانه کاری را انجام می‌دهند که باید فوراً ازسر گرفته شود، مثل بارگیری فایل.

برای جزئیات بیشتر درباره این مقادیر برگشتی، به اسناد مرجع پیوندشده برای هر ثابت مراجعه کنید.

شروع کردن سرویس

می‌توانید سرویسی را از فعالیت یا مؤلفه برنامه دیگری با ارسال Intent به startService() یا startForegroundService() شروع کنید. سیستم Android روش onStartCommand() سرویس را فراخوانی می‌کند و Intent را به آن ارسال می‌کند، که مشخص می‌کند کدام سرویس باید راه‌اندازی شود.

توجه: اگر برنامه شما سطح میانای برنامه‌سازی کاربردی ۲۶ یا بالاتر را هدف قرار می‌دهد، سیستم محدودیت‌هایی را برای استفاده یا ایجاد سرویس‌های پس‌زمینه‌ای اعمال می‌کند، مگراینکه خود برنامه در پیش‌زمینه باشد. اگر برنامه‌ای نیاز به ایجاد سرویس پیش‌زمینه‌ای داشته باشد، برنامه باید startForegroundService() را فراخوانی کند. این روش یک سرویس پس‌زمینه‌ای ایجاد می‌کند، اما این روش به سیستم سیگنال می‌دهد که سرویس خود را به پیش‌زمینه ارتقا خواهد داد. پس‌از ایجاد سرویس، سرویس باید ظرف پنج ثانیه روش startForeground() خود را فراخوانی کند.

برای مثال، یک فعالیت می‌تواند سرویس نمونه در بخش قبلی (HelloService) را بااستفاده از یک قصد صریح با startService() شروع کند، همان‌طور که در اینجا نشان داده شده است:

کاتلین

startService(Intent(this, HelloService::class.java))

جاوا

startService(new Intent(this, HelloService.class));

روش startService() بلافاصله برمی‌گردد و سیستم Android روش onStartCommand() سرویس را فراخوانی می‌کند. اگر سرویس ازقبل درحال اجرا نباشد، سیستم ابتدا onCreate() را فراخوانی می‌کند و سپس onStartCommand() را فراخوانی می‌کند.

اگر سرویس اتصال را نیز ارائه ندهد، قصدی که با startService() ارائه می‌شود تنها حالت ارتباط بین مؤلفه برنامه و سرویس است. بااین‌حال، اگر می‌خواهید سرویس نتیجه‌ای را برگرداند، کارخواهی که سرویس را شروع می‌کند می‌تواند PendingIntent را برای همه‌فرستی ایجاد کند (با getBroadcast()) و آن را در Intent که سرویس را شروع می‌کند به سرویس تحویل دهد. سپس سرویس می‌تواند از همه‌فرستی برای ارائه نتیجه استفاده کند.

چندین درخواست برای شروع سرویس منجر به چندین تماس متناظر با onStartCommand() سرویس می‌شود. بااین‌حال، برای توقف سرویس، فقط یک درخواست توقف (با stopSelf() یا stopService()) لازم است.

متوقف کردن سرویس

سرویس شروع‌شده باید چرخه حیات خودش را مدیریت کند. یعنی سیستم سرویس را متوقف یا نابود نمی‌کند، مگراینکه مجبور باشد حافظه سیستم را بازیابی کند و سرویس پس‌از بازگشت onStartCommand() به اجرا ادامه می‌دهد. سرویس باید با فراخوانی stopSelf() خودش را متوقف کند، یا مؤلفه دیگری می‌تواند با فراخوانی stopService() آن را متوقف کند.

وقتی درخواست توقف با stopSelf() یا stopService() ارسال شود، سیستم در اسرع وقت سرویس را ازبین می‌برد.

اگر سرویس شما چندین درخواست را به‌طور هم‌زمان به onStartCommand() ارسال می‌کند، نباید سرویس را پس‌از اتمام پردازش درخواست شروع متوقف کنید، زیرا ممکن است درخواست شروع جدیدی دریافت کرده باشید (توقف در پایان درخواست اول باعث پایان یافتن درخواست دوم می‌شود). برای جلوگیری از این مشکل، می‌توانید از stopSelf(int) استفاده کنید تا مطمئن شوید درخواست شما برای توقف سرویس همیشه براساس جدیدترین درخواست شروع است. یعنی وقتی stopSelf(int) را فراخوانی می‌کنید، شناسه درخواست شروع (startId ارسال‌شده به onStartCommand()) را که با درخواست توقف شما مطابقت دارد ارسال می‌کنید. سپس، اگر سرویس درخواست شروع جدیدی را قبل‌از اینکه بتوانید stopSelf(int) را فراخوانی کنید دریافت کند، شناسه مطابقت ندارد و سرویس متوقف نمی‌شود.

احتیاط: برای جلوگیری از هدر رفتن منابع سیستم و مصرف شارژ باتری، مطمئن شوید که برنامه‌تان پس‌از اتمام کار، سرویس‌هایش را متوقف می‌کند. درصورت لزوم، اجزای دیگر می‌توانند با فراخوانی stopService() سرویس را متوقف کنند. حتی اگر اتصال را برای سرویس فعال کنید، اگر سرویس تماسی با onStartCommand() دریافت کند، همیشه باید خودتان سرویس را متوقف کنید.

برای اطلاعات بیشتر درباره چرخه حیات سرویس، بخش زیر را درباره مدیریت چرخه حیات سرویس ببینید.

ایجاد خدمات محدودکننده

سرویس محدودکننده سرویسی است که به مؤلفه‌های برنامه اجازه می‌دهد با فراخوانی bindService() به آن متصل شوند تا اتصالی طولانی‌مدت ایجاد کنند. به‌طورکلی به مؤلفه‌ها اجازه نمی‌دهد با فراخوانی startService() آن را شروع کنند.

وقتی می‌خواهید از فعالیت‌ها و دیگر مؤلفه‌های برنامه‌تان با سرویس تعامل داشته باشید یا برخی‌از عملکردهای برنامه‌تان را ازطریق ارتباط میان‌پردازشی (IPC) دراختیار برنامه‌های دیگر قرار دهید، سرویس محدودکننده ایجاد کنید.

برای ایجاد خدمات محدودکننده، روش بازخوان onBind() را اجرا کنید تا IBinder را برگرداند که میانای ارتباط با سرویس را تعریف می‌کند. سپس مؤلفه‌های دیگر برنامه می‌توانند bindService() را فراخوانی کنند تا میانای موردنظر را بازیابی کنند و فراخوانی روش‌ها را در سرویس شروع کنند. این سرویس فقط برای ارائه خدمات به مؤلفه برنامه‌ای که به آن متصل است وجود دارد، بنابراین وقتی هیچ مؤلفه‌ای به سرویس متصل نباشد، سیستم آن را ازبین می‌برد. لازم نیست سرویس محدودکننده را به همان روشی که باید هنگام شروع سرویس ازطریق onStartCommand() انجام دهید، متوقف کنید.

برای ایجاد خدمات محدودکننده، باید واسطی را تعریف کنید که نحوه برقراری ارتباط کارخواه با سرویس را مشخص می‌کند. این میانای بین سرویس و کارخواه باید پیاده‌سازی IBinder باشد و همان چیزی است که سرویس شما باید از روش callback (فراخوانی) onBind() برگرداند. پس‌از اینکه کارخواه IBinder را دریافت کرد، می‌تواند تعامل با سرویس را ازطریق آن میانای کاربری شروع کند.

چند مشتری می‌توانند به‌طور هم‌زمان به سرویس متصل شوند. وقتی تعامل مشتری با سرویس تمام می‌شود، برای لغو اتصال، unbindService() را فراخوانی می‌کند. وقتی هیچ کارخواهی به سرویس متصل نباشد، سیستم سرویس را ازبین می‌برد.

روش‌های متعددی برای پیاده‌سازی خدمات محدودکننده وجود دارد و پیاده‌سازی آن پیچیده‌تر از خدمات شروع‌شده است. به این دلایل، بحث خدمات محدودکننده در سند جداگانه‌ای درباره خدمات محدودکننده ارائه شده است.

درحال ارسال اعلان به کاربر

وقتی سرویسی درحال اجرا است، می‌تواند بااستفاده از اعلان‌های نوار توضیحات یا اعلان‌های نوار وضعیت رویدادها را به کاربر اطلاع دهد.

اعلان اسنک‌بار پیامی است که فقط برای یک لحظه روی سطح پنجره فعلی ظاهر می‌شود و سپس ناپدید می‌شود. اعلان نوار وضعیت نمادی را در نوار وضعیت با پیامی ارائه می‌دهد که کاربر می‌تواند آن را برای انجام کنشی (مانند شروع فعالیت) انتخاب کند.

معمولاً اعلان نوار وضعیت بهترین تکنیک برای استفاده است وقتی کار پس‌زمینه مانند بارگیری فایل تکمیل شده است و کاربر اکنون می‌تواند روی آن اقدام کند. وقتی کاربر اعلان را از نمای گسترده انتخاب می‌کند، اعلان می‌تواند فعالیتی را شروع کند (مثلاً نمایش فایل بارگیری‌شده).

مدیریت چرخه حیات سرویس

چرخه حیات سرویس بسیار ساده‌تر از چرخه حیات فعالیت است. بااین‌حال، بسیار مهم‌تر است که به نحوه ایجاد و ازبین رفتن سرویس خود توجه زیادی داشته باشید زیرا سرویس می‌تواند بدون اطلاع کاربر در پس‌زمینه اجرا شود.

چرخه حیات سرویس (از زمان ایجاد تا زمان نابودی) می‌تواند یکی از این دو مسیر را دنبال کند:

  • سرویس شروع‌شده

    این سرویس زمانی ایجاد می‌شود که مؤلفه دیگری startService() را فراخوانی کند. سپس سرویس به‌طور نامحدود اجرا می‌شود و باید خودش با فراخوانی stopSelf() متوقف شود. مؤلفه دیگری نیز می‌تواند با فراخوانی stopService()، سرویس را متوقف کند. وقتی سرویس متوقف می‌شود، سیستم آن را ازبین می‌برد.

  • خدمات محدودکننده

    وقتی مؤلفه دیگری (یک کارخواه) bindService() را فراخوانی می‌کند، سرویس ایجاد می‌شود. سپس کارخواه ازطریق میانای IBinder با سرویس ارتباط برقرار می‌کند. کارخواه می‌تواند با فراخوانی unbindService() اتصال را ببندد. چند کلاینت می‌توانند به یک سرویس متصل شوند و وقتی همه آن‌ها اتصالشان را قطع کنند، سیستم سرویس را ازبین می‌برد. سرویس نباید خودش را متوقف کند.

این دو مسیر کاملاً از هم جدا نیستند. می‌توانید به سرویسی که قبلاً با startService() شروع شده است متصل شوید. برای مثال، می‌توانید با فراخوانی startService() با Intent که موسیقی را برای پخش شناسایی می‌کند، سرویس موسیقی پس‌زمینه را شروع کنید. بعداً، احتمالاً وقتی کاربر می‌خواهد کنترلی روی پخش‌کننده داشته باشد یا اطلاعاتی درباره آهنگ فعلی دریافت کند، فعالیتی می‌تواند با فراخوانی bindService() به سرویس متصل شود. در مواردی مانند این، stopService() یا stopSelf() درواقع سرویس را متوقف نمی‌کند تا زمانی که همه کلاینت‌ها لغو اتصال کنند.

پیاده‌سازی فراخوانی‌های چرخه حیات

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

کاتلین

class ExampleService : Service() {
    private var startMode: Int = 0             // indicates how to behave if the service is killed
    private var binder: IBinder? = null        // interface for clients that bind
    private var allowRebind: Boolean = false   // indicates whether onRebind should be used

    override fun onCreate() {
        // The service is being created
    }

    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
        // The service is starting, due to a call to startService()
        return startMode
    }

    override fun onBind(intent: Intent): IBinder? {
        // A client is binding to the service with bindService()
        return binder
    }

    override fun onUnbind(intent: Intent): Boolean {
        // All clients have unbound with unbindService()
        return allowRebind
    }

    override fun onRebind(intent: Intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }

    override fun onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

جاوا

public class ExampleService extends Service {
    int startMode;       // indicates how to behave if the service is killed
    IBinder binder;      // interface for clients that bind
    boolean allowRebind; // indicates whether onRebind should be used

    @Override
    public void onCreate() {
        // The service is being created
    }
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        // The service is starting, due to a call to startService()
        return startMode;
    }
    @Override
    public IBinder onBind(Intent intent) {
        // A client is binding to the service with bindService()
        return binder;
    }
    @Override
    public boolean onUnbind(Intent intent) {
        // All clients have unbound with unbindService()
        return allowRebind;
    }
    @Override
    public void onRebind(Intent intent) {
        // A client is binding to the service with bindService(),
        // after onUnbind() has already been called
    }
    @Override
    public void onDestroy() {
        // The service is no longer used and is being destroyed
    }
}

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

شکل ۲. چرخه حیات سرویس. نمودار سمت راست چرخه حیات را وقتی سرویس با startService() ایجاد می‌شود نشان می‌دهد و نمودار سمت چپ چرخه حیات را وقتی سرویس با bindService() ایجاد می‌شود نشان می‌دهد.

شکل ۲ روش‌های فراخوانی معمول برای یک سرویس را نشان می‌دهد. اگرچه این شکل سرویس‌هایی را که توسط startService() ایجاد شده‌اند از سرویس‌هایی که توسط bindService() ایجاد شده‌اند جدا می‌کند، به‌خاطر داشته باشید که هر سرویسی، صرف‌نظر از نحوه شروع آن، می‌تواند به‌طور بالقوه به کارخواهان اجازه دهد به آن متصل شوند. سرویسی که در ابتدا با onStartCommand() شروع شده است (توسط کارخواهی که با startService() تماس می‌گیرد) همچنان می‌تواند تماسی با onBind() دریافت کند (وقتی کارخواهی با bindService() تماس می‌گیرد).

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

  • کل طول عمر یک سرویس بین زمانی که onCreate() فراخوانی می‌شود و زمانی که onDestroy() برمی‌گردد رخ می‌دهد. مانند یک فعالیت، یک سرویس راه‌اندازی اولیه خود را در onCreate() انجام می‌دهد و تمام منابع باقی‌مانده را در onDestroy() آزاد می‌کند. برای مثال، یک سرویس بازپخش موسیقی می‌تواند رشته‌ای را ایجاد کند که در آن موسیقی در onCreate() پخش می‌شود، و سپس می‌تواند رشته را در onDestroy() متوقف کند.

    توجه: روش‌های onCreate() و onDestroy() برای همه سرویس‌ها فراخوانی می‌شوند، چه توسط startService() ایجاد شده باشند چه bindService().

  • طول عمر فعال سرویس با تماس با onStartCommand() یا onBind() شروع می‌شود. هر روش Intent را که به startService() یا bindService() ارسال شده است دریافت می‌کند.

    اگر سرویس شروع شده باشد، طول عمر فعال هم‌زمان با پایان کل طول عمر به‌پایان می‌رسد (سرویس حتی پس‌از بازگشت onStartCommand() همچنان فعال است). اگر سرویس مقید باشد، طول عمر فعال وقتی onUnbind() برمی‌گردد به‌پایان می‌رسد.

توجه: اگرچه سرویس شروع‌شده با تماس با stopSelf() یا stopService() متوقف می‌شود، اما فراخوانی برگشتی مربوطه برای سرویس وجود ندارد (فراخوانی برگشتی onStop() وجود ندارد). مگر اینکه سرویس به مشتری محدود شده باشد، سیستم آن را هنگام توقف سرویس ازبین می‌برد—onDestroy() تنها فراخوانی برگشتی دریافتی است.

برای اطلاعات بیشتر درباره ایجاد سرویسی که اتصال را ارائه می‌دهد، سند سرویس‌های متصل را ببینید، که شامل اطلاعات بیشتری درباره روش فراخوانی بازگشتی onRebind() در بخش مربوط به مدیریت چرخه حیات سرویس متصل است.