আবদ্ধ সেবা ওভারভিউ

একটি আবদ্ধ পরিষেবা একটি ক্লায়েন্ট-সার্ভার ইন্টারফেসের সার্ভার। এটি ক্রিয়াকলাপগুলির মতো উপাদানগুলিকে পরিষেবার সাথে আবদ্ধ করতে দেয়, অনুরোধ পাঠায়, প্রতিক্রিয়াগুলি গ্রহণ করে এবং আন্তঃপ্রক্রিয়া যোগাযোগ (IPC) সম্পাদন করে। একটি আবদ্ধ পরিষেবা সাধারণত বেঁচে থাকে যখন এটি অন্য অ্যাপ্লিকেশন উপাদান পরিবেশন করে এবং অনির্দিষ্টকালের জন্য পটভূমিতে চলে না।

এই দস্তাবেজটি বর্ণনা করে যে কীভাবে একটি আবদ্ধ পরিষেবা তৈরি করতে হয়, অন্যান্য অ্যাপ্লিকেশন উপাদানগুলি থেকে কীভাবে পরিষেবার সাথে আবদ্ধ করা যায়। সাধারণভাবে পরিষেবাগুলি সম্পর্কে অতিরিক্ত তথ্যের জন্য, যেমন একটি পরিষেবা থেকে বিজ্ঞপ্তিগুলি কীভাবে সরবরাহ করা যায় এবং পরিষেবাটিকে অগ্রভাগে চালানোর জন্য সেট করা যায়, পরিষেবাগুলির ওভারভিউ দেখুন৷

বুনিয়াদি

একটি আবদ্ধ পরিষেবা হল Service শ্রেণীর একটি বাস্তবায়ন যা অন্যান্য অ্যাপ্লিকেশনগুলিকে এটির সাথে আবদ্ধ হতে দেয় এবং এর সাথে যোগাযোগ করতে দেয়। একটি পরিষেবার জন্য বাইন্ডিং প্রদান করতে, আপনি onBind() কলব্যাক পদ্ধতি প্রয়োগ করেন। এই পদ্ধতিটি একটি IBinder অবজেক্ট প্রদান করে যা প্রোগ্রামিং ইন্টারফেসকে সংজ্ঞায়িত করে যা ক্লায়েন্টরা পরিষেবার সাথে যোগাযোগ করতে ব্যবহার করতে পারে।

একটি শুরু করা পরিষেবাতে আবদ্ধ

পরিষেবার ওভারভিউতে যেমন আলোচনা করা হয়েছে, আপনি একটি পরিষেবা তৈরি করতে পারেন যা শুরু এবং আবদ্ধ উভয়ই। অর্থাৎ, আপনি startService() কল করে একটি পরিষেবা শুরু করতে পারেন, যা পরিষেবাটিকে অনির্দিষ্টকালের জন্য চলতে দেয়৷ আপনি bindService() কল করে একটি ক্লায়েন্টকে পরিষেবার সাথে আবদ্ধ হতে দিতে পারেন।

আপনি যদি আপনার পরিষেবাটি শুরু করতে এবং আবদ্ধ হতে দেন, তাহলে পরিষেবাটি শুরু হলে, সমস্ত ক্লায়েন্টদের আবদ্ধ করার সময় সিস্টেমটি পরিষেবাটিকে ধ্বংস করে না । পরিবর্তে, আপনাকে অবশ্যই stopSelf() বা stopService() কল করে পরিষেবাটি স্পষ্টভাবে বন্ধ করতে হবে।

যদিও আপনি সাধারণত onBind() বা onStartCommand() প্রয়োগ করেন, তবে কখনও কখনও উভয়টি বাস্তবায়ন করা প্রয়োজন। উদাহরণস্বরূপ, একটি মিউজিক প্লেয়ার তার পরিষেবাকে অনির্দিষ্টকালের জন্য চলতে দেওয়া এবং বাইন্ডিং প্রদান করা দরকারী বলে মনে করতে পারে। এইভাবে, একটি কার্যকলাপ কিছু সঙ্গীত বাজানোর জন্য পরিষেবা শুরু করতে পারে এবং ব্যবহারকারী অ্যাপ্লিকেশনটি ছেড়ে দিলেও সঙ্গীতটি চলতে থাকে। তারপর, যখন ব্যবহারকারী অ্যাপ্লিকেশনে ফিরে আসে, তখন কার্যকলাপটি প্লেব্যাকের নিয়ন্ত্রণ পুনরুদ্ধার করতে পরিষেবার সাথে আবদ্ধ হতে পারে।

একটি শুরু পরিষেবাতে বাইন্ডিং যোগ করার সময় পরিষেবা জীবনচক্র সম্পর্কে আরও তথ্যের জন্য, একটি আবদ্ধ পরিষেবার জীবনচক্র পরিচালনা বিভাগটি দেখুন।

একটি ক্লায়েন্ট bindService() কল করে একটি পরিষেবার সাথে আবদ্ধ হয়। যখন এটি হয়, এটি অবশ্যই ServiceConnection একটি বাস্তবায়ন প্রদান করবে, যা পরিষেবার সাথে সংযোগ পর্যবেক্ষণ করে৷ bindService() এর রিটার্ন মান নির্দেশ করে যে অনুরোধ করা পরিষেবাটি বিদ্যমান কিনা এবং ক্লায়েন্টকে এটিতে অ্যাক্সেসের অনুমতি দেওয়া হয়েছে কিনা।

যখন অ্যান্ড্রয়েড সিস্টেম ক্লায়েন্ট এবং পরিষেবার মধ্যে সংযোগ তৈরি করে, তখন এটি ServiceConnection onServiceConnected() কল করে। onServiceConnected() পদ্ধতিতে একটি IBinder আর্গুমেন্ট রয়েছে, যা ক্লায়েন্ট তখন আবদ্ধ পরিষেবার সাথে যোগাযোগ করতে ব্যবহার করে।

আপনি একই সাথে একাধিক ক্লায়েন্টকে একটি পরিষেবাতে সংযুক্ত করতে পারেন। যাইহোক, সিস্টেমটি IBinder পরিষেবা যোগাযোগের চ্যানেল ক্যাশে করে। অন্য কথায়, সিস্টেমটি পরিষেবার onBind() পদ্ধতিকে কল করে IBinder তৈরি করার জন্য যখন প্রথম ক্লায়েন্ট আবদ্ধ হয়। সিস্টেম তারপর একই IBinder সমস্ত অতিরিক্ত ক্লায়েন্টদের কাছে সরবরাহ করে যা একই পরিষেবার সাথে আবদ্ধ হয়, onBind() আবার কল না করে।

যখন শেষ ক্লায়েন্ট পরিষেবা থেকে আনবাইন্ড করে, সিস্টেমটি পরিষেবাটিকে ধ্বংস করে, যদি না পরিষেবাটি startService() ব্যবহার করে শুরু করা হয়।

আপনার আবদ্ধ পরিষেবা বাস্তবায়নের সবচেয়ে গুরুত্বপূর্ণ অংশ হল ইন্টারফেসটি সংজ্ঞায়িত করা যা আপনার onBind() কলব্যাক পদ্ধতিটি ফেরত দেয়। নিম্নলিখিত বিভাগে আপনি আপনার পরিষেবার IBinder ইন্টারফেস সংজ্ঞায়িত করতে পারেন এমন বিভিন্ন উপায় নিয়ে আলোচনা করা হয়েছে।

একটি আবদ্ধ পরিষেবা তৈরি করুন

বাইন্ডিং প্রদান করে এমন একটি পরিষেবা তৈরি করার সময়, আপনাকে অবশ্যই একটি IBinder প্রদান করতে হবে যা প্রোগ্রামিং ইন্টারফেস প্রদান করে যা ক্লায়েন্টরা পরিষেবার সাথে যোগাযোগ করতে ব্যবহার করতে পারে। আপনি ইন্টারফেস সংজ্ঞায়িত করতে পারেন তিনটি উপায় আছে:

বাইন্ডার ক্লাস প্রসারিত করুন
যদি আপনার পরিষেবাটি আপনার নিজের অ্যাপ্লিকেশনের জন্য ব্যক্তিগত হয় এবং ক্লায়েন্টের মতো একই প্রক্রিয়ায় চলে, যা সাধারণ, তাহলে আপনার ইন্টারফেস তৈরি করুন Binder ক্লাস প্রসারিত করে এবং onBind() থেকে এর একটি উদাহরণ প্রদান করে। ক্লায়েন্ট Binder গ্রহণ করে এবং Binder বাস্তবায়ন বা Service উপলব্ধ সর্বজনীন পদ্ধতিগুলি সরাসরি অ্যাক্সেস করতে এটি ব্যবহার করতে পারে।

এটি হল পছন্দের কৌশল যখন আপনার পরিষেবাটি শুধুমাত্র আপনার নিজের অ্যাপ্লিকেশনের জন্য একটি ব্যাকগ্রাউন্ড কর্মী। শুধুমাত্র ব্যবহারের ক্ষেত্রে যখন এটি আপনার ইন্টারফেস তৈরি করার পছন্দের উপায় নয় তা হল যদি আপনার পরিষেবা অন্য অ্যাপ্লিকেশন দ্বারা বা পৃথক প্রক্রিয়া জুড়ে ব্যবহার করা হয়।

একটি মেসেঞ্জার ব্যবহার করুন
বিভিন্ন প্রক্রিয়ায় কাজ করার জন্য আপনার ইন্টারফেসের প্রয়োজন হলে, আপনি একটি Messenger দিয়ে পরিষেবার জন্য একটি ইন্টারফেস তৈরি করতে পারেন। এই পদ্ধতিতে, পরিষেবাটি একটি Handler সংজ্ঞায়িত করে যা বিভিন্ন ধরণের Message বস্তুর প্রতিক্রিয়া জানায়।

এই Handler হল একটি Messenger ভিত্তি যা ক্লায়েন্টের সাথে একটি IBinder শেয়ার করতে পারে, ক্লায়েন্টকে Message অবজেক্ট ব্যবহার করে পরিষেবাতে কমান্ড পাঠাতে দেয়। উপরন্তু, ক্লায়েন্ট তার নিজস্ব একটি Messenger সংজ্ঞায়িত করতে পারে, যাতে পরিষেবাটি বার্তাগুলি ফেরত পাঠাতে পারে।

ইন্টারপ্রসেস কমিউনিকেশন (IPC) সঞ্চালনের এটি সবচেয়ে সহজ উপায়, কারণ Messenger সমস্ত অনুরোধকে একটি একক থ্রেডে সারিবদ্ধ করে যাতে আপনাকে আপনার পরিষেবাটিকে থ্রেড-নিরাপদ করার জন্য ডিজাইন করতে না হয়।

এআইডিএল ব্যবহার করুন
অ্যান্ড্রয়েড ইন্টারফেস ডেফিনিশন ল্যাঙ্গুয়েজ (এআইডিএল) অবজেক্টগুলিকে আদিম করে ফেলে যা অপারেটিং সিস্টেম বুঝতে পারে এবং আইপিসি সঞ্চালনের জন্য সেগুলিকে প্রসেস জুড়ে মার্শাল করে। একটি Messenger ব্যবহার করে পূর্ববর্তী কৌশলটি আসলে AIDL এর অন্তর্নিহিত কাঠামোর উপর ভিত্তি করে।

পূর্ববর্তী বিভাগে উল্লিখিত হিসাবে, Messenger একটি একক থ্রেডে সমস্ত ক্লায়েন্ট অনুরোধের একটি সারি তৈরি করে, তাই পরিষেবাটি একবারে একটি অনুরোধ গ্রহণ করে। যাইহোক, আপনি যদি চান যে আপনার পরিষেবা একই সাথে একাধিক অনুরোধ পরিচালনা করতে পারে, তাহলে আপনি সরাসরি AIDL ব্যবহার করতে পারেন। এই ক্ষেত্রে, আপনার পরিষেবা অবশ্যই থ্রেড-নিরাপদ এবং মাল্টিথ্রেডিং করতে সক্ষম হতে হবে।

AIDL সরাসরি ব্যবহার করতে, একটি .aidl ফাইল তৈরি করুন যা প্রোগ্রামিং ইন্টারফেসকে সংজ্ঞায়িত করে। অ্যান্ড্রয়েড SDK টুলগুলি একটি বিমূর্ত শ্রেণী তৈরি করতে এই ফাইলটি ব্যবহার করে যা ইন্টারফেস প্রয়োগ করে এবং IPC পরিচালনা করে, যা আপনি আপনার পরিষেবার মধ্যে প্রসারিত করতে পারেন।

দ্রষ্টব্য: বেশিরভাগ অ্যাপ্লিকেশনের জন্য, AIDL একটি আবদ্ধ পরিষেবা তৈরি করার সর্বোত্তম পছন্দ নয়, কারণ এটি মাল্টিথ্রেডিং ক্ষমতার প্রয়োজন হতে পারে এবং এর ফলে আরও জটিল বাস্তবায়ন হতে পারে। অতএব, এই নথিটি আপনার পরিষেবার জন্য এটি কীভাবে ব্যবহার করবেন তা নিয়ে আলোচনা করে না। আপনি যদি নিশ্চিত হন যে আপনাকে সরাসরি AIDL ব্যবহার করতে হবে, AIDL নথিটি দেখুন।

বাইন্ডার ক্লাস প্রসারিত করুন

যদি শুধুমাত্র স্থানীয় অ্যাপ্লিকেশনটি আপনার পরিষেবা ব্যবহার করে এবং এটির সমস্ত প্রক্রিয়া জুড়ে কাজ করার প্রয়োজন হয় না, তাহলে আপনি আপনার নিজস্ব Binder ক্লাস বাস্তবায়ন করতে পারেন যা আপনার ক্লায়েন্টকে পরিষেবাতে সর্বজনীন পদ্ধতিতে সরাসরি অ্যাক্সেস প্রদান করে।

দ্রষ্টব্য: এটি কেবল তখনই কাজ করে যখন ক্লায়েন্ট এবং পরিষেবা একই অ্যাপ্লিকেশন এবং প্রক্রিয়ায় থাকে, যা সবচেয়ে সাধারণ। উদাহরণস্বরূপ, এটি একটি মিউজিক অ্যাপ্লিকেশনের জন্য ভাল কাজ করে যা ব্যাকগ্রাউন্ডে মিউজিক বাজছে এমন একটি ক্রিয়াকলাপকে নিজস্ব পরিষেবাতে আবদ্ধ করতে হবে।

এটি কীভাবে সেট আপ করবেন তা এখানে:

  1. আপনার পরিষেবাতে, Binder একটি উদাহরণ তৈরি করুন যা নিম্নলিখিতগুলির মধ্যে একটি করে:
    • ক্লায়েন্ট কল করতে পারেন যে পাবলিক পদ্ধতি রয়েছে.
    • বর্তমান Service দৃষ্টান্ত প্রদান করে, যার সর্বজনীন পদ্ধতি রয়েছে যা ক্লায়েন্ট কল করতে পারে।
    • ক্লায়েন্ট কল করতে পারে এমন সর্বজনীন পদ্ধতি সহ পরিষেবা দ্বারা হোস্ট করা অন্য ক্লাসের একটি উদাহরণ প্রদান করে।
  2. onBind() কলব্যাক পদ্ধতি থেকে Binder এই উদাহরণটি ফেরত দিন।
  3. ক্লায়েন্টে, onServiceConnected() কলব্যাক পদ্ধতি থেকে Binder গ্রহণ করুন এবং প্রদত্ত পদ্ধতি ব্যবহার করে আবদ্ধ পরিষেবাতে কল করুন।

দ্রষ্টব্য: পরিষেবা এবং ক্লায়েন্ট অবশ্যই একই অ্যাপ্লিকেশনে থাকতে হবে যাতে ক্লায়েন্ট প্রত্যাবর্তিত বস্তুটি কাস্ট করতে পারে এবং সঠিকভাবে এর APIগুলিকে কল করতে পারে। পরিষেবা এবং ক্লায়েন্টকেও একই প্রক্রিয়ার মধ্যে থাকতে হবে, কারণ এই কৌশলটি সমস্ত প্রক্রিয়া জুড়ে কোনো মার্শালিং সঞ্চালন করে না।

উদাহরণস্বরূপ, এখানে একটি পরিষেবা রয়েছে যা ক্লায়েন্টদের একটি 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 ক্লায়েন্টদের LocalService এর বর্তমান উদাহরণ পুনরুদ্ধার করার জন্য getService() পদ্ধতি প্রদান করে। এটি ক্লায়েন্টদের পরিষেবাতে সর্বজনীন পদ্ধতিতে কল করতে দেয়। উদাহরণস্বরূপ, ক্লায়েন্টরা পরিষেবা থেকে getRandomNumber() কল করতে পারে।

এখানে একটি অ্যাক্টিভিটি রয়েছে যা LocalService সাথে আবদ্ধ হয় এবং একটি বোতামে ক্লিক করলে getRandomNumber() কল করে:

কোটলিন

class BindingActivity : Activity() {
    private lateinit var mService: LocalService
    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) {
            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)
        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
            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);
        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) {
            mBound = false;
        }
    };
}

পূর্ববর্তী নমুনা দেখায় কিভাবে ক্লায়েন্ট ServiceConnection বাস্তবায়ন এবং onServiceConnected() কলব্যাক ব্যবহার করে পরিষেবার সাথে আবদ্ধ হয়। পরবর্তী বিভাগে পরিষেবার সাথে আবদ্ধ হওয়ার এই প্রক্রিয়া সম্পর্কে আরও তথ্য প্রদান করে৷

দ্রষ্টব্য: পূর্ববর্তী উদাহরণে, onStop() পদ্ধতি ক্লায়েন্টকে পরিষেবা থেকে আনবাইন্ড করে। অতিরিক্ত নোট বিভাগে আলোচনা করা হয়েছে, উপযুক্ত সময়ে পরিষেবা থেকে ক্লায়েন্টদের আবদ্ধ করুন।

আরও নমুনা কোডের জন্য, ApiDemos-LocalService.java ক্লাস এবং LocalServiceActivities.java ক্লাস দেখুন।

একটি মেসেঞ্জার ব্যবহার করুন

আপনার যদি দূরবর্তী প্রক্রিয়াগুলির সাথে যোগাযোগ করার জন্য আপনার পরিষেবার প্রয়োজন হয়, তাহলে আপনি আপনার পরিষেবার জন্য ইন্টারফেস প্রদান করতে একটি Messenger ব্যবহার করতে পারেন৷ এই কৌশলটি আপনাকে এআইডিএল ব্যবহার করার প্রয়োজন ছাড়াই আন্তঃপ্রক্রিয়া যোগাযোগ (আইপিসি) সম্পাদন করতে দেয়।

আপনার ইন্টারফেসের জন্য একটি Messenger ব্যবহার করা AIDL ব্যবহার করার চেয়ে সহজ কারণ Messenger পরিষেবাতে সমস্ত কলকে সারিবদ্ধ করে। একটি বিশুদ্ধ AIDL ইন্টারফেস পরিষেবাতে একযোগে অনুরোধ পাঠায়, যা তারপর মাল্টিথ্রেডিং পরিচালনা করতে হবে।

বেশিরভাগ অ্যাপ্লিকেশনের জন্য, পরিষেবাটির মাল্টিথ্রেডিং করার প্রয়োজন নেই, তাই একটি Messenger ব্যবহার করে পরিষেবাটিকে একবারে একটি কল পরিচালনা করতে দেয়৷ যদি আপনার পরিষেবাটি মাল্টিথ্রেড করা গুরুত্বপূর্ণ হয়, তাহলে আপনার ইন্টারফেস সংজ্ঞায়িত করতে AIDL ব্যবহার করুন।

কিভাবে একটি Messenger ব্যবহার করতে হয় তার একটি সারাংশ এখানে দেওয়া হল:

  1. পরিষেবাটি একটি Handler প্রয়োগ করে যা একটি ক্লায়েন্টের কাছ থেকে প্রতিটি কলের জন্য একটি কলব্যাক গ্রহণ করে।
  2. পরিষেবাটি একটি Messenger অবজেক্ট তৈরি করতে Handler ব্যবহার করে (যা Handler একটি রেফারেন্স)।
  3. Messenger একটি IBinder তৈরি করে যে পরিষেবাটি onBind() থেকে গ্রাহকদের কাছে ফিরে আসে।
  4. ক্লায়েন্টরা Messenger ইনস্ট্যান্টিয়েট করার জন্য IBinder ব্যবহার করে (যেটি পরিষেবার Handler উল্লেখ করে), যা ক্লায়েন্ট পরিষেবাতে Message বস্তু পাঠাতে ব্যবহার করে।
  5. পরিষেবাটি তার Handler প্রতিটি Message গ্রহণ করে — বিশেষত, 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();
    }
}

Handler handleMessage() পদ্ধতিটি হল যেখানে পরিষেবাটি আগত 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)
            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);
            bound = false;
        }
    }
}

এই উদাহরণটি দেখায় না যে পরিষেবাটি ক্লায়েন্টকে কীভাবে প্রতিক্রিয়া জানাতে পারে। আপনি যদি পরিষেবাটি সাড়া দিতে চান তবে আপনাকে ক্লায়েন্টে একটি Messenger তৈরি করতে হবে। যখন ক্লায়েন্ট onServiceConnected() কলব্যাক পায়, তখন এটি send() পদ্ধতির replyTo প্যারামিটারে ক্লায়েন্টের Messenger অন্তর্ভুক্ত করে এমন একটি Message পাঠায়।

আপনি MessengerService.java (পরিষেবা) এবং MessengerServiceActivities.java (ক্লায়েন্ট) নমুনাগুলিতে কীভাবে দ্বি-মুখী বার্তা প্রদান করতে হয় তার একটি উদাহরণ দেখতে পারেন।

একটি সেবা আবদ্ধ

অ্যাপ্লিকেশন উপাদান (ক্লায়েন্ট) bindService() কল করে একটি পরিষেবার সাথে আবদ্ধ হতে পারে। অ্যান্ড্রয়েড সিস্টেম তারপর পরিষেবাটির onBind() পদ্ধতিতে কল করে, যা পরিষেবার সাথে ইন্টারঅ্যাক্ট করার জন্য একটি IBinder ফেরত দেয়।

বাইন্ডিংটি অ্যাসিঙ্ক্রোনাস, এবং bindService() ক্লায়েন্টকে IBinder ফেরত না দিয়ে অবিলম্বে ফিরে আসে। IBinder পেতে, ক্লায়েন্টকে অবশ্যই ServiceConnection এর একটি উদাহরণ তৈরি করতে হবে এবং এটি bindService() এ পাস করতে হবে। ServiceConnection একটি কলব্যাক পদ্ধতি রয়েছে যা সিস্টেমটি IBinder সরবরাহ করতে কল করে।

দ্রষ্টব্য: শুধুমাত্র কার্যকলাপ, পরিষেবা এবং বিষয়বস্তু প্রদানকারীরা একটি পরিষেবার সাথে আবদ্ধ হতে পারে—আপনি একটি সম্প্রচার রিসিভার থেকে একটি পরিষেবার সাথে আবদ্ধ হতে পারবেন না

আপনার ক্লায়েন্ট থেকে একটি পরিষেবাতে আবদ্ধ হতে, এই পদক্ষেপগুলি অনুসরণ করুন:

  1. ServiceConnection বাস্তবায়ন করুন।

    আপনার বাস্তবায়ন অবশ্যই দুটি কলব্যাক পদ্ধতি ওভাররাইড করবে:

    onServiceConnected()
    পরিষেবার onBind() পদ্ধতির মাধ্যমে ফিরে আসা IBinder সরবরাহ করার জন্য সিস্টেম এটিকে কল করে।
    onServiceDisconnected()
    অ্যান্ড্রয়েড সিস্টেম এটিকে কল করে যখন পরিষেবাটির সাথে সংযোগটি অপ্রত্যাশিতভাবে হারিয়ে যায়, যেমন যখন পরিষেবাটি ক্র্যাশ হয় বা মারা যায়৷ যখন ক্লায়েন্ট আনবাইন্ড করে তখন এটি বলা হয় না
  2. কল bindService() , ServiceConnection বাস্তবায়ন পাস করে।

    দ্রষ্টব্য: যদি পদ্ধতিটি মিথ্যা রিটার্ন করে, আপনার ক্লায়েন্টের পরিষেবার সাথে একটি বৈধ সংযোগ নেই। যাইহোক, আপনার ক্লায়েন্টে unbindService() কল করুন। অন্যথায়, আপনার ক্লায়েন্ট পরিষেবাটি নিষ্ক্রিয় থাকা অবস্থায় বন্ধ করা থেকে বিরত রাখে।

  3. যখন সিস্টেম আপনার onServiceConnected() কলব্যাক পদ্ধতিতে কল করে, তখন আপনি ইন্টারফেসের দ্বারা সংজ্ঞায়িত পদ্ধতিগুলি ব্যবহার করে পরিষেবাতে কল করা শুরু করতে পারেন।
  4. পরিষেবা থেকে সংযোগ বিচ্ছিন্ন করতে, unbindService() কল করুন।

    আপনার অ্যাপ ক্লায়েন্টকে ধ্বংস করার সময় যদি আপনার ক্লায়েন্ট এখনও একটি পরিষেবার সাথে আবদ্ধ থাকে, ধ্বংসের ফলে ক্লায়েন্টকে আবদ্ধ করে। পরিষেবার সাথে ইন্টারঅ্যাক্ট করার সাথে সাথে ক্লায়েন্টকে আবদ্ধ করা একটি ভাল অভ্যাস। এটি করার ফলে নিষ্ক্রিয় পরিষেবাটি বন্ধ হয়ে যায়। বাঁধাই এবং মুক্ত করার উপযুক্ত সময় সম্পর্কে আরও তথ্যের জন্য, অতিরিক্ত নোট বিভাগটি দেখুন।

নিম্নলিখিত উদাহরণটি বাইন্ডার ক্লাস প্রসারিত করে পূর্বে তৈরি করা পরিষেবার সাথে ক্লায়েন্টকে সংযুক্ত করে, তাই এটি যা করতে হবে তা হল প্রত্যাবর্তিত IBinder LocalBinder ক্লাসে কাস্ট করা এবং LocalService ইনস্ট্যান্সের অনুরোধ করা:

কোটলিন

var mService: LocalService

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")
        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");
        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 সাথে আবদ্ধ করার জন্য একটি অভিপ্রায় ব্যবহার করেন, তাহলে একটি স্পষ্ট অভিপ্রায় ব্যবহার করে আপনার অ্যাপটি সুরক্ষিত আছে তা নিশ্চিত করুন৷ একটি পরিষেবা শুরু করার জন্য একটি অন্তর্নিহিত অভিপ্রায় ব্যবহার করা একটি নিরাপত্তা বিপত্তি কারণ আপনি নিশ্চিত হতে পারেন না যে কোন পরিষেবাটি অভিপ্রায়ে সাড়া দেয় এবং ব্যবহারকারী দেখতে পারে না কোন পরিষেবাটি শুরু হয়৷ অ্যান্ড্রয়েড 5.0 (API স্তর 21) দিয়ে শুরু করে, আপনি যদি একটি অন্তর্নিহিত অভিপ্রায়ে bindService() কল করেন তবে সিস্টেমটি একটি ব্যতিক্রম ছুঁড়ে দেয়।

  • দ্বিতীয় প্যারামিটার হল ServiceConnection অবজেক্ট।
  • তৃতীয় প্যারামিটার হল একটি পতাকা নির্দেশ করে বাইন্ডিংয়ের বিকল্পগুলি—সাধারণত BIND_AUTO_CREATE , পরিষেবাটি তৈরি করতে যদি এটি ইতিমধ্যে জীবিত না থাকে। অন্যান্য সম্ভাব্য মান হল BIND_DEBUG_UNBIND , BIND_NOT_FOREGROUND , অথবা 0 এর জন্য কোনটি নয়৷

অতিরিক্ত নোট

এখানে একটি পরিষেবার সাথে আবদ্ধ হওয়ার বিষয়ে কিছু গুরুত্বপূর্ণ নোট রয়েছে:

  • সর্বদা DeadObjectException ব্যতিক্রম ফাঁদ, যা সংযোগ বিচ্ছিন্ন হয়ে গেলে নিক্ষেপ করা হয়। এটি দূরবর্তী পদ্ধতি দ্বারা নিক্ষিপ্ত একমাত্র ব্যতিক্রম।
  • বস্তুগুলি প্রসেস জুড়ে রেফারেন্স গণনা করা হয়।
  • আপনি সাধারণত ক্লায়েন্টের জীবনচক্রের মিলিত আনা এবং ছিঁড়ে ফেলা মুহূর্তগুলির সময় বাইন্ডিং এবং আনবাইন্ডিং যুক্ত করেন, যেমনটি নিম্নলিখিত উদাহরণগুলিতে বর্ণনা করা হয়েছে:
    • আপনার অ্যাক্টিভিটি দৃশ্যমান থাকাকালীনই যদি আপনাকে পরিষেবার সাথে ইন্টারঅ্যাক্ট করতে হয়, onStart() সময় আবদ্ধ করুন এবং onStop() সময় আনবাইন্ড করুন।
    • আপনি যদি ব্যাকগ্রাউন্ডে বন্ধ থাকা অবস্থায়ও আপনার কার্যকলাপের প্রতিক্রিয়া পেতে চান, onCreate() সময় আবদ্ধ করুন এবং onDestroy() সময় আনবাইন্ড করুন। সতর্ক থাকুন যে এটি বোঝায় যে আপনার ক্রিয়াকলাপটি পরিষেবাটি চলাকালীন পুরো সময় ব্যবহার করতে হবে, এমনকি পটভূমিতেও, তাই যখন পরিষেবাটি অন্য প্রক্রিয়ায় থাকে, তখন আপনি প্রক্রিয়াটির ওজন বাড়িয়ে দেন এবং এটির দ্বারা মারা যাওয়ার সম্ভাবনা বেশি থাকে সিস্টেম

    দ্রষ্টব্য: আপনি সাধারণত আপনার কার্যকলাপের onResume() এবং onPause() কলব্যাকের সময় আবদ্ধ বা আবদ্ধ হন না , কারণ এই কলব্যাকগুলি প্রতিটি জীবনচক্র পরিবর্তনের সময় ঘটে। এই ট্রানজিশনে ঘটে যাওয়া প্রসেসিংকে সর্বনিম্ন রাখুন।

    এছাড়াও, যদি আপনার অ্যাপ্লিকেশানে একাধিক ক্রিয়াকলাপ একই পরিষেবার সাথে আবদ্ধ হয় এবং এই দুটি ক্রিয়াকলাপের মধ্যে একটি স্থানান্তর ঘটে, তবে পরিষেবাটি ধ্বংস হয়ে যেতে পারে এবং পুনরায় তৈরি করা যেতে পারে কারণ বর্তমান ক্রিয়াকলাপটি পরবর্তী একটি আবদ্ধ হওয়ার আগে (বিরতি চলাকালীন) বন্ধ হয়ে যায় (জীবনবৃত্তান্তের সময়)। ক্রিয়াকলাপগুলি কীভাবে তাদের জীবনচক্রকে সমন্বয় করে তার জন্য এই কার্যকলাপের রূপান্তরটি কার্যকলাপ জীবনচক্রে বর্ণিত হয়েছে।

একটি পরিষেবাতে কীভাবে আবদ্ধ হতে হয় তা দেখানো আরও নমুনা কোডের জন্য, ApiDemos-RemoteService.java ক্লাস দেখুন।

একটি আবদ্ধ পরিষেবার জীবনচক্র পরিচালনা করুন

যখন একটি পরিষেবা সমস্ত ক্লায়েন্ট থেকে আনবাউন্ড হয়, তখন অ্যান্ড্রয়েড সিস্টেম এটিকে ধ্বংস করে দেয় (যদি না এটি startService() ব্যবহার করে শুরু করা হয়)। সুতরাং, আপনার পরিষেবার জীবনচক্র পরিচালনা করতে হবে না যদি এটি সম্পূর্ণরূপে একটি আবদ্ধ পরিষেবা হয়। অ্যান্ড্রয়েড সিস্টেম এটি কোনো ক্লায়েন্টের সাথে আবদ্ধ কিনা তার ভিত্তিতে আপনার জন্য এটি পরিচালনা করে।

যাইহোক, যদি আপনি onStartCommand() কলব্যাক পদ্ধতি প্রয়োগ করতে চান, তাহলে আপনাকে অবশ্যই পরিষেবাটি স্পষ্টভাবে বন্ধ করতে হবে, কারণ পরিষেবাটি এখন শুরু হয়েছে বলে বিবেচিত হয়। এই ক্ষেত্রে, পরিষেবাটি চলবে যতক্ষণ না পরিষেবাটি stopSelf() দিয়ে বন্ধ হয়ে যায় বা অন্য কোনও উপাদান stopService() কল করে, এটি কোনও ক্লায়েন্টের জন্য আবদ্ধ হোক না কেন।

অতিরিক্তভাবে, যদি আপনার পরিষেবা শুরু হয় এবং বাইন্ডিং গ্রহণ করে, তারপর যখন সিস্টেম আপনার onUnbind() পদ্ধতিতে কল করে, আপনি যদি পরের বার কোনও ক্লায়েন্ট পরিষেবাতে আবদ্ধ হয় তখন আপনি onRebind() এ একটি কল পেতে চাইলে আপনি ঐচ্ছিকভাবে true ফেরত দিতে পারেন। onRebind() অকার্যকর ফেরত দেয়, কিন্তু ক্লায়েন্ট এখনও তার onServiceConnected() কলব্যাকে IBinder গ্রহণ করে। নিম্নলিখিত চিত্রটি এই ধরণের জীবনচক্রের যুক্তি ব্যাখ্যা করে।

চিত্র 1. একটি পরিষেবার জীবনচক্র যা শুরু হয়েছে এবং বাঁধাই করার অনুমতি দেয়৷

একটি শুরু করা পরিষেবার জীবনচক্র সম্পর্কে আরও তথ্যের জন্য, পরিষেবাগুলির ওভারভিউ দেখুন৷