পোস্টটি পড়া হয়েছে 4,737 বার
android sharedpreferences bengali tutorial

Android SharedPreferences: অ্যাপে Key-Value টাইপ ডেটা স্টোর করার উপায়

Post updated on 25th September, 2018 at 03:17 pm

আমাদের অ্যাপে অনেক সময় ছোটখাটো কিছু ডেটা স্টোর করে রাখতে হয়। যেমন user name, user email, session token ইত্যাদি। এসব ডেটার পরিমান এমন নয় যে ডেটাবেজ ইউজ করতে হবে। আবার একটা অ্যাপে অনেকগুলো user name, user email, session token রাখতে হবে তেমনটাও কিন্তু না। তাই এ ধরনের ডেটা অ্যাপে স্টোর করার জন্য ডেটাবেজ ইউজ না করে SharedPreferences ইউজ করা হয়। Map বা HashMap ইউজ করে যেমন KEY-VALUE pair হিসাবে ডেটা রাখতে পারি, SharedPreferences-ও এই রকম একটা ব্যবস্থা। অ্যাপ বন্ধ করে দিলে বা recent list থেকে kill করলেও এই ডেটাগুলো ডিভাইসে স্টোর হয়ে থাকবে।

SharedPreferences পরে হবে। আসেন আপাতত একটু অন্য দিকের গল্পগুজব করি।

Android Studio তে একটা Hello World প্রোজেক্ট খুললেও দেখা যাবে res > values > string.xml তৈরি হয়েছে। এই string.xml এর ভিতরে প্রাথমিক অবস্থায় দেখা যাবে “<string name=”app_name”>My Application</string>”. এই XML ফাইলটা দিয়ে আমরা কী করি? আপনি নিশ্চয়ই জেনে থাকবেন এখানে আমরা অ্যাপের বিভিন্ন জায়গায় ব্যবহার হবে এমন predefined কিছু স্ট্রিং ভ্যালু রাখি। প্রোজেক্ট ওপেন করার পর Android Studio আমাদের জন্য “app_name” এই key দিয়ে এরপরে আমাদের প্রোজেক্ট বা অ্যাপের নামটা বসিয়ে দেয়। পরবর্তীতে আমরা অ্যাপ ডেভেলপ করার সময় যখন কোনো বাটনে একটা টেক্সট দেয়ার দরকার হয় তখন Layout এর XML এ বাটনের টেক্সট না লিখে এই string.xml ফাইলে লিখিঃ <string name=”button_text”>Submit</string>. আর Button এর attribute হিসাবে android:text=”Submit” না লিখে লিখে থাকি android:text=”@string/button_text”. আপনি যদি আপনার text-গুলোকে string.xml ফাইলে না রেখে hardcode করে লেআউটের XML এর মধ্যেই রেখে দেন তাহলে Android Studio warning দিবে।

এই string.xml ফাইলটা আপনার পরিচিত। তাই এটা নিয়ে আপনার জানা কথাগুলোকেই আরেকবার রিপিট করলাম। এখানে একটা টেক্সট অ্যাড করার জন্য টেক্সটের একটা নাম দিতে হয়। এরপর একটা টেক্সট লিখতে হয়। যেই নামটা দিচ্ছি সেটা হচ্ছে KEY আর টেক্সটটা হচ্ছে value. অর্থাৎ string.xml ফাইলে KEY-VALUE PAIR হিসাবে কিছু স্ট্রিং ডেটা রাখা হয়। অ্যাপ বিল্ড করে ফেলার পর কিন্তু এই ফাইলে আর ডেটা রাইট করা যায় না। অ্যাপ ডেভেলপের সময়েই যা যা স্ট্রিং দরকার সেগুলোকে লিখে রাখতে হয়।

আচ্ছা!!! অ্যাপ রান করার সময় যদি এই ধরনের কোনো একটা সিসটেম থাকত যে ইউজার চাইলে কিছু ডেটা এই string.xml এ স্টোর করে রাখবে! ধরা যাক ইউজারকে একটা ফিল্ড দিলাম তার নাম লিখার জন্য। সে নাম লিখে সেভ বাটনে ক্লিক করলে একটা XML ফাইলে <string name=”user_name”>John Doe</string> এভাবে তার নামটা স্টোর হয়ে যাবে। অ্যাপের যে কোনো জায়গা থেকে string.xml এর ডেটা যেভাবে KEY এর মাধ্যমে access করা যায়, ইউজারের নামও সেভাবে access করা যাবে। তাহলে ভাল হত না সিসটেমটা???

এই টাইপের একটা কাজই করে SharedPreferences নামক interface-টি!

Android SharedPreferences at a glance

SharedPreferences কোনো class নয়। এটা Android এর জন্য বিশেষ ভাবে তৈরি করা একটা interface.

android.content.SharedPreferences এ গেলে পাওয়া যাবে বেশ কিছু প্রয়োজনীয় method signature. SharedPreferences এ data read-write করার জন্য যেই যেই মেথডগুলো দরকার সেগুলোর সিগনেচার এখানে দেয়া আছে। সত্যিকারের read-write করার কাজগুলো বা মেথডের বডিগুলো কোথায় লেখা আছে আপাতত জানি না। SharedPreferences নিয়ে কাজ করার জন্য অবশ্য সেগুলো এই মুহূর্তে জানাও জরুরি না। Implementation এর পার্টটুকু পুরো সেপারেট করে আমাদের কাছে অ্যাবস্ট্রাক্ট রাখা হয়েছে। যেন কাজের সময় অত গভীরে গিয়ে চিন্তা করা না লাগে।

আমরা যখন SharedPreferences নিয়ে কাজ করা শুরু করব তখন ফোনের একটা প্রাইভেট ডিরেক্টরিতে একটা XML ফাইল তৈরি হবে। দারুণ ব্যাপার হলো শুধু স্ট্রিংই না, এই XML ফাইলে আমরা চাইলে যে কোনো primitive type (int, float, String, boolean etc.) এর data স্টোর করতে পারব। আমাদের নিজেদের ডিফাইন করা কোনো অবজেক্ট সেখানে রাখা যাবে না।

দুই ধরনের SharedPreferences ইউজ করা যায়। একটা হচ্ছে default, আরেকটা আমাদের তৈরি করা। ডিফল্ট SharedPreferences এর জন্য নতুন কোনো XML file তৈরি হয় না। অপরটির ক্ষেত্রে আমাদের provide করা একটা নামের সাথে সামঞ্জস্য রেখে নতুন XML file তৈরি হয়।

.
<!--this location for your defined SharedPreferences-->
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PREFS_NAME.xml

<!--default one will be stored here-->
/data/data/YOUR_PACKAGE_NAME/shared_prefs/YOUR_PACKAGE_NAME_preferences.xml

উল্লেখ্য, /data/data/<package_name> এ access করার জন্য superuser privileges লাগবে।

SharedPreferences Implementation (Naive Approach)

ধরা যাক, আমরা চাই ‘my_shared_preferences.xml’ নামের একটা ফাইল তৈরি হোক। তাতে user name, age আর ইউজার student কিনা সেই তথ্য স্টোর করব। এজন্য Activity class এর onCreate() মেথডের ভিতরে নিচের কোডটুকুই যথেষ্ট!

SharedPreferences sharedPreferences = getSharedPreferences("my_shared_preferences", MODE_PRIVATE); //MODE_PRIVATE = 0
SharedPreferences.Editor editor = sharedPreferences.edit();

editor.putString("user_name", name); //String name = "John Doe";
editor.putBoolean("is_student", isStudent); //boolean isStudent = true;
editor.putInt("age", age); //int age = 20;

editor.apply();

getSharedPreferences() আমাদের ডিফাইন করা কোনো মেথড নয়। এটা Context নামক আমাদের সুপরিচিত abstract class এর একটা abstract method. এই মেথডের প্যারামিটার দুইটি। প্রথমটি দেখেই বুঝা যাচ্ছে SharedPreferences এর নাম। আর দ্বিতীয় প্যারামিটার হচ্ছে accessible mode. Context class এর ভিতরে public static final int হিসাবে এর মান দেয়া আছে 0. এই প্যারামিটারে MODE_PRIVATE না লিখে জাস্ট 0 লিখলেও কাজ করবে।

এই প্রাইভেট মোডের মানে হচ্ছে এই SharedPreferences এর ডেটাগুলো এই অ্যাপ ব্যতীত অন্য কোনো অ্যাপ থেকে অ্যাক্সেস করা যাবে না। অ্যাপের ডেটার সিকিউরিটির জন্য অ্যাপের রিকোয়ার্মেন্ট অনুসারে আমরা বেশির ভাগ ক্ষেত্রে এটাকে প্রাইভেটই রাখি। যদি এই অ্যাপে প্রথম প্যারামিটারে পাঠানো নামের কোনো ফাইল থাকে তাহলে সেই SharedPreferences এর instance return করা হবে। কিন্তু যদি এই নামের কোনো SharedPreferences না থাকে তাহলে এই নামের একটা SharedPreferences তৈরি করবে।

SharedPreferences interface এর ভিতরে Editor নামের আরেকটা interface রয়েছে। এর ভিতরে রয়েছে কিছু মেথড সিগনেচার, যেগুলোর মাধ্যমে actually আমরা ডেটা স্টোর করব। editor.putString(), editor.putBoolean() ও editor.putInt() মেথড কল করে আমরা ডেটাগুলো ফাইলে রাখলাম। আর সর্বশেষ লাইনে editor.apply() কল করে বলা যেতে পারে XML ফাইলটাকে save করলাম।

apply() এর return type হচ্ছে void. তাই এটি কোনো কিছু রিটার্ন করে না। কিন্তু ডেটাগুলো ফাইলে ঠিকঠাক মত  সেভ হয়েছে কিনা সেটা যদি কনফার্ম হতে চান তাহলে editor.commit() মেথডটা কল করে ডেটা সেভ করতে পারেন। commit() এর রিটার্ন টাইপ boolean. ডেটা successfully স্টোর হলে true, অন্যথায় false return করবে।

এ তো গেল ডেটা স্টোর করার কাজ। স্টোর হওয়া ডেটাগুলো যদি retrieve করতে চাই তাহলে? খুব সিম্পল!

.
sharedPreferences.getString("user_name", "Name not found");
sharedPreferences.getBoolean("is_student", false);
sharedPreferences.getInt("age", -1);

যেই key-গুলো দিয়ে ডেটা স্টোর করা হয়েছিল ঠিক সেই key-গুলো দিয়েই data retrieve করতে হবে। উপরের মেথডগুলো দ্বিতীয় প্যারামিটারে default value রাখা হয়েছে। অর্থাৎ যদি সংশ্লিষ্ট Key এর কোনো ভ্যালু না পাওয়া যায় তখন এই ডিফল্ট ভ্যালুগুলো রিটার্ন হবে। যদি user_name সেট করার আগেই এর ভ্যালু get করার জন্য মেথড কল করা হয় তাহলে “Name not found” এই স্ট্রিংটা রিটার্ন হবে।

SharedPreferences Implementation (Better Approach)

কোড করার সুবিধার্থে উপরের সিসটেমে কোড করা যেতেই পারে। কিন্তু ফার্স্ট টাইম একটা অ্যাপ বানানো যত না কষ্টের তার চেয়ে হাজার গুণ বেশি কষ্ট ঐ অ্যাপের রেগুলার মেইনটেনেন্স! তাই জুনিয়রদেরকে বলি আগের করা অ্যাপে নতুন ফিচার অ্যাড করতে বা কিছু মডিফাই করতে। তাহলেই বুঝা যাবে আগের কোডগুলো কতটা জাতের কোড হয়েছে!

উপরের মত করে পুরো অ্যাপের ৮-১০ জায়গায় এবাবে ডেটা স্টোর করা বা retrieve করতে গেলে কোনো না কোনো জায়গায় অবশ্যই ঝামেলা হবে। কোথাও হয়ত Key এর নাম ভুল হবে, কোথাও হয়ত দেখা যাবে user_name KEY দিয়ে ডেটা সেভ করা হয়েছে userEmail ইত্যাদি। এরকম messed up অবস্থা এড়ানোর জন্য উপরের কোডটুকুই একটু মানুষ করে নিচের চেহারা দেয়া হয়েছেঃ

public class MyPreferences {

    private static MyPreferences myPreferences;
    private static SharedPreferences sharedPreferences;
    private static SharedPreferences.Editor editor;

    private MyPreferences(Context context) {
        sharedPreferences = context.getSharedPreferences(Config.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
        editor = sharedPreferences.edit();
        editor.apply();
    }

    public static MyPreferences getPreferences(Context context) {
        if (myPreferences == null) myPreferences = new MyPreferences(context);
        return myPreferences;
    }

    public void setUserName(String userName){
        editor.putString(Config.USER_NAME, userName);
        editor.apply();
    }

    public String getUserName(){
        //if no data is available for Config.USER_NAME then this getString() method returns
        //a default value that is mentioned in second parameter
        return sharedPreferences.getString(Config.USER_NAME, "Name not found");
    }

    public void setAge(int age){
        editor.putInt(Config.AGE, age);
        editor.apply();
    }

    public int getAge(){
        return sharedPreferences.getInt(Config.AGE, -1); //if user's age not found then it'll return -1
    }

    public void setStudentFlag(boolean isStudent){
        editor.putBoolean(Config.IS_STUDENT, isStudent);
        editor.apply();
    }

    public boolean isStudent(){
        return sharedPreferences.getBoolean(Config.IS_STUDENT, false); //assume the default value is false
    }

}

MyPreferences class এর ভিতর MyPreferences এর একটা অবজেক্ট রাখা হয়েছে। Constructor রাখা হয়েছে private. যেন সরাসরি অন্য কোনো ক্লাস থেকে এই ক্লাসের অবজেক্ট তৈরি না করা যায়। Singleton design pattern ফলো করা হয়েছে এখানে। কোনো Activity বা যে কোনো ক্লাসের যদি SharedPreferences এ কোনো ডেটা রিড-রাইট করার জন্য MyPreferences এর অবজেক্ট দরকার হয় তাহলে MyPreferences.getPreferences() মেথড কল করতে হবে। এই মেথড চেক করে দেখবে এই ক্লাসের কোনো অবজেক্ট অলরেডি তৈরি হয়ে আছে কিনা। যদি না থাকে (myPreferences == null) তাহলে private constructor call করে একটা অবজেক্ট বানিয়ে রিটার্ন করবে। আর যদি অলরেডি কোনো অবজেক্ট বানানো থাকে তাহলে সেটাকে রিটার্ন করবে।

প্রতিটা ক্ষেত্রে KEY হিসাবে Config.USER_NAME, Config.AGE ইত্যাদি দেখা যাচ্ছে। সরাসরি এখানে string ইউজ না করে Config নামের একটা ক্লাস লিখেছি। সেখানে USER_NAME = “user_name”, AGE = “age” এভাবে ভ্যালুগুলো রাখা হয়েছে। এতে সুবিধা হবে যে যদি কোনো কারণে কোনো একটা KEY পরিবর্তন করতে হয় তাহলে Config ফাইলে গিয়ে এক জায়গায় এডিট করলেই পুরো অ্যাপে কাজ হয়ে যাবে। কোথাও আর ঐ KEY এর জন্য ঝামেলায় পড়তে হবে না। এছাড়াও বারবার string ভ্যালু লিখতে গেলে টাইপিং মিসটেক হবার চান্স থাকে। আলাদা ফাইলে রাখতে সেই চান্স কমে যায়।

এখন এই ক্লাসের অবজেক্ট নিয়ে আসল কাজ করার পালা। Activity এর ভিতরে MyPreferences এর একটা অবজেক্ট বানাবো। আর ১ লাইনের মেথড কল করে কাজকর্ম যা করার করবো!

MyPreferences myPreferences = MyPreferences.getPreferences(this);

myPreferences.setUserName(name); //String name = "John Doe";
myPreferences.setStudentFlag(studentFlag); //boolean studentFlag = true;
myPreferences.setAge(age); //int age = 20;

SharedPreferences থেকে ডেটা নেয়ার জন্যও এক লাইন করে কোডঃ

.
String name = myPreferences.getUserName();
boolean status = myPreferences.isStudent();
int age = myPreferences.getAge();

অনেক ইজি হয়ে গেল না ব্যাপারটা??? যদি ১০টা ক্লাসে SharedPreferences এর ডেটা নিয়ে কাজ করার দরকার হয় তাহলে সেখানে শুধু MyPreferences এর একটা অবজেক্ট নিতে হবে। এরপর জাস্ট getter-setter use করা! কিন্তু naive approach এ কাজ করলে ১০ টা ক্লাসেই SharedPreferences, SharedPreferences.Editor, apply() ইত্যাদি ইত্যাদি একই কাজ বারবার করা লাগতো! এর চেয়ে ভালো একটু ঝামেলা করে হলেও একবারে একটা আলাদা ক্লাস লিখে ফেলা। এরপর শুধু মেথড কল করে কাজ করা!

এই প্যারা লিখা শুরু করার আগেই দেখলাম পোস্টের সাইজ হয়ে গেছে ১৫০০ শব্দ!!! SharedPreferences এর উপর এত বড় আর্টিকেল হওয়া সম্ভব এটা জানা ছিল না! একদম সিম্পল আর ছোট্ট একটা টপিক কভার করতে অনেক অনেক বেশি কথা লিখে ফেলেছি। আপনার অনেকগুলো সময় নিয়ে ফেলার জন্য দুঃখিত। আপনার যে কোনো মতামত ও পরামর্শ একান্ত কাম্য।

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

9 thoughts on “Android SharedPreferences: অ্যাপে Key-Value টাইপ ডেটা স্টোর করার উপায়

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

  1. Very useful article about android Sharedpreferences. My be need to one correction in the line “যদি SharedPreferences এ কোনো ডেটা রিড-রাইট করার জন্য MyPreferences এর অবজেক্ট দরকার হয় তাহলে MyPreferences.getInstance() মেথড কল করতে হবে।”
    It will be “MyPreferences.getPreferences() মেথড কল করতে হবে।”

Leave a Reply

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