পোস্টটি পড়া হয়েছে 7,121 বার

Android এ Retrofit ব্যবহার করে GET ও POST রিকোয়েস্ট

Retrofit হচ্ছে Square এর ডেভেলপ করা একটা REST client বা network library. জাভা বা অ্যান্ড্রয়েডে এটা ব্যবহার করে খুব সহজেই নেটওয়ার্কের সাথে অর্থাৎ কোন ওয়েব সার্ভারের সাথে HTTP protocol এর মাধ্যমে কানেক্টেড হওয়া যায়। JSON ফরমেটে ডেটা আদান-প্রদানের জন্য এই লাইব্রেরিটা অতুলনীয়।

আমার ডেভেলপমেন্টের শুরুর দিকে নেটওয়ার্ক কল দেয়ার কাজ করেছিলাম AsyncTask এর মাধ্যমে। এরপর পেলাম গুগলের ডেভেলপ করা Volley library’র খোঁজ। গত প্রায় বছরখানেক ধরে সবগুলো প্রোজেক্টে ভলি লাইব্রেরি ইউজ করেছি। এখন পেলাম আরো সহজে ব্যবহার উপযোগি রেট্রোফিট লাইব্রেরি। ভলিতে কাজ করার সময় JSON ডেটা পাঠানো বা রিসিভ করার সময় manually parse করা লাগতো। রেট্রোফিটের বড় একটা সুবিধা হচ্ছে এটা JSON parse করার জন্য জনপ্রিয় আর বহুল ব্যবহৃত Gson library ব্যবহার করে। তাই সার্ভারে ডেটা পাঠানোর সময় সেটাকে আপনার প্রয়োজন অনুসারে JSON বানিয়ে পাঠানোর দরকার হবে না। রিসিভ করার সময়েও আপনাকে নিজে হাতে ধরে ধরে পার্স করা লাগবে না। আপনি পাঠানোর সময় আপনার কোন একটা ক্লাসের অবজেক্টকে পাঠিয়ে দিবেন। Retrofit-ই Gson এর সহযোগিতায় সেটাকে JSON বানিয়ে সার্ভারে পাঠাবে। আবার রিসিভ করার পরে আপনি একটা সিংগেল মেথড কল করেই রেস্পন্সে পাওয়া ডেটাকে আপনার কাংক্ষিত ক্লাসের অবজেক্টে পার্স করতে পারবেন। আর ব্যবহার করাও তুলনামূলক সহজ। চলে যাচ্ছি সরাসরি প্রোজেক্টে।

Problem Definition 1

App এ একটা মাত্র Activity থাকবে। সেখাবে বাটন ক্লিকে একটা REST API-তে কল করা হবে। সেখান থেকে পাওয়া যাবে এই মোবাইল ফোনের IP address, City, Country সহ আরো দুই একটি তথ্য। এই তথ্যগুলো শো করতে হবে। যেই API endpoint এ হিট করতে হবে তার base URL হচ্ছে https://ifconfig.co. আপনি এই লিংকে ক্লিক করলে ব্রাউজারে আপনার ডিভাইসের আইপি, সিটি, কান্ট্রি সহ আরো কিছু ইনফো দেখতে পাবেন। আর অ্যান্ড্রয়েড অ্যাপে আমরা সার্ভার থেকে ডেটা এনে দেখানোর জন্য JSON format এ ডেটা আদান প্রদান করে থাকি। JSON response এর জন্য হিট করতে হবে https://ifconfig.co/json এই end point এ। লক্ষ্য করেছেন নিশ্চয়ই যে, আগে দেয়া base URL এর পরে PATH PARAMETER হিসাবে json যুক্ত হয়েছে। JSON type ডেটার জন্য যে, পাথ প্যারামিটার হিসাবে json পাঠাতে হবে সেটা প্রথমে দেয়া লিংকে গেলেই দেখতে পাবেন।

Solution

পছন্দ মত নাম দিয়ে একটা প্রোজেক্ট ওপেন করেন অ্যান্ড্রয়েড স্টুডিও ব্যবহার করে।

Add Retrofit to Gradle file

Retrofit আর Gson library ব্যবহার করার জন্য App level gradle ফাইলের dependencies এ অ্যাড করেন নিচের দুটি লাইন। আমি এখানে 2.4.0 ভার্সন ব্যবহার করেছি। আপনি যখন কাজ করবেন তখন সর্বশেষ ভার্সনটি ব্যবহার করবেন। সর্বশেষ ভার্সন জানার জন্য Retrofit এর অফিসিয়াল সাইটে দেখতে পারেন।

Gradle Sync করেন, প্রথম কাজ শেষ।

Create some packages and classes

নিচের ছবির মত করে নেটওয়ার্ক ক্লাসগুলোর জন্য আলাদা প্যাকেজ আর ServerResponse নামের একটা ক্লাস ক্রিয়েট করি। MainActivity নিশ্চয়ই প্রোজেক্ট খোলার সময়ই তৈরি করা হয়ে গেছে!

অ্যাপে নেটওয়ার্ক কলের জন্য আমি যেই কাজগুলো পরপর করি সেগুলোকে একটার পর একটা আমি তুলে ধরছি। যেমন প্রথমে গ্র্যাডলে ডিপেন্ডেন্সি অ্যাড করি। এরপর নেটওয়ার্ক প্যাকেজ বানাই। সেখানে থাকা ApiInterface ও RetrofitApiClient ক্লাসগুলো আগের কোনো একটা প্রোজেক্ট থেকে প্রথমত কপি-পেস্ট করি। এরপর প্রয়োজন অনুসারে বিভিন্ন মডেল ক্লাস বানাই। মডেল ক্লাস বানানোর পর ApiInterface ও RetrofitApiClient প্রয়োজন মত আপডেট করি।

আমাদের এই প্রবলেমে একটা মাত্র API endpoint এ হিট করতে হবে। তাই আমাদের প্রথম জানতে হবে হিট করার পর আমরা কোন ফরমেটে ডেটা পাব। এজন্য ভিজিট করুন এই লিংকেঃ https://ifconfig.co/json. এই মুহূর্তে আমি ভিজিট করে নিচের মত ডেটাগুলো পেলাম। নিরাপত্তাজনিত কারণে আইপি অ্যাড্রেস ও hostname এ dummy data বসানো হয়েছে।

আমরা বুঝতে পারলাম অ্যাপ থেকে https://ifconfig.co/json এ হিট করলে উপরের মত জ্যাসন ফরমেটে ডেটা আমার অ্যাপ রিসিভ করবে। তাই এই জ্যাসনের অনুরূপ একটা জাভা মডেল ক্লাস বানাতে হবে। যেন এই জ্যাসনটাকে কনভার্ট করে সেই জাভা ক্লাসের অবজক্টে রূপান্তর করা যায়। বসে বসে হাতে টাইপ করে model class বানানো খুব বোরিং আর সময় নষ্ট টাইপ একটা কাজ। তাই আমি http://www.jsonschema2pojo.org/ এই সাইটটি ব্যবহার করে POJO model class এর কোড জেনারেট করি। এই সাইটে গিয়ে Source Type: JSON এবং Annotation Type: GSON সিলেক্ট করে দিন। GSON হচ্ছে গুগলের ডেভেলপ করা একটা লাইব্রেরি। এর সাহায্যে জ্যাসন অবজেক্টকে Java class এর object এবং Java class এর object কে JSON object এ কনভার্ট করা যায়। চেক করে নিন Make Classes Serializable, Include getters and setters. ফলে আপনার ক্লাসটা হবে সিরিয়ালাইজেবল এবং দরকারি getter, setter মেথডগুলোও এই কনভার্টার নিজে বানিয়ে দিবে। সিরিয়ালাইজেবল কী জিনিস সেটা জানার জন্য গুগল করতে পারেন। এরপর বাম পাশে জ্যাসন অবজেক্টটা কপি-পেস্ট করে নিচে প্রিভিউ বাটনে ক্লিক করলেই POJO class টা প্রিভিউতে দেখাবে। JSON to POJO converter দিয়ে জেনারেট করা জাভা কোডটা নিচে দেয়া হলো।

আপনার জেনারেট করা কোডে @Expose এই অ্যানোটেশন থাকতে পারে। ওগুলোকে কেটে দিতে পারেন। উপরের কোডে দেখবেন হোস্টনেমের জন্য কোনো ভ্যারিয়েবল বা গেটার-সেটার রাখা হয় নাই। এর কারণ হচ্ছে আমি আমার অ্যাপে হোস্টনেম দেখাতে চাই না। হোস্টনেমটা ইচ্ছা করেই বাদ দিয়েছি। কারণ, বুঝাতে চাচ্ছি যে network call এর response এ যা পাওয়া যাবে সব ডেটাই যে রিসিভ করে অ্যাপে রাখতে হবে এমন কোনো কথা নাই। আমাদের যেগুলো দরকার শুধু সেগুলো POJO class এ রাখাই ভাল। এতে অপ্রয়োজনীয় মেমরি ইউজ করা থেকে অ্যাপকে বাঁচাতে পারি। আবার দেখেন, IP decimal ও Country ISO এর ফিল্ডগুলো POJO class এ রেখেছি। তার মানে নেটওয়ার্ক কলের রেসপন্সে ডেটা আসলে আমাদের অ্যাপ এই ভ্যালু দুটিও রিসিভ করে রাখবে। কিন্তু এই দুইটা ডেটাও আমরা অ্যাপের কোথাও ব্যবহার করব না। এটা দিয়ে বুঝাতে চাচ্ছি যে, যদি এক্সট্রা কোনো ফিল্ডও POJO class এ থাকে যেটা নেটওয়ার্ক থেকে পাওয়া যাবে সেটা রিসিভ করলেও সমস্যা নাই। যদিও সেটা আমরা ব্যবহার না করি। সমস্যা একটাই, তা হচ্ছে এই ডেটাগুলোর জন্যেও ফোন মেমরি খরচ হবে।

@SerializedName অ্যানোটেশন দিয়ে JSON এর ভিতরে থাকা key-গুলোর নাম সেট করা হয়েছে। যেমন “ip_decimal” নামের একটা key জ্যাসনে রয়েছে। এই key এর associated value টা আমরা ipDecimal নামক ইন্টিজার ফিল্ডে assign করতে চাই। এজন্য নিচের মত কোড লিখা হয়েছে।

@SerializedName(“ip_decimal”)
private Integer ipDecimal;

যদি উপরের ২ লাইন কোডের প্রথম লাইনটা না লিখতাম তাহলে আমাদের অ্যাপ জ্যাসনের মধ্যে “ipDecimal” নামের একটা key খুঁজত। কারণ GSON library এভাবেই কাজ করে যে সিরিয়ালাইজড নেম বলে দিলে যা বলা হবে সেটার সাথেই ভ্যারিয়েবলকে ম্যাপ করে। আর যদি জ্যাসনের key-টা নির্দিষ্ট করে @SerializedName annotation এ বলা না হয় তাহলে ভেরিবলের নামটাকেই জ্যাসন key ধরে কনভার্ট করার চেষ্টা করে। যেহেতু সার্ভার থেকে ডেটাটা আসছে “ip_decimal” key এর সাথে associated হয়ে। সেহেতু এটা ছাড়া অন্য কোনো key দিয়ে ডেটা খুঁজলে সেই ডেটা পাওয়া যাবে না। অ্যাপ তখন ক্র্যাশ করবে।

RetrofitApiClient class

শুরুর BASE_URL এ সাইটের মূল বা প্রথম অংশটা লিখে দেয়া হয়েছে।

পুরো প্রোজেক্টে Retrofit এর একটা মাত্র instance-ই তৈরি হবে। সেটা নিয়ন্ত্রিত হচ্ছে getClient() মেথডের মাধ্যমে। নেটওয়ার্ক কলের জন্য RetrofitApiClient ক্লাসের একটা মাত্র অবজেক্টই সবাই ব্যবহার করবে। খেয়াল করে দেখেন, এই ক্লাসের constructor একটা private method. একটা প্রাইভেট মেথড দিয়ে কিভাবে এই ক্লাসের অবজেক্ট বানাবো? কোন ক্লাস থেকে এই ক্লাসের অবজেক্ট বানাতে গেলে কনস্ট্রাকটর ব্যবহার করা যাবে না। বরং ব্যবহার করতে হবে getClient() এই স্ট্যাটিক মেথডটি। এই মেথডের ভিতরে চেক করা হচ্ছে এই ক্লাসেরই একটা প্রাইভেট ডেটা retrofit অবজেক্টটা null কিনা। retrofit একটা প্রাইভেট ডেটা, এটার কোন সেটারও নাই। তাই অন্যান্য ক্লাস থেকে ইচ্ছা করলেও এই ডেটাকে ইনিশিয়ালাইজ করা সম্ভব না। প্রথমবার getClient() কল হবার সময় retrofit==null সত্য হবে। সত্য হলে এর ভিতরে retrofit কে initialize করা হয়েছে। এরপর return করে দেয়া হয়েছে। পরের বার আবার যদি getClient() কল করা হয় তখন এই প্রথম বার তৈরি হওয়া instance এর রেফারেন্সটাই রিটার্ন করে দেয়া হবে। null check এর পরে synchronized keyword ব্যবহার করা হয়েছে এই অবজেক্ট ক্রিয়েট করার কাজটাকে thread safe করার জন্য। যদি আলাদা আলাদা জায়গা থেকে বা থ্রেড থেকে একই সময়ে getClient() কল হয় তাহলে যেন কোনো ক্রমেই দুইটা অবজেক্ট ক্রিয়েট না হয় সেজন্য সিনক্রোনাইজড কীওয়ার্ড ইউজ করা হয়েছে। যেখান থেকে যতবারই কল হোক না কেন সিনক্রোনাইজ ব্লকটা একই সময়ে একজনই অ্যাক্সেস করতে পারবে। এই যে কোন একটা ক্লাসের একটা অবজেক্টই শুধু তৈরি করা যাবে এই বাধ্যবাধ্যকতার একটা গাল ভরা নাম আছে। গুরুজনেরা এটাকে Singleton Design Pattern বলে থাকেন। আর সিনক্রোনাইজড কীওয়ার্ড ইউজ করা হয়েছে এটাকে থ্রেড সেফ করার জন্য। তাই এটাকে বলতে পারি thread safe singleton. এই ডিজাইন প্যাটার্ন ব্যবহার করে অনেক বড় বড় হাতি-ঘোড়া মারা যায়। শুধু Singleton Design Pattern এর উপর বিস্তারিত একটা ব্লগ পোস্ট করার ইচ্ছা আছে। আপাতত এইটুকু আইডিয়া থাকলেই হবে।

retrofit কে initialization এর সময় addConverterFactory() ব্যবহার করা হয়েছে যার প্যারামিটার হিসেবে দেয়া আছে ক্লাসের ভিতরে বানানো Gson ক্লাসের একটা প্রাইভেট object. এটা দিয়ে বুঝানো হয়েছে যে retrofit অবজেক্টের মাধ্যমে যত ডেটা পাঠানো হবে সেগুলো যাওয়ার আগে আমাদের মডেল ক্লাস অনুযায়ী সাইজ হয়ে যাবে। আর এই কনভার্ট বা সাইজ করার মহান দায়িত্ব পালন করতে সাহয্য করবে gson অবজেক্টটি।

ApiInterface class

আমাদের অ্যাপে যে কয়টা নেটওয়ার্ক কল হবে সবগুলো নেটওয়ার্ক কলের জন্য আলাদা আলাদা মেথডের সিগনেচার ডিফাইন করতে হবে একটা interface এ। যেহেতু একটা নেটওয়ার্ক কলের কাজ হবে, তাই getMyIp() নামের একটা মেথডের সিগনেচার এখানে ডিফাইন করা করা হয়েছে। এর আগে বলে দেয়া হয়েছে এটা একটা GET request আর এর পাথ প্যারামিটার “/json”. সাইটের যে base URL আছে তার সাথে concate হবে এই পাথ প্যারামিটার। মেথডের রিটার্ন টাইপ হচ্ছে Call generic class এর একটা অবজেক্ট। যেটা ক্রিয়েট করা হবে ServerResponse ক্লাসের সমন্বয়ে। Call, @GET উভয়েই Retrofit library এর ক্লাস ও অ্যানোটেশন। এই দুইটার জন্য শুরুতে এগুলোর ক্লাসকে import করে নেয়া হয়েছে।

Network call from MainActivity class

MainActivity-তে থাকা বাটন ক্লিক করলে নিচের মেথডটি কল হবে। এর প্রথম লাইনে একটা ProgressBar দেখানো হয়েছে। পরের লাইনে ApiInterface এর একটা instance নেয়া হয়েছে। এরপর ইন্টারফেসে ডিফাইন করা মেথডটি কল করা হয়েছে। এটি রিটার্ন করবে Call<ServerResponse> ক্লাসের একটা অবজেক্ট।

চতুর্থ লাইনে নেটওয়ার্কে কল দেয়ার জন্য call.enqueue() মেথড কল করা হবে। এই মেথডের argument হিসাবে পাঠাতে হবে Callback<ServerResponse> ইন্টারফেসের একটা implementation. মেথডের ভিতর আর্গুমেন্ট হিসাবে new Callback লিখা শুরু করলেই অটো সাজেশন আসবে। enter press করলে দেখবেন দুইটা মেথড override করা হয়েছে। একটা onResponse() অপরটা onFailure(). সার্ভারে হিট করে যদি সার্ভার থেকে ঠিকঠাক মত ডেটা পাওয়া যায় তখন onResponse() কল হবে। তখন আমরা আইপি অ্যাড্রেস সহ অন্যান্য অ্যাড্রেস শো করে দিচ্ছি। আর কোনো কারণে রিকোয়েস্টটা ফেইল করলে onFailure() মেথডটা কল হবে। তখন আমরা IP Address শো করানোর TextView-তে error message-টা শো করাচ্ছি আর বাকি ফিল্ডগুলোতে empty string প্রিন্ট করানোর মাধ্যমে ফাঁকা করে দিচ্ছি।

রিকোয়েস্ট success বা failure যেটাই হোক না কেন ProgressBar দেখানো বন্ধ করে দেয়া হচ্ছে।

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

শুধু সার্ভারে হিট করে ডেটা নিয়ে আসাই একমাত্র শেখার মত কাজ না। ব্যাপারটা ভাল ভাবে বুঝতে হলে ওয়েব সার্ভার কিভাবে কাজ করে সেগুলো আস্তে আস্তে জানতে হবে। Client-Server Architecture নিয়ে পড়াশোনা করতে হবে। HTTP verbs-গুলোর আইডিয়া ক্রিস্টাল ক্লিয়ার থাকতে হবে। উপরের কোডে দেখেছেন রেসপন্স কোড ২০০ কিনা চেক করা হয়েছে। প্রতিটা সার্ভার রিকোয়েস্টে সার্ভার একটা রেসপন্স কোড পাঠায়। আমাদের কাছে সবচেয়ে পরিচিত সার্ভার রেসপন্স হচ্ছে 404 (No found). এরকম আরো যতগুলো রেসপন্স কোড আছে, কোনটা দিয়ে কী বুঝায় সেগুলো জানতে হবে।

সার্ভার সাইডের কাজগুলো কিভাবে হয় বা একজন ওয়েব ডেভেলপার বা API developer কিভাবে কাজ করেন। তাকে কিভাবে সব কিছু হ্যান্ডেল করতে হয় সেটার অন্তত ব্যাসিক লেভেলের জ্ঞান মোবাইল ডেভেলপারদের না থাকলে নেটওয়ার্ক কল নিয়ে ভাল ভাবে কাজ করা কঠিন। তাই আমার মতে অন্তত কিছু ছোটখাটো হলেও প্রত্যেক মোবাইল ডেভেলপারকেও ওয়েব এপিআই ডেভেলপ করা উচিত। সেজন্য পরের প্রবলেমটা দেয়া হলো। পরের প্রবলেমে মোবাইল অ্যাপ থেকে যেই সার্ভারে হিট করব সেই সার্ভারের কোডও আমরা নিজেরা লিখব। তাহলে আশা করি পুরো কাজের ফ্লো-টা আরো ক্লিয়ারলি বুঝা যাবে।

Problem Definition 2

একটা Activity থাকবে। লগিন আইডি আর পাসওয়ার্ড ইনপুট দেয়ার ফিল্ড থাকবে। টেস্ট করার সুবিধার জন্য লগিন আইডি hasan আর পাসওয়ার্ড 123 ফিক্সড করে দেয়া হয়েছে। লগিন আইডি আর পাসওয়ার্ড ইনপুট দিয়ে লগিন বাটনে ক্লিক করলে সার্ভারে চলে যাবে এই আইডি-পাস। সেখানে চেক হবে আইডি-পাস ঠিক আছে কিনা। ঠিক থাকলে রেসপন্সে পাঠাবে ‘Your are a valid user’ আর ভুল হলে পাঠাবে ‘User ID or Password is wrong’. যেহেতু এই নেটওয়ার্ক কলে পাসওয়ার্ড পাঠানো হচ্ছে তাই সিকিউরিটির একটা ব্যাপার আছে। তাই এই ডেটাগুলো পাঠানো হবে POST method এ।

একই একটিভিটিতে আরেকটা টেক্সট ইনপুটের ফিল্ড থাকবে। সেখানে ইউজার আইডি হিসেবে hasan দিয়ে সাবমিট করলে সার্ভার আপনাকে একটা joke পাঠাবে। Joke টা শুধু hasan আইডি এর জন্যেই পাঠানো হবে। অন্য কোন আইডি দিলে সার্ভার রেসপন্সে পাঠাবে ‘You are not a valid user’. আর এই নেটওয়ার্ক রিকোয়েস্টটা হবে GET method এ।

উপরের দুইটা কাজ করার মাধ্যমে আমরা GET-POST দুইটা মেথডই শিখব।

Open a project

পছন্দ মত নাম দিয়ে একটা প্রোজেক্ট ওপেন করেন অ্যান্ড্রয়েড স্টুডিও ব্যবহার করে।

Add Retrofit to Gradle file

Retrofit আর Gson library ব্যবহার করার জন্য App level gradle ফাইলের dependencies এ অ্যাড করেন নিচের দুটি লাইন। আমি এখানে 2.4.0 ভার্সন ব্যবহার করেছি। আপনি যখন কাজ করবেন তখন সর্বশেষ ভার্সনটি ব্যবহার করবেন। সর্বশেষ ভার্সন জানার জন্য Retrofit এর অফিসিয়াল সাইটে দেখতে পারেন।

Gradle Sync করেন, প্রথম কাজ শেষ।

Create some packages and classes

MainActivity ছাড়াও আরো চারটা ক্লাস/ফাইল বানাতে হবে। ম্যানেজ করার সুবিধার্থে ৪ টা প্যাকেজ বানিয়ে নিয়ে পারেন ছবির মত করে। Package-গুলো হচ্ছেঃ Activity, Interface, Model ও Retrofit.

Activity প্যাকেজে MainActivity-কে drag and drop করুন। এরপর বাকি প্যাকেজগুলোতে যথাক্রমে ApiInterface, ServerResponse, User ও RetrofitApiClient নামের ক্লাসগুলো create করেন।

retrofit-1

এখন একটা একটা করে ক্লাস দেখব আর বর্ণনা করব।

Model classes

মডেল ক্লাসের মধ্যে আমাদের ডেটাগুলো কিভাবে থাকবে না থাকবে সেটা বলা হয়েছে। Problem Definition থেকে এতক্ষণে হয়ত বুঝে গেছেন আমরা ইউজারের আইডি আর পাসওয়ার্ড ইনপুট নিচ্ছি। এটা সার্ভারে পাঠাচ্ছি। তাহলে User একটা ক্লাস হতে পারে। যার মধ্যে id, password এই ডেটাগুলো থাকবে।

সোজা-সাপটা একটা ক্লাস। দুইটা ডেটা আর দুইটা মেথড আছে। একটু কারিগরি ফলানো হয়েছে ডেটাগুলো declare করার আগের লাইনে। এইটুকু কারিগরিই আমাদের এই ক্লাসের অবজেক্টকে দরকার মত JSON বানিয়ে দিবে। যখন এই User ক্লাসের কোন অবজেক্টকে সার্ভারে পাঠাবো তখন Gson library খুঁজবে যে এই ক্লাসের ডেটাগুলোর নাম কী? সে এসে এই ক্লাসের ডেটা বা ভেরিয়েবলগুলোর নাম দিয়েই JSON key বানাবে। কিন্তু আমরা “userId” এরকম JSON key চাই না। আমাদের দরকার “user_id” এই ফরমেট। এটার জন্যেই ভেরিয়েবলের শুরুতে লিখে দেয়া হয়েছে @SerializedName(“user_id”). কারণ Gson library শুরুতে খুঁজে দেখে @SerializedName আছে কিনা। যদি থাকে তাহলে তার পরের ভেরিয়েবলের ডেটাকে @SerializedName এর ভিতরের string এর Key হিসেবে সার্ভারে পাঠায়।

যদি @SerializedName ব্যবহার না করতাম তাহলে এই ক্লাসের অবজেক্টকে সার্ভারে পাঠানো হত এই ফরমেটেঃ

কিন্তু @SerializedName এর ভিতরে প্যারামিটার দিয়ে সেট করে দেয়াতে আমাদের লিখা ক্লাসের অবজেক্টকে পাঠানো হবে এই ফরমেটেঃ

লাইফ অনেক সহজ হয়ে গেল তাই না? 🙂

এ তো গেল যেই ডেটা সার্ভারে পাঠাবো তার Model. সার্ভার থেকে তো কিছু ডেটা আসবে। সেগুলোকে রিসিভ করে ফরমেট করার জন্য আরেকটা ক্লাস দরকার। সেই ক্লাসের নাম দিয়েছি ServerResponse ক্লাস।

আমরা সার্ভারে দুইটা কল করছি। একটা করছি লগিন করার জন্য অর্থাৎ POST method শেখার জন্য। আরেকটা হচ্ছে সার্ভারে আইডি পাঠিয়ে একটা কৌতুক পড়তে চাচ্ছি। এটা GET method শেখার জন্য। টিউটোরিয়ালের সাইজ ছোট করার জন্য বুদ্ধি করে দুইটা রিকোয়েস্টের রেসপন্সের ডেটা একই রেখেছি। লগিনের সময় সার্ভার আমাকে দুইটা ডেটা পাঠাবে। একটা হচ্ছে status (login successful কিনা সেটার true-false স্ট্যাটাস) আরেকটা ডেটা হচ্ছে message (status true হলে সার্ভার পাঠাবে You are a valid user আর false হলে পাঠাবে ID/Password wrong). GET request এর কৌতুক রিসিভ করার জন্যেও একই JSON KEY ব্যবহার করা হচ্ছে। আইডি সঠিক হলে সার্ভার থেকে status এ true, সাথে message এ কৌতুক পাঠানো হবে। অন্যথায় status এ false আর message এ বলা হবে Invalid User.

এত কথা বলার কারণ হচ্ছে, আপনি যতগুলো সার্ভার কল করবেন তার রেসপন্সের ডেটাগুলো ভিন্ন ভিন্ন হতে পারে। সবগুলো ভিন্ন ভিন্ন JSON object এর জন্য আলাদা আলাদা response ক্লাস বানাতে হবে। এত্তগুলা ক্লাস বানাতে কষ্ট লাগবে? বানায়ে দেখেন, পরের কাজ কত্ত সোজা হয়ে যায়!

এরপরেও যারা ভয় পাচ্ছেন তাদের জন্য JetBrain এর POJO Generator প্লাগিন আছে যেটা Android Studio এর সাথে এড করে ব্যবহার করতে পারবেন। এতে একটা JSON object ইনপুট দিলে আউটপুট হিসেবে Java model class পাওয়া যায়। এছাড়াও online pojo class generator রয়েছে। গুগল করলেই সব পেয়ে যাবেন।

RetrofitApiClient class

প্রথম প্রবলেমের মত এটার কোডও হুবহু এক রকম। শুধু পার্থক্য হচ্ছে শুরুর BASE_URL এ আমার পিসির localhost এর IP বসানো আছে। ইমুলেটর বা রিয়েল ডিভাইস (যদি WiFi এর সাথে কানেক্টেড থাকে তাহলে রান করে) যেন চেক করা যায় তাই এই আইপি দেয়া। আপনার যদি রিয়েল সার্ভার থাকে তাহলে সেটার লিংক এখানে বসবে।

ApiInterface class

এটা সাধারণ জাভা ক্লাস না। এটা ইন্টারফেস। আপনি ক্লাস বানানোর সময় এটা অটোমেটিক্যাল্যি পাবলিক ক্লাস হিসেবে তৈরি হয়েছিল। নিচের কোডটা কপি পেস্ট করুন আপনার এই ফাইলেঃ

একটা পোস্ট আরেকটা গেট মেথড উল্লেখ করা আছে। @POST annotation এর পরে উল্লেখ করা হয়েছে সার্ভারের রাউট। Base URL আমরা RetrofitApiClient ক্লাসে উল্লেখ করেছিলাম। সেই বেজ লিংক কিন্তু সাধারণত সবগুলো কলের ক্ষেত্রে একই থাকে। যেমন বেজ লিংক হিসেবে বলা হয়েছে লোকালহোস্টের আইপি বা localhost. localhost এ ঢুকে আমি একটা ফোল্ডার বানিয়েছি retrofit_get_post নামে। এর ভিতরে একটা ফাইল ক্রিয়েট করেছি server_side_code.php নাম দিয়ে। তার মানে আমি যদি কোন রিকোয়েস্ট এই ফাইলে পাঠাতে চাই তাহলে localhost এর URL এর পরে ফাইলের path-ও বলে দিতে হবে। লোকালহোস্টের URL RetrofitApiClient এ উল্লেখ করেছি। এখানে বাকি অংশটা উল্লেখ করলাম। POST method এর ক্ষেত্রে আমাদের রিকোয়েস্টটা যাবে মূলত http://192.168.0.101/retrofit_get_post/server_side_code.php  অ্যাড্রেসে। POST, GET উভয় ক্ষেত্রেই একই রাউট দেখা যাচ্ছে। এর কারণ হচ্ছে আমি একটা PHP ফাইল দিয়েই উভয় রিকোয়েস্ট হ্যান্ডেল করেছি। আপনার পোস্ট, গেট এর ফাইল যদি আলাদা হয় তাহলে সেই অনুযায়ী রাউট বলে দিবেন।

POST request এর মাধ্যমে আমরা একজন ইউজারের ভ্যালিডিটি চেক করতে চাচ্ছি। তাই @POST annotation এর পরের লাইনে মেথডের নাম দিয়েছি getUserValidity(). এতে প্যারামিটার হিসেবে পাঠানো হয়েছে User ক্লাসের একটি অবজেক্টকে। সেটার শুরুতে উল্লেখ করতে হবে @Body annotation. এতো গেল পাঠানোর কথা। সার্ভার থেকে যেটা পাঠাবে সেটা রিসিভ করতে হবে না? সেটা রিসিভ করে কোন ক্লাস অনুযায়ী JSON parse করা লাগবে? ServerResponse ক্লাস অনুযায়ী তাই না? সেটাই বলা হয়েছে getUserValidity() এর শুরুতে return type হিসাবে।

দ্বিতীয় মেথডটা GET রিকোয়েস্টের জন্য। এতে প্যারামিটার হিসেবে কোন অবজেক্ট পাঠাচ্ছি না। শুধু একটা ইউজার আইডি পাঠালেই হচ্ছে তাই স্ট্রিং হিসেবে এটাকে পাঠিয়ে দিচ্ছি। @Query annotation এর ভিতরে “user_id” দিয়ে এরপর একটা স্ট্রিং এর অবজেক্ট userId উল্লেখ করা হয়েছে। এর মানে হচ্ছে userId স্ট্রিং অবজেক্টটা “user_id” এর সাথে map হবে। অর্থাৎ user_id হবে key আর userId হবে value. POST request এ URL এর সাথে ডেটা দৃশ্যমান থাকে না। কিন্তু GET request এ কিন্তু ডেটাগুলো URL এর সাথে যায়। আমার করা PHP কোড অনুযায়ী অ্যাপ থেকে গেট রিকোয়েস্টে হিট করতে হবে http://192.168.0.101/retrofit_get_post/server_side_code.php?user_id=hasan এই লিংকে। লক্ষ্য করেন, মূল লিংকের পরে ?user_id=hasan যুক্ত হয়েছে। আমার সার্ভার সাইডের কোড user_id এর ভ্যালু hasan কিনা সেটা চেক করবে। কিন্তু প্রশ্ন হচ্ছে জাভা কোডের @GET annotation এর ভিতরে কিন্তু এই টাইপের কিছুই উল্লেখ নাই, তাহলে এই লিংকে হিট করবে কেমনে? উত্তর হচ্ছে (@Query(“user_id”) String userId), এটাই লিংকের শেষে “?” ও “user_id” যোগ করে সমান চিহ্ন দিয়ে এরপরে userId স্ট্রিং এর ভ্যালু বসায় দিবে।

@Body, @Query, @Path এরকম বেশ কয়েকটা annotation আছে। গুগল করে জেনে নিবেন প্লিজ।

উপরে আলোচনা করা হয়েছে এমন দুইটি API request নিয়ে যাদের base url আমরা RetrofitApiClient ক্লাসে বলে দিয়েছি। কিন্তু যদি কখনো ভিন্ন একটা URL এ হিট করার দরকার হয়? অর্থাৎ যেই URL টা আপনি রান টাইমে হিট করবেন, যার base url আগে assign করা url থেকে ভিন্ন তখন কী করবেন? সেটা করা খুব সহজ!

এক্ষেত্রে @GET এর পরে route বলে দিতে হবে না। কারণ আপনি তো predefined base url এর কোনো রাউটে হিট করতে চাচ্ছেন না। তাই রাউটের অংশটুকু ফাঁকা থাকবে। আর আপনি যেই url এ হিট করতে চাচ্ছেন সেটা মেথডের প্যারামিটার হিসাবে পাঠাতে হবে। এটা যদি আবহাওয়ার আপডেট পাওয়ার কোনো end point হয় তাহলে আপনার dynamicUrl এর ভ্যালু হতে পারে http://weather-update.com/api/dhaka/. অর্থাৎ এই মেথডটি কল করার সময় আপনার সম্পূর্ণ URL-টাই পাঠিয়ে দিতে হবে।

Network call from MainActivity class

MainActivity তে Interface class এর একটা instance বানাতে হবে। ক্লাসের ডেটা মেম্বার হিসেবে উল্লেখ করা যায়ঃ

onCreate() এর ভিতরে initialize করতে পারেন এভাবেঃ

Login এর button click event থেকে এই মেথডটা কল করে পোস্ট রিকোয়েস্ট পাঠানো হচ্ছে ও রেসপন্স রিসিভ করা হচ্ছেঃ

সার্ভার থেকে রেসপন্স যদি ঠিকঠাক মত আসে তাহলে সেটা রিসিভ হবে onResponse() এর ভিতরে। কিন্তু যদি আপনার এখান থেকে কোন কারণে রিকোয়েস্ট সেন্ড না হয়, বা সার্ভারে কোন ঝামেলা থাকে। বা দুই দিকের data key না মিলে অথবা URL ভুল থাকে ইত্যাদি কারণে সার্ভারে রিকোয়েস্ট গিয়ে ‘সহি সালামতে’ ফেরত না আসলে সেটার ট্র্যাক পাবেন onFailure মেথডে। সার্ভার থেকে আসা রেসপন্সকে আমাদের ServeResponse ক্লাসের অবজেক্টে কনভার্ট করা হচ্ছে validity = resoponse.body() এই একটা মাত্র মেথড কল করার মাধ্যমে।

GET request এর জন্যেও প্রায় একই ভাবে কাজ করবে এই মেথডটিঃ

Dynamic URL এ রিকোয়েস্ট পাঠানোর কোডঃ

Server side PHP Code

আপনার যদি লাইভ সার্ভার না থাকে সেক্ষেত্রে Xampp/Wamp এর মাধ্যমে আপনার পিসিকেই সার্ভার হিসেবে ব্যবহার করে অ্যাপটা টেস্ট করতে পারবেন। Xampp/wamp ইন্সটলের পর নির্দিষ্ট ফোল্ডারে গিয়ে retrofit_get_post নামের একটা ফোল্ডার বানান। এর ভিতরে তৈরি করুন server_side_code.php নামের একটি php file. <?php ?>  tag এর ভিতরে কপি-পেস্ট করুন নিচের কোডঃ

ঠিকঠাক মত UI বানিয়ে রান করলে আশা করি কোড কাজ করবে। আমি বলা চলে অ্যান্ড্রয়েডে বিগিনার। অনেক বেস্ট প্র্যাক্টিসই জানি না। অভিজ্ঞ কারো চোখে ব্লগটা যদি পরে তাহলে কোথাও কোন পরিবর্তন-পরিবর্ধন বা পরামর্শ থাকলে অনুগ্রহ করে জানাবেন। আদৌ কিছু বুঝাতে পেরেছি কিনা সেটাও জানাবেন। কেউ যদি এই ব্লগ পড়ে প্রথম বারের রেট্রোফিট ইমপ্লিমেন্ট করে থাকেন তাহলে আপনাকে অভিনন্দন। ধন্যবাদ আমার পরিশ্রমকে সফল করার জন্য। কমেন্ট করে জানাতে ভুলবেন না যে আপনি প্রথম বারের মত রেট্রোফিট ব্যবহার করেছেন আমার ব্লগ পড়ে।

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

এটাই শেষ নয়!!! আর এখানে Android এর যেই ইমপ্লিমেন্টেশন দেখানো হয়েছে রিয়েল প্রোজেক্টে সেভাবেও করা উচিত হবে না। কিন্তু প্রথম শেখার জন্য এটা ঠিক আছে। কিছু best practice আর better implementation এর জন্য পড়ুন Retrofit এর উপর লেখা পরের পর্বটি

18 thoughts on “Android এ Retrofit ব্যবহার করে GET ও POST রিকোয়েস্ট

  1. A very good one indeed about Retrofit. I wrote a post back in 2015 when Retrofit 2 was in beta and there wasn’t resources available about it. Because a lot of changes was brought in 2.0. I was active in the repo at that time, mostly in issue tracker. Uploading image with multipart post request wasn’t easy at that time, fixing that problem was my very small contribution there. I still get messages/queries from people about it.

    I hope you’ll have to help people more from now on 🙂 Keep up good writing.

  2. ভাইয়া আপনি যে বললেন Volley তে ম্যানুয়াল পার্স করতে হয়, কিন্তু আমাকে একজন বড় ভাই
    দিয়ে এমনভাবে রেডি করে দিয়েছেন যে, আর পার্স করা নিয়ে চিন্তা করতে হয়না। মডেল ক্লাস টা বলে দিলেই ওইটা অনুযায়ী ডাটা রেডি করে দেয়। আমার কাছে Retrofit বেশি কঠিন লাগতেসে 🙁 । আপনি বললে আপনার সাথে Volley শেয়ার করতে পারি যেটা আমি ব্যবহার করি।

    1. করা যেতে পারে। আমি ২-১ টা প্রোজেক্টে ভলি ইউজ করেছি। এখন নতুন যে কোনো প্রোজেক্টে রেট্রোফিট ইউজ করি। এটা আসলে ডেভেলপার চয়েজ। নেটে সার্চ করলে উভয়ের সুবিধা-অসুবিধা নিয়ে বিস্তর তর্ক পাওয়া যাবে। রেট্রোফিটের instance create করার জন্য যেই ক্লাসটা ইউজ হয় সেটা কমন। আমি গ্র্যাডলে রেট্রফিটের জন্য একটা লাইন লিখি। instance create এর ক্লাস আর interface class আগের কোনো প্রোজেক্ট থেকে কপি করি। ইন্টারফেসের method signature গুলো দরকার অনুযায়ী ঠিকঠাক করে নিই। ব্যস! এটা আমার কাছে বেশি সুবিধাজনক মনে হয়। Dynamic Url ইউজ করা Retrofit এ একদম ইজি। ভলিতে করার দরকার হয় নাই তাই জানি না প্রসেসটা। দিন শেষে যে যেটা ব্যবহার করে সুখ পায় তার জন্য অনেক ক্ষেত্রে সেটাই ইউজ করা উচিত। 🙂

    1. Retrofit deals with JSON format data. By default it uses GsonConverter to convert JSON to POJO and POJO to JSON data. REST mostly use JSON format data between client and server.

      Anyway… I think, if you send to any endpoint and that endpoint serves HTML response then you’ll get HTML data in your App. After receiving those data you have to parse them tag by tag. I believe it’s not so suitable for mobile platform.

      If you have not API endpoint but you need to collect data from a HTML webpage, then you can use JSOUP library. To know about JSOUP library you can follow my blog post from here.

      N.B: Scraping has some legal issues. Don’t forget that!

    1. “Pura ekta test source code” বলতে কী বুঝাচ্ছেন? এখানে GET, POST দুইটা মেথডের কোড দেখানো হয়েছে। এই দুইটা বুঝলে বাকি মেথডগুলো গুগল করে শিখে নেয়া যাবে

  3. আমি এন্ডয়েডে নতুন। আপনার ব্লগ পরে অনেক অসাধারন কিছু সহজে শিখতে পারতেছি। আশা করি আপনি গুরুত্বপূর্ন সব বিষয়গুলো নিয়ে আপনার লিখা চালিয়ে যাবেন। ঈশ্বর আপনার মঙ্গল করুণ।
    ধন্যবাদ

  4. POST and GET এর জন্যে কোনো query লিখার প্রয়োজন হলে তা কীভাবে, কোথায় add করব?

    1. আমি ঠিক ক্লিয়ার না আপনার প্রশ্নটা।
      আপনি কি এমন কিছুর কথা জানতে চাচ্ছেন যে, সার্ভারে কোনো ডেটার উপর সার্চ চালিয়ে সার্চের রেজাল্টটা মোবাইল অ্যাপে পাঠানোর ব্যাপারে?
      যদি তাই হয় তাহলে এটা করতে হবে সার্ভার সাইডের কোডে। সেটা PHP বা যেই ল্যাঙ্গুয়েজেই করা হোক না কেন। সেখানে ডেটাবেজ ইউজ করতে হবে। এই পোস্টটাকে সিম্পল রাখার জন্য সার্ভার সাইডে ডেটাবেজ ইউজ করা হয় নি। আর এটা যেহেতু মোবাইল অ্যাপের টিউটোরিয়াল তাই সার্ভার সাইডের বিষয়গুলো খুব একটা দেখানোও হয় নি। সার্ভার সাইডের জন্য অনলাইনে বেশ ভাল ভাল টিউটোরিয়াল আছে। আপনার প্রয়োজন অনুসারে সার্চ করে দেখতে পারেন।

Leave a Reply

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