نمای کلی خدمات

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

احتیاط: یک سرویس در رشته اصلی فرآیند میزبانی آن اجرا می شود. این سرویس رشته خود را ایجاد نمی کند و در یک فرآیند جداگانه اجرا نمی شود مگر اینکه شما خلاف آن را مشخص کنید. برای جلوگیری از خطاهای Application Not Responding (ANR) باید هرگونه عملیات مسدود کردن را در یک رشته جداگانه در سرویس اجرا کنید.

انواع خدمات

این سه نوع خدمات مختلف است:

پیش زمینه

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

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

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

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

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

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

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

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

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

انتخاب بین یک سرویس و یک موضوع

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

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

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

اصول اولیه

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

یک جزء برنامه مانند یک اکتیویتی می تواند با فراخوانی startService() و ارسال یک Intent که سرویس را مشخص می کند و شامل هر داده ای برای استفاده از سرویس است، سرویس را راه اندازی کند. سرویس این Intent در متد onStartCommand() دریافت می کند.

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

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

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

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

بخش‌های زیر نحوه پیاده‌سازی سرویس سفارشی خود را توضیح می‌دهند، با این حال باید قویاً استفاده از 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 کار می کند و تمام درخواست ها را به صورت سریال یکی پس از دیگری پردازش می کند. برای مثال، اگر می‌خواهید چندین درخواست را به طور همزمان اجرا کنید، می‌توانید کد را برای اجرای کار روی یک Thread Pool تغییر دهید.

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

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

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

راه اندازی یک سرویس

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

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

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

کاتلین

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

جاوا

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

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

اگر سرویس نیز binding را ارائه ندهد، هدفی که با 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() برای unbind فرا می خواند. هنگامی که هیچ مشتری محدودی به سرویس وجود ندارد، سیستم سرویس را از بین می برد.

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

ارسال نوتیفیکیشن به کاربر

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

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

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

مدیریت چرخه عمر یک سرویس

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

چرخه عمر سرویس - از زمانی که ایجاد می شود تا زمانی که از بین می رود - می تواند یکی از این دو مسیر را دنبال کند:

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

    این سرویس زمانی ایجاد می شود که مؤلفه دیگری 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
    }
}

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

شکل 2. چرخه عمر سرویس. نمودار سمت چپ چرخه عمر زمانی که سرویس با startService() ایجاد می شود و نمودار سمت راست چرخه عمر زمانی که سرویس با bindService() ایجاد می شود را نشان می دهد.

شکل 2 روش های معمولی برگشت تماس را برای یک سرویس نشان می دهد. اگرچه این شکل سرویس‌هایی را که توسط 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() تنها تماس برگشتی دریافت شده است.

برای کسب اطلاعات بیشتر در مورد ایجاد سرویسی که binding را ارائه می‌کند، به سند Bound Services مراجعه کنید، که شامل اطلاعات بیشتر در مورد روش onRebind() onRebind در بخش مدیریت چرخه حیات یک سرویس محدود است .