পোস্টটি পড়া হয়েছে 4,011 বার
Eventbus library Android tutorial for publisher/subscriber pattern

Android ডেভেলপমেন্টে publisher/subscriber pattern এর জন্য EventBus

Post updated on 1st October, 2017 at 06:49 pm

একটা ইউজ কেস চিন্তা করা যাক। আমাদের অ্যাপে এক বা একাধিক Activity আছে। একটা থেকে বাটন ক্লিকে আরেকটায় যাওয়া যায়। এরকম কোনো একটা activity-তে একটা বাটন আছে। তাতে ক্লিক করলে রিমোট সার্ভারে একটা রিকোয়েস্ট যাবে। এরপর সার্ভার থেকে কিছু JSON ডেটা আসবে। JSON এর সাইজ হতে পারে ৫ থেকে ১০ মেগাবাইট। এই রিকোয়েস্ট যাওয়া ও সার্ভার থেকে ডেটা আসার জন্য কিছু সময় লাগবে। এই সময়ে কোনো Loader বা progress bar দিয়ে UI ব্লক করা যাবে না। ইউজার অ্যাপ ইউজ করতে থাকবে। শেষে যখন সে MainActivity (launcher activity)-তে আসবে তখন সেখানে ঐ রিকোয়েস্টের status দেখতে পাবে। যদি সার্ভার থেকে সব ডেটা অ্যাপে চলে আসে তাহলে Status দেখাবে OK, যদি রিকোয়েস্ট failed হয় তাহলে সেটাও জানা যাবে।

এই কাজটা করার জন্য প্রথমত ২ টা জিনিস দরকার হবে। আপনাকে জানতে হবে কিভাবে অ্যাপ থেকে রিমোট সার্ভারে HTTP request পাঠানো যায়। এটা খুব সহজে করা যায় Retrofit Network Library এর মাধ্যমে। টপিকটা আপনার জানা না থাকলে এখান থেকে স্টাডি করে আসতে পারেন। দ্বিতীয়ত এমন একটা ব্যবস্থা করতে হবে যেন সার্ভার থেকে রেসপন্স আসলে ঐ background thread থেকে MainActivity এর UI-তে response এর স্ট্যাটাসটা আপডেট করা। এটা আপডেট করার একটা way হতে পারে network call এর onResponse() এর ভিতরে একটা interface call করা। সেটাকে MainActivity-তে implement করে override মেথডের ভিতরে কোনো স্ট্যাটাস ডেটা ফিল্ডের ভ্যালু আপডেট করা। পরে যখন অন্যান্য activity থেকে ইউজার ঘুরাঘুরি করে MainActivity তে আসবে তখন onResume() state এ TextView আপডেট করা।

এটা একটা উপায়। আরো ভাল কোনো উপায় থাকতে পারে। তো এই সিসটেমে কাজ করলে বেশ খানিকটা কোড লিখতে হবে। Interface বানাতে হবে, সেটাকে কল করা, ইমপ্লিমেন্ট করা, activity lifecycle নিয়ে কাজ করা ইত্যাদি। এই সবগুলো কাজ সহজ করে দিতে পারে EventBus Library. Publisher/Subscriber pattern ব্যবহার করার এই লাইব্রেরি ব্যবহার করে পুরো কাজটা করতে ৫-৬ লাইনের বেশি কোড লিখতে হবে না।

Publisher/Subscriber Pattern

EventBus হচ্ছে অ্যান্ড্রয়েড অ্যাপে publisher/subscriber pattern ইউজ করার জন্য একটা জনপ্রিয় লাইব্রেরি। Publisher/subscriber প্যাটার্নের মূল বিষয়টা হচ্ছে এমন একটা সিসটেম ডেভেলপ করা যেখানে কোনো একটা ক্লাসের কোনো একটা মেথড একটা event publish করতে পারে। সহজ করে বললে একটা মেসেজ সেন্ড করতে পারে। সেই মেসেজে একটা স্ট্রিং থাকতে পারে। অথবা কোনো একটা ক্লাসের অবজেক্টও থাকতে পারে। তো যেই মেথডটা এই মেসেজ পাবলিশ করবে তার কাজ ঐখানেই শেষ। একে বলে publisher (অর্থাৎ যে পাবলিশ করলো)।  তো একটা event publish করার পর publisher আর কোনো চিন্তা করবে না যে ঐ মেসেজ কোন ক্লাস রিসিভ করলো বা করলো না। Publisher এর কাজ শুধু পাবলিশ করে দেয়া।

এদিকে যেই ক্লাসগুলোতে আমরা চাই ঐ মেসেজটা রিসিভ হোক এবং তার উপর ভিত্তি করে কোনো অপারেশন হোক সেইসব ক্লাসকে বলছি Subscriber. যেই ক্লাসগুলোকে আমরা ঐ নির্দিষ্ট টাইপের মেসেজের subscriber বানাতে চাই সেই ক্লাসগুলোতে এমন একটা সিসটেম করতে হবে যেন; সে যেই টাইপের মেসেজ expect করছে, সেই টাইপের মেসেজ published হলে সে মেসেজটা রিসিভ করবে। কোনো একটা ইভেন্টের একাধিক সাবসক্রাইবার থাকতে পারে। আবার কোনো একটা ক্লাস একাধিক ইভেন্টকে সাবসক্রাইব করে রাখতে পারে।

Publisher/subscriber pattern এ ৩টা component থাকে। যথাঃ

  1. Publisher
  2. Subscriber
  3. Infrastructure

Publisher আর subscriber এর ব্যাপারটা উপরের আলোচনা থেকে ক্লিয়ার হয়ে যাবার কথা। অস্পষ্ট মনে হলে গুগল তো আছেই! তৃতীয় component-টা হচ্ছে infrastructure. উপরের প্যারায় বলেছিলাম “এমন একটা সিসটেম করতে হবে যাতে, কোনো ক্লাস যেই টাইপের ইভেন্ট সাবসক্রাইব করে আছে সে যেন সেই টাইপের ইভেন্ট রিসিভ করে”। এই “সিস্টেম”টাই হচ্ছে infrastructure. অর্থাৎ publisher আর subscriber এর মাঝের কমুনিকেশনটা যেই ক্লাস বা কোডগুলো হ্যান্ডেল করবে তাদেরকেই Infrastructure বলছি। EventBus আমাদেরকে এই infrastructure-টা ডেভেলপ করে দিয়েছে। তাই আমাদের আর নতুন করে মাঝের এই কোডগুলো লিখা লাগছে না। EventBus এর থ্রু তে যখন পাবলিশ করার দরকার তখন পাবলিশ করব, যেখানে সাবসক্রাইব করার দরকার সাবসক্রাইব করব। কোনটা কিভাবে কাজ করছে সেটা নিয়ে আপাতত চিন্তা করার দরকার হচ্ছে না।

Installation

অন্যান্য লাইব্রেরি ইউজ করার মত এটাও gradle file এর মাধ্যমে install/download করে নিতে হবে। gradle এর dependencies এর ভিতর লিখতে হবে এই লাইনটাঃ

compile ‘org.greenrobot:eventbus:3.0.0’

এখন পর্যন্ত EventBus এর latest version হচ্ছে 3.0.0. আপনি যখন ইউজ করবেন তখন ওদের গিটহাব রিপোজিটরি থেকে সর্বশেষ ভার্সনটা দেখে নিবেন।

Implementation

সিম্পল একটা প্রোজেক্ট ডেভেলপ করব। একটা Activity থাকবে। সেখানে থাকবে একটা TextView আর Button. বাটনে ক্লিক করলে সার্ভারে রিকোয়েস্ট পাঠাবে। রেসপন্সে বেশ বড়সড় সাইজের একটা JSON object আসবে। এই ডেটার সাইজ প্রায় 10 MB. পুরো ডেটা যখন রিসিভ হয়ে যাবে তখন onResponse() থেকে একটা ইভেন্ট পাবলিশ করা হবে। MainActivity ঐ ইভেন্টটা সাবসক্রাইব করে থাকবে। ফলে server থেকে প্রাপ্ত response message টা দিয়ে ভিউ আপডেট করে দিবে।

Publish an EVENT

Retrofit Library ইউজ করে আমরা একটা আলাদা ক্লাস থেকে সার্ভারে একটা GET request পাঠিয়েছি। তার onResponse() এর ভিতর থেকে একটা event publish করা হচ্ছেঃ

public class NetworkCall {
    public static void getData(){
        Logger.addLogAdapter(new AndroidLogAdapter());
        String myUrl = "https://raw.githubusercontent.com/hasancse91/EventBus-Android-Tutorial/master/Data/data.json";

        ApiInterface apiInterface = RetrofitApiClient.getClient().create(ApiInterface.class);
        Call<ResponseBody> call = apiInterface.getDataFromServer(myUrl);
        call.enqueue(new Callback<ResponseBody>() {
            @Override
            public void onResponse(@NonNull Call<ResponseBody> call, @NonNull Response<ResponseBody> response) {
                Logger.d("Response: " + response.message());
                EventBus.getDefault().post(new DataReceiveEvent("data_received", response.message()));
            }

            @Override
            public void onFailure(@NonNull Call<ResponseBody> call, @NonNull Throwable t) {
                Logger.d("Failure: " + t.toString());
                EventBus.getDefault().post(new DataReceiveEvent("data_received", t.toString()));
            }
        });
    }
}

.post() মেথডের ভিতর parameter হিসাবে পাঠানো হয়েছে DataReceiveEvent model class এর একটা অবজেক্ট। এটা আমার বানানো একটা ক্লাস। আপনি আপনার প্রয়োজন অনুসারে মডেল ক্লাস বানিয়ে নিবেন। onResponse ও onFailure এর ভিতর একটা লগ মেসেজ প্রিন্ট করা হয়েছে কনসোলে দেখার জন্য। Pretty Logger ইউজ করে কনসোলে কিভাবে আরো সুন্দর ভাবে লগ দেখা যায় তার জন্য দেখতে পারেন এই ব্লগ পোস্টটি

Model class for EventBus

Event হিসাবে যেই মডেল ক্লাসের অবজেক্ট পাঠাচ্ছি সেটা এখানে দেয়া হলোঃ

public class DataReceiveEvent {
    private String eventTag;
    private String responseMessage;

    public DataReceiveEvent(String eventTag, String responseMessage) {
        this.eventTag = eventTag;
        this.responseMessage = responseMessage;
    }

    public boolean isTagMatchWith(String tag){
        return eventTag.equals(tag);
    }

    public String getResponseMessage() {
        return responseMessage;
    }
}

এখানে দুইটি ডেটা ফিল্ড আছে। eventTag আর responseMessage. এই একই ক্লাস ব্যবহার করে আমরা চাইলে কয়েক ধরনের ইভেন্ট থ্রো করতে পারি। আমরা ইভেন্টগুলোকে আলাদা করতে পারব এই eventTag দিয়ে। আর responseMessage টা একটা স্ট্রিং। যেই স্ট্যাটাসটা MainActivity তে আপডেট করব সেটা। আপনার প্রোজেক্টের রিকোয়ার্মেন্ট অনুযায়ী এই মেসেজটা চেঞ্জ হতে পারে।

Subscribe an EVENT

উপরে যেই ইভেন্টটা পাবলিশ করা হয়েছে, MainActivity সেটার একমাত্র সাবসক্রাইবার। MainActivity-এর একদম উপরে নিচের কোডটুকু লিখতে হবে ঐ নির্দিষ্ট ইভেন্টে সাবসক্রাইবের জন্যঃ

@Subscribe(threadMode = ThreadMode.MAIN)
public void onEvent(DataReceiveEvent event) throws ClassNotFoundException {
    if (event.isTagMatchWith("data_received")) {
        textView.setText(event.getResponseMessage());
    }
}

এই কোডটুকুর অর্থ হচ্ছে “যে সকল ইভেন্টে DataReceiveEvent ক্লাসের কোনো অবজেক্ট পাঠানো হবে তার সবগুলোই এই activity class রিসিভ করবে”। আগের পয়েন্টে বলেছিলাম একই মডেল ক্লাস ব্যবহার করে ভিন্ন ভিন্ন টাইপের ইভেন্ট থ্রো করা যায় আর সেটা ট্যাগের মাধ্যমে আলাদা করা যায়। MainActivity ক্লাস DataReceiveEvent এর সবগুলো ইভেন্ট রিসিভ করবে কিন্তু প্রসেস করবে শুধু যেই ইভেন্টের ট্যাগ “data_received” তাকে। অন্য কোনো ক্লাস হয়ত একই মডেল ক্লাসের মাধ্যমে ইভেন্ট থ্রো করবে। সে হয়ত সেখানে ট্যাগ দিবে অন্য একটা স্ট্রিং। সেক্ষেত্রে MainActivity ইভেন্ট রিসিভ করলেও if(event.isTagMatchWith(“data_received”)) মিথ্যা হবার কারণে এটাকে প্রসেস করবে না।

যেই ক্লাসটা সাবসক্রাইব করবে সেখানে বলে দিতে হবে কখন সে সাবসক্রিপশনের জন্য রেজিস্টার করবে আর কখন আনরেজিস্টার করবে।

@Override
    public void onStart() {
        super.onStart();
        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        super.onStop();
        EventBus.getDefault().unregister(this);
    }

অর্থাৎ MainActivity যখন onStart state এ আসবে তখন সে EventBus এর যে কোনো ইভেন্ট রিসিভ করার জন্য নিজেকে রেজিস্টার করে ফেলবে। আর যখন সে onStop state এ পৌঁছবে তখন unregister করবে।

পুরো সোর্সকোডটি পাওয়া যাবে আমার গিটহাট রিপোজিটরিতে

4 thoughts on “Android ডেভেলপমেন্টে publisher/subscriber pattern এর জন্য EventBus

  1. ব্যাপার টা অনেক টা Handler আর Looper টাইপের । কিন্তু মজাদার ।। ভাল লাগলো ।।

Leave a Reply

Your email address will not be published. Required fields are marked *