দারুণ একটা বিষয় শিখতে যাচ্ছি আমরা। বিভিন্ন সাইটের API এর মাধ্যমে XML বা JSON ডেটা দিয়ে থাকে। যেমন গুগল ম্যাপ নিয়ে কোন অ্যাপ তৈরি করব, তখন গুগল ম্যাপ API কল করলে আমাদের কিছু ডেটা পাঠাবে, সেগুলো আমরা আমাদের অ্যাপে দেখাবো। ঐ ডেটা গুলো একজন ব্যবহারকারীকে দেখালে সে কিছুই বুঝবে না। সেগুলোকে সুন্দর করে পার্স করে দেখাতে হবে। আর আজকে আমরা দেখব কিভাবে ওয়েব সাইট থেকে একটা XML ফাইল রিড করতে হয় এবং XML ফাইলের ডেটা গুলো হিউম্যান রিডএবল করতে হয়।
এ টিউটোরিয়ালে আমরা W3school থেকে পাওয়া সিম্পল একটা XML ফাইল ব্যবহার করব। যেখানে সকালের খাবারের আইটেম গুলোর কিছু তথ্য দেওয়া আছে। ফাইলটি দেখা যাবে এখানে ক্লিক করে।
প্রথমেই activity_main.xml নিয়ে বলি, এটিতে একটি বাটন থাকবে, যেখানে ক্লিক করলে আমাদের ডেটা গুলো লোড হবে। ইন্টারনেট থেকে ডেটা গুলো লোড হতে কিছু সময় লাগবে। আর এ জন্য আমরা যখন ডেটা লোড করব, তখন একট প্রগ্রেসবার বা লোডিং চিহ্ন দেখাবো। এবং ডেটা গুলো লোড হয়ে একটা টেক্সটভিউতে দেখাবে। সম্পূর্ণ কোডঃ
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="me.jakir.androidxmlparsing.MainActivity"> <Button android:layout_width="match_parent" android:layout_height="wrap_content" android:text="Load and Parse XML" android:id="@+id/loadXML" android:layout_alignParentTop="true" android:layout_centerHorizontal="true" /> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:text="" android:id="@+id/output" android:layout_below="@+id/loadXML" android:layout_centerHorizontal="true" android:layout_marginTop="23dp" /> <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/progressBar" android:layout_below="@+id/output" android:layout_centerHorizontal="true" android:layout_centerVertical="true" /> </RelativeLayout>
MainActivity.java যে কোড গুলো লাগবে, সে গুলো নিয়ে বলি। অ্যান্ড্রয়েডে কোন ব্যাকগ্রাউন্ড টাস্ক UI thread থেকে করা যায় না। সাধারণ ভাবে বলতে গেলে Activity থেকে করা যায় না। কিন্তু আমরা ইন্টারনেট থেকে XML ডেটা কল করব ব্যাকগ্রাউন্ডে। ব্যাক গ্রাউন্ডে করার জন্য আলাদা একটা ক্লাস রয়েছে, AsyncTask নামে। আমরা একটা নতুন ক্লাস তৈরি করে AsyncTask কে এক্সটেন্ড করে তারপর আমাদের ব্যাক গ্রাউন্ড কাজ গুলো করতে পারি। তো আমাদের মেইন এক্টিভিটি থেকে আগে দেখব ইন্টারনেট কানেক্ট আছে কিনা, যদি থাকে তাহলে আমরা ব্যাকগ্রাউন্ডে XML লোড এবং পার্স করব। যদি না থাকে, তাহলে একটা টোস্ট দেখাবো, যেখানে লেখা থাকবে ইন্টারনেট নেই।
ইন্টারনেট চেক করা সহজ। এভাবে চেক করতে পারিঃ
private boolean isOnline() { ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); if (netInfo != null && netInfo.isConnectedOrConnecting()){ return true; }else return false; }
ইন্টারনেট চেক করার জন্য আমাদের পারমিশনের প্রয়োজন হবে। নিচের পারমিশন দুইটি AndroidManifest এ যুক্ত করে দিবঃ
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.INTERNET"/>
যদি ইন্টারনেট থাকে, তাহলে আমরা আমদের xml url টা পাস করব BackgroundTask এ। সম্পূর্ন MainActivity.java
import android.content.Context; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; import android.view.View; import android.widget.Button; import android.widget.ProgressBar; public class MainActivity extends AppCompatActivity { Button loadXML; ProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // set main activity setContentView(R.layout.activity_main); // reference to view loadXML =(Button) findViewById(R.id.loadXML); progressBar = (ProgressBar) findViewById(R.id.progressBar); progressBar.setVisibility(View.INVISIBLE); loadXML.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (isOnline()){ BackgroundTask task = new BackgroundTask(MainActivity.this); task.execute("http://www.w3schools.com/xml/simple.xml"); } } }); } private boolean isOnline() { ConnectivityManager cm = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo netInfo = cm.getActiveNetworkInfo(); if (netInfo != null && netInfo.isConnectedOrConnecting()){ return true; }else return false; } }
BackgroundTask.java
package me.jakir.androidxmlparsing; import android.app.Activity; import android.os.AsyncTask; import android.view.View; import android.widget.ProgressBar; import android.widget.TextView; import java.io.BufferedReader; import java.io.InputStreamReader; import java.net.HttpURLConnection; import java.net.URL; import java.util.List; /** * Created by Jack on 2/17/2016. */ public class BackgroundTask extends AsyncTask<String, String, String> { Activity mainActivity; ProgressBar progressBar; TextView output; List<Food> foodList; public BackgroundTask(Activity activity) { mainActivity = activity; } @Override protected void onPreExecute() { // show progress bar progressBar = (ProgressBar) mainActivity.findViewById(R.id.progressBar); progressBar.setVisibility(View.VISIBLE); } @Override protected String doInBackground(String... params) { String content = getData(params[0]); return content; } @Override protected void onPostExecute(String result) { // Hide Progress bar progressBar.setVisibility(View.INVISIBLE); output = (TextView) mainActivity.findViewById(R.id.output); foodList = XmlParser.parse(result); if(foodList != null){ for (Food foodItem: foodList) { output.append("Item name: " + foodItem.getName() + "\n"); output.append("Item price: " + foodItem.getPrice() + "\n"); output.append("Item Description: " + foodItem.getDescription() + "\n \n \n"); } } } // Method for get XML data using HTTP Request private String getData(String uri) { BufferedReader reader; try { URL url = new URL(uri); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); StringBuilder stringBuilder = new StringBuilder(); reader = new BufferedReader(new InputStreamReader(connection.getInputStream())); String line; while ((line = reader.readLine()) != null) { stringBuilder.append(line + "\n"); } return stringBuilder.toString(); } catch (Exception e) { e.printStackTrace(); return null; } } }
BackgroundTask.java তে আমরা অনেক গুলো কাজ করেছি। BackgroundTask.java কে আমরা এক্সটেন্ড করেছি AsyncTask ক্লাসটি। এ ক্লাসে অনেক গুলো মেথড রয়েছে। onPreExecute, onPostExecute, doInBackground ইত্যাদি। মেথড গুলোর নাম থেকেই বুঝা যাচ্ছে কোনটার কাজ কি।
আমরা onPreExecute এ প্রগ্রেস বারটি দেখাবো। ব্যবহার কারী যেন বুঝতে পারে ব্যাকগ্রাউন্ডে কিছু কাজ হচ্ছে।
doInBackground মূলত AsyncTask যে জন্য, তা করবে এখানে। এখানে আমরা ইন্টারনেট থেকে ডেটা কল করেছি। একটা XML ফাইল। আমরা একটা মেথড লিখেছি getData নামে, সেটা doInBackground থেকে কল করা হয়েছে। এটিতে মূলত XML ফাইলটা ইন্টারনেট থেকে নিয়ে স্ট্রিং আকারে রিটার্ন করেছে।
onPostExecute মানেহচ্ছে ব্যাক গ্রাউন্ড কাজ শেষ, তাহলে আমরা প্রগ্রেসবারটি হাইড করতে পারি। এবং আমাদের xml ডেটা গুলো দিয়ে যা করতে চাই, তা করতে পারি।
এখানেই শেষ নয়!
আমরা এখন মূলত কিছু xml ডেটা পেয়েছি। এগুলোকে পার্স করতে হবে। তার জন্য এখান থেকে আরকেটা ক্লাসকে কল করেছি। XmlParser.java নামেঃ
import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import org.xmlpull.v1.XmlPullParserFactory; import java.io.IOException; import java.io.InputStream; import java.io.StringReader; import java.util.ArrayList; import java.util.List; public class XmlParser { public static List<Food> parse(String content) { List<Food> foods= new ArrayList<Food>(); Food food = null; String text =""; try { XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); factory.setNamespaceAware(true); XmlPullParser parser = factory.newPullParser(); parser.setInput(new StringReader(content)); int eventType = parser.getEventType(); while (eventType != XmlPullParser.END_DOCUMENT) { String tagName = parser.getName(); switch (eventType) { case XmlPullParser.START_TAG: if (tagName.equalsIgnoreCase("food")) { // create a new instance of food food = new Food(); } break; case XmlPullParser.TEXT: text = parser.getText(); break; case XmlPullParser.END_TAG: if (tagName.equalsIgnoreCase("food")) { // add food object to list foods.add(food); }else if (tagName.equalsIgnoreCase("name")) { food.setName(text); } else if (tagName.equalsIgnoreCase("price")) { food.setPrice(text); } else if (tagName.equalsIgnoreCase("description")) { food.setDescription(text); } break; default: break; } eventType = parser.next(); } } catch (XmlPullParserException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} return foods; } }
এখানে আমরা XML কন্টেন্ট পার্স করেছি। xml নিয়ে কাজ করে থাকলে এটা বুঝতে সুবিধে হবে। এখানে আমরা কিছু খাবারের তথ্য পেয়েছি। Food.java নামে একটা ক্লাস তৈরি করেছি, যেন খাবার আইটেম গুলোর জন্য একটা অ্যারে লিস্ট তৈরি করতে পারি। যা আমরা উপরে XmlParsing ক্লাসে ব্যবহার করেছি।
Food.java
public class Food { // food info variables private String name; private String price; private String description; // getters and setters public String getPrice() { return price; } public void setPrice(String price) { this.price = price; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } }
সব কিছু ঠিক মত করতে পারলে নিচের মত আউটপুট পাবো।
আপনার কাজ হচ্ছে আরো কমপ্লেক্স কোন XML ফাইল নিয়ে কাজ করা। এটা ঠিক মত করতে পারলে যে কোন XML ডেটা নিয়েই কাজ করা যাবে। এটি বেশি কঠিন মনে হলে আগে এ লেখাটা দেখা যেতে পারেঃ XML parsing – অ্যান্ড্রয়েড