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

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

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

اصول اولیه

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

اتصال به سرویس شروع‌شده

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

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

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

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

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

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

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

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

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

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

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

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

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

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

این Handler مبنای Messenger است که سپس می‌تواند IBinder را با کارخواه هم‌رسانی کند، و به کارخواه اجازه دهد بااستفاده از اشیای Message به سرویس فرمان ارسال کند. علاوه‌براین، کارخواه می‌تواند Messenger خودش را تعریف کند تا سرویس بتواند پیام‌ها را برگرداند.

این ساده‌ترین راه برای انجام ارتباط میان پردازشی (IPC) است، زیرا Messenger همه درخواست‌ها را در یک رشته صف‌بندی می‌کند تا مجبور نباشید سرویس خود را به‌گونه‌ای طراحی کنید که ازنظر رشته‌ای ایمن باشد.

استفاده از AIDL
«زبان تعریف میانای Android» (AIDL) اشیاء را به عناصر اولیه‌ای تجزیه می‌کند که سیستم‌عامل می‌تواند آن‌ها را درک کند و آن‌ها را در فرایندها برای انجام IPC مرتب می‌کند. روش قبلی، بااستفاده از Messenger، درواقع براساس AIDL به‌عنوان ساختار زیربنایی آن است.

همان‌طور که در بخش قبلی ذکر شد، Messenger صفی از همه درخواست‌های کارخواه را در یک رشته ایجاد می‌کند، بنابراین سرویس درخواست‌ها را یکی‌یکی دریافت می‌کند. اما اگر می‌خواهید سرویس شما چندین درخواست را به‌طور هم‌زمان مدیریت کند، می‌توانید مستقیماً از AIDL استفاده کنید. دراین‌صورت، سرویس شما باید ایمن از رشته باشد و قابلیت چندرشته‌ای داشته باشد.

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

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

گسترش کلاس Binder

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

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

نحوه راه‌اندازی آن در اینجا ارائه شده است:

  1. در سرویس خود، نمونه‌ای از Binder ایجاد کنید که یکی از کارهای زیر را انجام دهد:
    • حاوی روش‌های عمومی است که مشتری می‌تواند فراخوانی کند.
    • نمونه Service فعلی را برمی‌گرداند که دارای روش‌های عمومی است که کارخواه می‌تواند فراخوانی کند.
    • نمونه‌ای از کلاس دیگری را که توسط سرویس میزبانی می‌شود با روش‌های عمومی که مشتری می‌تواند فراخوانی کند برمی‌گرداند.
  2. این نمونه از Binder را از روش بازخوانی onBind() برگردانید.
  3. در کلاینت، Binder را از روش فراخوانی onServiceConnected() دریافت کنید و بااستفاده از روش‌های ارائه‌شده، با خدمات محدودکننده تماس بگیرید.

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

برای مثال، در اینجا سرویسی وجود دارد که ازطریق پیاده‌سازی Binder، دسترسی به روش‌های موجود در سرویس را برای مشتریان فراهم می‌کند:

کاتلین

class LocalService : Service() {
    // Binder given to clients.
    private val binder = LocalBinder()

    // Random number generator.
    private val mGenerator = Random()

    /** Method for clients.  */
    val randomNumber: Int
        get() = mGenerator.nextInt(100)

    /**
     * Class used for the client Binder. Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    inner class LocalBinder : Binder() {
        // Return this instance of LocalService so clients can call public methods.
        fun getService(): LocalService = this@LocalService
    }

    override fun onBind(intent: Intent): IBinder {
        return binder
    }
}

جاوا

public class LocalService extends Service {
    // Binder given to clients.
    private final IBinder binder = new LocalBinder();
    // Random number generator.
    private final Random mGenerator = new Random();

    /**
     * Class used for the client Binder.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with IPC.
     */
    public class LocalBinder extends Binder {
        LocalService getService() {
            // Return this instance of LocalService so clients can call public methods.
            return LocalService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    /** Method for clients. */
    public int getRandomNumber() {
      return mGenerator.nextInt(100);
    }
}

LocalBinder روش getService() را برای مشتریان فراهم می‌کند تا نمونه فعلی LocalService را بازیابی کنند. این کار به مشتریان اجازه می‌دهد روش‌های عمومی را در سرویس فراخوانی کنند. برای مثال، مشتریان می‌توانند ازطریق این سرویس با getRandomNumber() تماس بگیرند.

در اینجا فعالیتی وجود دارد که به LocalService متصل می‌شود و هنگام کلیک روی دکمه، getRandomNumber() را فراخوانی می‌کند:

کاتلین

class BindingActivity : Activity() {
    private var mService: LocalService? = null
    private var mBound: Boolean = false

    /** Defines callbacks for service binding, passed to bindService().  */
    private val connection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance.
            val binder = service as LocalService.LocalBinder
            mService = binder.getService()
            mBound = true
        }

        override fun onServiceDisconnected(arg0: ComponentName) {
            mService = null
            mBound = false
        }
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
    }

    override fun onStart() {
        super.onStart()
        // Bind to LocalService.
        Intent(this, LocalService::class.java).also { intent ->
            bindService(intent, connection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onStop() {
        super.onStop()
        unbindService(connection)
        // Release the reference so the service can be garbage-collected once it's
        // destroyed. onServiceDisconnected() isn't called when the client unbinds.
        mService = null
        mBound = false
    }

    /** Called when a button is clicked (the button in the layout file attaches to
     * this method with the android:onClick attribute).  */
    fun onButtonClick(v: View) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call is something that might hang, then put this request
            // in a separate thread to avoid slowing down the activity performance.
            val num: Int = mService?.randomNumber ?: return
            Toast.makeText(this, "number: $num", Toast.LENGTH_SHORT).show()
        }
    }
}

جاوا

public class BindingActivity extends Activity {
    LocalService mService;
    boolean mBound = false;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to LocalService.
        Intent intent = new Intent(this, LocalService.class);
        bindService(intent, connection, Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        unbindService(connection);
        // Release the reference so the service can be garbage-collected once it's
        // destroyed. onServiceDisconnected() isn't called when the client unbinds.
        mService = null;
        mBound = false;
    }

    /** Called when a button is clicked (the button in the layout file attaches to
      * this method with the android:onClick attribute). */
    public void onButtonClick(View v) {
        if (mBound) {
            // Call a method from the LocalService.
            // However, if this call is something that might hang, then put this request
            // in a separate thread to avoid slowing down the activity performance.
            int num = mService.getRandomNumber();
            Toast.makeText(this, "number: " + num, Toast.LENGTH_SHORT).show();
        }
    }

    /** Defines callbacks for service binding, passed to bindService(). */
    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName className,
                IBinder service) {
            // We've bound to LocalService, cast the IBinder and get LocalService instance.
            LocalBinder binder = (LocalBinder) service;
            mService = binder.getService();
            mBound = true;
        }

        @Override
        public void onServiceDisconnected(ComponentName arg0) {
            mService = null;
            mBound = false;
        }
    };
}

نمونه قبلی نشان می‌دهد که چگونه کارخواه بااستفاده از پیاده‌سازی ServiceConnection و بازخوان onServiceConnected() به سرویس متصل می‌شود. بخش بعدی اطلاعات بیشتری درباره این فرایند اتصال به سرویس ارائه می‌دهد.

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

برای کد نمونه بیشتر، به کلاس LocalService.java و کلاس LocalServiceActivities.java در ApiDemos مراجعه کنید.

استفاده از پیام‌رسان

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

استفاده از Messenger برای میانای شما ساده‌تر از استفاده از AIDL است زیرا Messenger همه تماس‌ها با سرویس را در صف قرار می‌دهد. یک میانای AIDL خالص درخواست‌های هم‌زمان را به سرویس ارسال می‌کند، که سپس باید چندرشته‌ای را مدیریت کند.

برای اکثر برنامه‌ها، سرویس نیازی به انجام چندرشته‌ای ندارد، بنابراین استفاده از Messenger به سرویس اجازه می‌دهد تا یک تماس را در هر زمان مدیریت کند. اگر مهم است که سرویس شما چندرشته‌ای باشد، از AIDL برای تعریف میانای خود استفاده کنید.

در اینجا خلاصه‌ای از نحوه استفاده از Messenger آورده شده است:

  1. این سرویس یک Handler را پیاده‌سازی می‌کند که برای هر تماس از مشتری، یک فراخوانی برگشتی دریافت می‌کند.
  2. این سرویس از Handler برای ایجاد شیء Messenger استفاده می‌کند (که مرجعی برای Handler است).
  3. Messenger یک IBinder ایجاد می‌کند که سرویس از onBind() به کارخواهان برمی‌گرداند.
  4. کارخواه‌ها از IBinder برای نمونه‌سازی Messenger (که به Handler سرویس ارجاع می‌دهد) استفاده می‌کنند، که کارخواه از آن برای ارسال اشیاء Message به سرویس استفاده می‌کند.
  5. این سرویس هر Message را در Handler خود دریافت می‌کند—به‌طور مشخص، در روش handleMessage().

به این ترتیب، هیچ روشی برای کارخواه وجود ندارد که بتواند سرویس را فراخوانی کند. درعوض، کارخواه پیام‌هایی (Message شیء) را تحویل می‌دهد که سرویس در Handler خود دریافت می‌کند.

در اینجا نمونه‌ای از سرویسی که از میانای Messenger استفاده می‌کند آورده شده است:

کاتلین

/** Command to the service to display a message.  */
private const val MSG_SAY_HELLO = 1

class MessengerService : Service() {

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    private lateinit var mMessenger: Messenger

    /**
     * Handler of incoming messages from clients.
     */
    internal class IncomingHandler(
            context: Context,
            private val applicationContext: Context = context.applicationContext
    ) : Handler() {
        override fun handleMessage(msg: Message) {
            when (msg.what) {
                MSG_SAY_HELLO ->
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show()
                else -> super.handleMessage(msg)
            }
        }
    }

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    override fun onBind(intent: Intent): IBinder? {
        Toast.makeText(applicationContext, "binding", Toast.LENGTH_SHORT).show()
        mMessenger = Messenger(IncomingHandler(this))
        return mMessenger.binder
    }
}

جاوا

public class MessengerService extends Service {
    /**
     * Command to the service to display a message.
     */
    static final int MSG_SAY_HELLO = 1;

    /**
     * Handler of incoming messages from clients.
     */
    static class IncomingHandler extends Handler {
        private Context applicationContext;

        IncomingHandler(Context context) {
            applicationContext = context.getApplicationContext();
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_SAY_HELLO:
                    Toast.makeText(applicationContext, "hello!", Toast.LENGTH_SHORT).show();
                    break;
                default:
                    super.handleMessage(msg);
            }
        }
    }

    /**
     * Target we publish for clients to send messages to IncomingHandler.
     */
    Messenger mMessenger;

    /**
     * When binding to the service, we return an interface to our messenger
     * for sending messages to the service.
     */
    @Override
    public IBinder onBind(Intent intent) {
        Toast.makeText(getApplicationContext(), "binding", Toast.LENGTH_SHORT).show();
        mMessenger = new Messenger(new IncomingHandler(this));
        return mMessenger.getBinder();
    }
}

روش handleMessage() در Handler جایی است که سرویس Message ورودی را دریافت می‌کند و براساس عضو what تصمیم می‌گیرد چه کاری انجام دهد.

تنها کاری که کارخواه باید انجام دهد این است که براساس IBinder برگردانده‌شده توسط سرویس، Messenger ایجاد کند و بااستفاده از send() پیام ارسال کند. برای مثال، در اینجا فعالیتی وجود دارد که به سرویس متصل می‌شود و پیام MSG_SAY_HELLO را به سرویس ارسال می‌کند:

کاتلین

class ActivityMessenger : Activity() {
    /** Messenger for communicating with the service.  */
    private var mService: Messenger? = null

    /** Flag indicating whether we have called bind on the service.  */
    private var bound: Boolean = false

    /**
     * Class for interacting with the main interface of the service.
     */
    private val mConnection = object : ServiceConnection {

        override fun onServiceConnected(className: ComponentName, service: IBinder) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = Messenger(service)
            bound = true
        }

        override fun onServiceDisconnected(className: ComponentName) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected—that is, its process crashed.
            mService = null
            bound = false
        }
    }

    fun sayHello(v: View) {
        if (!bound) return
        // Create and send a message to the service, using a supported 'what' value.
        val msg: Message = Message.obtain(null, MSG_SAY_HELLO, 0, 0)
        try {
            mService?.send(msg)
        } catch (e: RemoteException) {
            e.printStackTrace()
        }

    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.main)
    }

    override fun onStart() {
        super.onStart()
        // Bind to the service.
        Intent(this, MessengerService::class.java).also { intent ->
            bindService(intent, mConnection, Context.BIND_AUTO_CREATE)
        }
    }

    override fun onStop() {
        super.onStop()
        // Unbind from the service.
        if (bound) {
            unbindService(mConnection)
            mService = null
            bound = false
        }
    }
}

جاوا

public class ActivityMessenger extends Activity {
    /** Messenger for communicating with the service. */
    Messenger mService = null;

    /** Flag indicating whether we have called bind on the service. */
    boolean bound;

    /**
     * Class for interacting with the main interface of the service.
     */
    private ServiceConnection mConnection = new ServiceConnection() {
        public void onServiceConnected(ComponentName className, IBinder service) {
            // This is called when the connection with the service has been
            // established, giving us the object we can use to
            // interact with the service.  We are communicating with the
            // service using a Messenger, so here we get a client-side
            // representation of that from the raw IBinder object.
            mService = new Messenger(service);
            bound = true;
        }

        public void onServiceDisconnected(ComponentName className) {
            // This is called when the connection with the service has been
            // unexpectedly disconnected—that is, its process crashed.
            mService = null;
            bound = false;
        }
    };

    public void sayHello(View v) {
        if (!bound) return;
        // Create and send a message to the service, using a supported 'what' value.
        Message msg = Message.obtain(null, MessengerService.MSG_SAY_HELLO, 0, 0);
        try {
            mService.send(msg);
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
    }

    @Override
    protected void onStart() {
        super.onStart();
        // Bind to the service.
        bindService(new Intent(this, MessengerService.class), mConnection,
            Context.BIND_AUTO_CREATE);
    }

    @Override
    protected void onStop() {
        super.onStop();
        // Unbind from the service.
        if (bound) {
            unbindService(mConnection);
            mService = null;
            bound = false;
        }
    }
}

این مثال نشان نمی‌دهد که سرویس چگونه می‌تواند به مشتری پاسخ دهد. اگر می‌خواهید سرویس پاسخ دهد، باید یک Messenger نیز در کارخواه ایجاد کنید. وقتی کارخواه onServiceConnected() را دریافت می‌کند، یک Message به سرویس ارسال می‌کند که شامل Messenger کارخواه در پارامتر replyTo روش send() است.

می‌توانید نمونه‌ای از نحوه ارائه پیام‌رسانی دوطرفه را در نمونه‌های MessengerService.java (سرویس) و MessengerServiceActivities.java (کارخواه) ببینید.

اتصال به سرویس

مؤلفه‌های برنامه (کارخواه‌ها) می‌توانند با فراخوانی bindService() به سرویس متصل شوند. سپس سیستم Android روش onBind() سرویس را فراخوانی می‌کند که برای تعامل با سرویس، IBinder را برمی‌گرداند.

اتصال ناهمگام است و bindService() بلافاصله بدون برگرداندن IBinder به کارخواه برمی‌گردد. برای دریافت IBinder، کارخواه باید نمونه‌ای از ServiceConnection ایجاد کند و آن را به bindService() ارسال کند. ServiceConnection شامل روش فراخوانی است که سیستم برای ارائه IBinder فراخوانی می‌کند.

توجه: فقط فعالیت‌ها، سرویس‌ها، و ارائه‌دهندگان محتوا می‌توانند به سرویس متصل شوند—نمی‌توانید از گیرنده رویداد ثبت‌شده به سرویس متصل شوید.

برای اتصال به سرویسی از مشتری‌تان، این مراحل را دنبال کنید:

  1. ServiceConnection را اجرا کنید.

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

    onServiceConnected()
    سیستم این را برای ارائه IBinder برگردانده‌شده توسط روش onBind() سرویس فراخوانی می‌کند.
    onServiceDisconnected()
    سیستم Android زمانی این را فراخوانی می‌کند که اتصال به سرویس به‌طور غیرمنتظره‌ای قطع شود، مثلاً وقتی سرویس ازکار می‌افتد یا متوقف می‌شود. وقتی مشتری لغو اتصال می‌کند، این مورد فراخوانی نمی‌شود.
  2. با ارسال پیاده‌سازی ServiceConnection، با bindService() تماس بگیرید.

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

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

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

مثال زیر کارخواه را به سرویسی که قبلاً با گسترش کلاس Binder ایجاد شده است متصل می‌کند، بنابراین تنها کاری که باید انجام دهد این است که IBinder برگردانده‌شده را به کلاس LocalBinder تبدیل کند و نمونه LocalService را درخواست کند:

کاتلین

var mService: LocalService? = null

val mConnection = object : ServiceConnection {
    // Called when the connection with the service is established.
    override fun onServiceConnected(className: ComponentName, service: IBinder) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        val binder = service as LocalService.LocalBinder
        mService = binder.getService()
        mBound = true
    }

    // Called when the connection with the service disconnects unexpectedly.
    override fun onServiceDisconnected(className: ComponentName) {
        Log.e(TAG, "onServiceDisconnected")
        mService = null
        mBound = false
    }
}

جاوا

LocalService mService;
private ServiceConnection mConnection = new ServiceConnection() {
    // Called when the connection with the service is established.
    public void onServiceConnected(ComponentName className, IBinder service) {
        // Because we have bound to an explicit
        // service that is running in our own process, we can
        // cast its IBinder to a concrete class and directly access it.
        LocalBinder binder = (LocalBinder) service;
        mService = binder.getService();
        mBound = true;
    }

    // Called when the connection with the service disconnects unexpectedly.
    public void onServiceDisconnected(ComponentName className) {
        Log.e(TAG, "onServiceDisconnected");
        mService = null;
        mBound = false;
    }
};

با این ServiceConnection، کارخواه می‌تواند با ارسال آن به bindService() به سرویس متصل شود، همان‌طور که در مثال زیر نشان داده شده است:

کاتلین

Intent(this, LocalService::class.java).also { intent ->
    bindService(intent, connection, Context.BIND_AUTO_CREATE)
}

جاوا

Intent intent = new Intent(this, LocalService.class);
bindService(intent, connection, Context.BIND_AUTO_CREATE);
  • پارامتر اول bindService() یک Intent است که صریحاً نام سرویس را برای اتصال مشخص می‌کند.

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

  • پارامتر دوم شیء ServiceConnection است.
  • پارامتر سوم پرچمی است که گزینه‌های اتصال را نشان می‌دهد—معمولاً BIND_AUTO_CREATE، برای ایجاد سرویس درصورتی‌که ازقبل فعال نباشد. مقادیر ممکن دیگر عبارتند از BIND_DEBUG_UNBIND، BIND_NOT_FOREGROUND، یا 0 برای هیچ‌کدام.

نکته‌های بیشتر

در اینجا چند نکته مهم درباره اتصال به سرویس آورده شده است:

  • همیشه استثناهای DeadObjectException را که هنگام قطع اتصال ایجاد می‌شوند، به‌دام بیندازید. این تنها استثنایی است که روش‌های از دور ایجاد می‌کنند.
  • اشیا در سراسر فرایندها شمارش مرجع می‌شوند.
  • معمولاً اتصال و قطع اتصال را درطول لحظات راه‌اندازی و توقف منطبق در چرخه حیات کارخواه جفت می‌کنید، همان‌طور که در مثال‌های زیر شرح داده شده است:
    • اگر لازم است فقط زمانی‌که فعالیتتان نمایان است با سرویس تعامل داشته باشید، درطول onStart() مقید کنید و درطول onStop() لغو قید کنید.
    • اگر می‌خواهید فعالیتتان حتی زمانی که در پس‌زمینه متوقف شده است پاسخ دریافت کند، درطول onCreate() متصل کنید و درطول onDestroy() اتصال را قطع کنید. توجه داشته باشید که این امر به این معنی است که فعالیت شما باید در تمام مدتی که درحال اجرا است، حتی در پس‌زمینه، از سرویس استفاده کند، بنابراین وقتی سرویس در فرایند دیگری است، وزن فرایند را افزایش می‌دهید و احتمال بیشتری وجود دارد که توسط سیستم متوقف شود.

    توجه: معمولاً درطول فراخوانی‌های برگشتی onResume() و onPause() فعالیتتان، اتصال و قطع اتصال را انجام نمی‌دهید، زیرا این فراخوانی‌های برگشتی در هر انتقال چرخه حیات رخ می‌دهند. پردازشی را که در این انتقال‌ها رخ می‌دهد به حداقل برسانید.

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

برای مشاهده کد نمونه بیشتر که نحوه اتصال به سرویس را نشان می‌دهد، به کلاس RemoteService.java در ApiDemos مراجعه کنید.

مدیریت چرخه حیات خدمات محدودکننده

وقتی سرویسی از همه کارخواه‌ها جدا می‌شود، سیستم Android آن را ازبین می‌برد (مگر اینکه بااستفاده از startService() شروع شده باشد). بنابراین، اگر سرویس شما صرفاً یک سرویس محدودکننده باشد، نیازی به مدیریت چرخه حیات آن ندارید. سیستم Android آن را براساس اینکه به مشتری‌ای محدود شده است یا نه برای شما مدیریت می‌کند.

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

علاوه‌براین، اگر سرویس شما شروع شده باشد و اتصال را بپذیرد، وقتی سیستم روش onUnbind() شما را فراخوانی می‌کند، می‌توانید به‌صورت اختیاری true را برگردانید اگر می‌خواهید دفعه بعد که مشتری به سرویس متصل می‌شود، فراخوانی onRebind() را دریافت کنید. onRebind() مقدار void برمی‌گرداند، اما مشتری همچنان IBinder را در فراخوانی برگشتی onServiceConnected() دریافت می‌کند. شکل زیر منطق این نوع چرخه حیات را نشان می‌دهد.

چرخه حیات خدمات شروع‌شده و محدودکننده: ()onStartCommand و()onBind فراخوانی می‌شوند؛ وقتی همه کلاینت‌ها لغو محدودیت می‌کنند، ()onUnbind فراخوانی می‌شود؛ در محدودیت بعدی، اگر()onUnbind مقدار true را برگرداند، ()onRebind فراخوانی می‌شود، درغیراین‌صورت()onBind فراخوانی می‌شود؛ پس‌از توقف خدمات، ()onDestroy فراخوانی می‌شود.
شکل ۱. چرخه حیات برای سرویسی که شروع شده است و همچنین اجازه اتصال می‌دهد.

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