UI অবস্থা (ভিউ) সংরক্ষণ করুন

ধারণা এবং জেটপ্যাক কম্পোজ বাস্তবায়ন

এই নির্দেশিকায় UI স্টেট সম্পর্কে ব্যবহারকারীর প্রত্যাশা এবং স্টেট সংরক্ষণের জন্য উপলব্ধ বিকল্পগুলো নিয়ে আলোচনা করা হয়েছে।

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

ব্যবহারকারীর প্রত্যাশা এবং সিস্টেমের আচরণের মধ্যে ব্যবধান পূরণ করতে নিম্নলিখিত পদ্ধতিগুলোর সমন্বয় ব্যবহার করুন:

  • ViewModel অবজেক্টসমূহ।
  • নিম্নলিখিত প্রেক্ষাপটগুলির মধ্যে সংরক্ষিত ইনস্ট্যান্স অবস্থা:
  • অ্যাপ এবং অ্যাক্টিভিটি পরিবর্তনের সময় UI অবস্থা সংরক্ষণ করার জন্য স্থানীয় স্টোরেজ।

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

আপনার অ্যাপটি যেন ব্যবহারকারীদের প্রত্যাশা পূরণ করে এবং একটি দ্রুত ও প্রতিক্রিয়াশীল ইন্টারফেস প্রদান করে, তা নিশ্চিত করুন। UI-তে ডেটা লোড করার সময় বিলম্ব এড়িয়ে চলুন, বিশেষ করে রোটেশনের মতো সাধারণ কনফিগারেশন পরিবর্তনের পরে।

ব্যবহারকারীর প্রত্যাশা এবং সিস্টেমের আচরণ

ব্যবহারকারীর গৃহীত পদক্ষেপের উপর নির্ভর করে, তিনি আশা করেন যে কার্যকলাপের অবস্থা হয় মুছে যাবে অথবা সংরক্ষিত থাকবে। কিছু ক্ষেত্রে সিস্টেম স্বয়ংক্রিয়ভাবে ব্যবহারকারীর প্রত্যাশা অনুযায়ী কাজ করে। অন্য ক্ষেত্রে সিস্টেম তার বিপরীত কাজ করে।

ব্যবহারকারী-প্রবর্তিত UI অবস্থা বরখাস্ত

ব্যবহারকারী আশা করেন যে, যখন তিনি কোনো অ্যাক্টিভিটি শুরু করবেন, তখন সেই অ্যাক্টিভিটির ক্ষণস্থায়ী UI অবস্থাটি ততক্ষণ পর্যন্ত একই থাকবে, যতক্ষণ না তিনি অ্যাক্টিভিটিটি সম্পূর্ণরূপে বন্ধ করে দেন। ব্যবহারকারী নিম্নলিখিত কাজগুলো করে একটি অ্যাক্টিভিটি সম্পূর্ণরূপে বন্ধ করতে পারেন:

  • ওভারভিউ (সাম্প্রতিক) স্ক্রিন থেকে কার্যকলাপটি সোয়াইপ করে সরিয়ে দেওয়া।
  • সেটিংস স্ক্রিন থেকে অ্যাপটি বন্ধ করা বা জোর করে বন্ধ করা।
  • ডিভাইসটি রিবুট করা হচ্ছে।
  • কোনো এক ধরনের "সমাপ্তি" ক্রিয়া সম্পন্ন করা (যা Activity.finish() দ্বারা সমর্থিত)।

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

সম্পূর্ণভাবে বাতিল করার এই নিয়মের কিছু ব্যতিক্রম আছে—উদাহরণস্বরূপ, একজন ব্যবহারকারী আশা করতে পারেন যে ব্যাক বাটন ব্যবহার করে ব্রাউজার থেকে বের হওয়ার আগে তিনি যে ওয়েবপেজটি দেখছিলেন, ব্রাউজারটি তাকে ঠিক সেখানেই নিয়ে যাবে।

সিস্টেম-প্রবর্তিত UI রাষ্ট্রীয় বরখাস্ত

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

উল্লেখ্য, কনফিগারেশন পরিবর্তনের ক্ষেত্রে ডিফল্ট আচরণকে অগ্রাহ্য করা সম্ভব (যদিও এটি সুপারিশ করা হয় না)। আরও বিস্তারিত জানতে ‘কনফিগারেশন পরিবর্তন পরিচালনা’ দেখুন।

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

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

UI অবস্থা সংরক্ষণের বিকল্পগুলি

যখন UI অবস্থা সম্পর্কে ব্যবহারকারীর প্রত্যাশা সিস্টেমের ডিফল্ট আচরণের সাথে মেলে না, তখন আপনাকে অবশ্যই ব্যবহারকারীর UI অবস্থা সংরক্ষণ এবং পুনরুদ্ধার করতে হবে, যাতে সিস্টেম-প্রবর্তিত ধ্বংস প্রক্রিয়াটি ব্যবহারকারীর কাছে অদৃশ্য থাকে।

UI স্টেট সংরক্ষণের প্রতিটি বিকল্প নিম্নলিখিত দিকগুলোর উপর ভিত্তি করে ভিন্ন হয়, যা ব্যবহারকারীর অভিজ্ঞতার উপর প্রভাব ফেলে:

ভিউমডেল

সংরক্ষিত ইনস্ট্যান্স অবস্থা

স্থায়ী সংরক্ষণ

স্টোরেজ অবস্থান

স্মৃতিতে

স্মৃতিতে

ডিস্কে বা নেটওয়ার্কে

কনফিগারেশন পরিবর্তন সত্ত্বেও টিকে থাকে

হ্যাঁ

হ্যাঁ

হ্যাঁ

সিস্টেম-প্রবর্তিত প্রক্রিয়া মৃত্যু থেকে বেঁচে থাকে

না

হ্যাঁ

হ্যাঁ

ব্যবহারকারীর কার্যকলাপ সম্পূর্ণ বাতিল/শেষ করার পরেও টিকে থাকে()

না

না

হ্যাঁ

ডেটার সীমাবদ্ধতা

জটিল অবজেক্টগুলো ঠিক আছে, কিন্তু উপলব্ধ মেমরির কারণে স্থান সীমিত।

শুধুমাত্র প্রিমিটিভ টাইপ এবং String মতো সরল, ছোট অবজেক্টের জন্য

শুধুমাত্র ডিস্কের স্থান অথবা নেটওয়ার্ক রিসোর্স থেকে তথ্য সংগ্রহের খরচ বা সময়ের দ্বারা সীমাবদ্ধ

পড়া/লেখার সময়

দ্রুত (শুধুমাত্র মেমরি অ্যাক্সেস)

ধীর (সিরিয়ালাইজেশন/ডিসেরিয়ালাইজেশন প্রয়োজন)

ধীর (ডিস্ক অ্যাক্সেস বা নেটওয়ার্ক ট্রানজ্যাকশন প্রয়োজন)

কনফিগারেশন পরিবর্তনগুলি পরিচালনা করতে ViewModel ব্যবহার করুন

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

ViewModel ডেটা মেমরিতে ধরে রাখে, যার মানে হলো ডিস্ক বা নেটওয়ার্ক থেকে ডেটা পুনরুদ্ধার করার চেয়ে এটি কম ব্যয়বহুল। একটি ViewModel একটি অ্যাক্টিভিটি (বা অন্য কোনো লাইফসাইকেল ওনার)-এর সাথে যুক্ত থাকে — এটি কনফিগারেশন পরিবর্তনের সময়ও মেমরিতে থেকে যায় এবং সিস্টেম স্বয়ংক্রিয়ভাবে ViewModel-টিকে সেই কনফিগারেশন পরিবর্তনের ফলে তৈরি হওয়া নতুন অ্যাক্টিভিটি ইনস্ট্যান্সের সাথে যুক্ত করে দেয়।

যখন আপনার ব্যবহারকারী আপনার অ্যাক্টিভিটি বা ফ্র্যাগমেন্ট থেকে বেরিয়ে যান অথবা আপনি finish() কল করেন, তখন সিস্টেম দ্বারা ViewModel-গুলি স্বয়ংক্রিয়ভাবে ধ্বংস হয়ে যায়, যার অর্থ হলো এই পরিস্থিতিগুলিতে ব্যবহারকারীর প্রত্যাশা অনুযায়ী স্টেট পরিষ্কার হয়ে যায়।

সেভড ইনস্ট্যান্স স্টেটের (saved instance state) বিপরীতে, সিস্টেম-প্রবর্তিত প্রসেস বন্ধ হয়ে গেলে ভিউমডেল (ViewModel) ধ্বংস হয়ে যায়। সিস্টেম-প্রবর্তিত প্রসেস বন্ধ হওয়ার পর একটি ভিউমডেলের ডেটা পুনরায় লোড করতে, SavedStateHandle API ব্যবহার করুন। বিকল্পভাবে, যদি ডেটা UI-এর সাথে সম্পর্কিত হয় এবং ভিউমডেলে ধরে রাখার প্রয়োজন না হয়, তাহলে onSaveInstanceState() ব্যবহার করুন। যদি ডেটাটি অ্যাপ্লিকেশন ডেটা হয়, তবে এটিকে ডিস্কে সংরক্ষণ করা আরও ভালো হতে পারে।

কনফিগারেশন পরিবর্তনের পরেও আপনার UI স্টেট সংরক্ষণের জন্য যদি আগে থেকেই কোনো ইন-মেমরি সমাধান থাকে, তাহলে আপনার ViewModel ব্যবহার করার প্রয়োজন নাও হতে পারে।

সিস্টেম-জনিত প্রসেস মৃত্যু সামাল দেওয়ার জন্য ব্যাকআপ হিসেবে সংরক্ষিত ইনস্ট্যান্স স্টেট ব্যবহার করুন।

ভিউ সিস্টেমের onSaveInstanceState() কলব্যাক এবং ভিউমডেলের SavedStateHandle এ এমন ডেটা সংরক্ষণ করা হয় যা কোনো UI কন্ট্রোলারের (যেমন অ্যাক্টিভিটি বা ফ্র্যাগমেন্ট) স্টেট পুনরায় লোড করার জন্য প্রয়োজন হয়, যদি সিস্টেম সেই কন্ট্রোলারটিকে ধ্বংস করে এবং পরে আবার তৈরি করে। onSaveInstanceState ব্যবহার করে কীভাবে সেভড ইনস্ট্যান্স স্টেট প্রয়োগ করতে হয় তা জানতে, অ্যাক্টিভিটি লাইফসাইকেল গাইডের " অ্যাক্টিভিটি স্টেট সংরক্ষণ এবং পুনরুদ্ধার" অংশটি দেখুন।

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

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

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

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

এই উভয় পরিস্থিতিতেই, কনফিগারেশন পরিবর্তনের সময় ডাটাবেস থেকে ডেটা পুনরায় লোড করে সময় নষ্ট করা এড়াতে আপনার একটি ViewModel ব্যবহার করা উচিত।

যেসব ক্ষেত্রে সংরক্ষণের জন্য UI ডেটা সহজ এবং হালকা হয়, সেখানে আপনি আপনার স্টেট ডেটা সংরক্ষণের জন্য শুধুমাত্র সেভড ইনস্ট্যান্স স্টেট API ব্যবহার করতে পারেন।

SavedStateRegistry ব্যবহার করে সংরক্ষিত অবস্থায় প্রবেশ করুন

Fragment 1.1.0 অথবা এর ট্রানজিটিভ ডিপেন্ডেন্সি Activity 1.0.0 থেকে শুরু করে, UI কন্ট্রোলার, যেমন একটি Activity বা Fragment , SavedStateRegistryOwner ইমপ্লিমেন্ট করে এবং একটি SavedStateRegistry প্রদান করে যা সেই কন্ট্রোলারের সাথে বাইন্ড করা থাকে। SavedStateRegistry কম্পোনেন্টগুলোকে আপনার UI কন্ট্রোলারের সেভড স্টেটে হুক করার সুযোগ দেয়, যাতে তারা তা ব্যবহার করতে বা তাতে অবদান রাখতে পারে। উদাহরণস্বরূপ, ViewModel-এর জন্য সেভড স্টেট মডিউলটি একটি SavedStateHandle তৈরি করতে এবং আপনার ViewModel অবজেক্টগুলোতে তা সরবরাহ করতে SavedStateRegistry ব্যবহার করে। আপনি আপনার UI কন্ট্রোলারের ভেতর থেকে getSavedStateRegistry কল করে SavedStateRegistry টি পুনরুদ্ধার করতে পারেন।

যেসব কম্পোনেন্ট সংরক্ষিত স্টেটে অবদান রাখে, সেগুলোকে অবশ্যই SavedStateRegistry.SavedStateProvider ইমপ্লিমেন্ট করতে হবে, যা saveState নামে একটিমাত্র মেথড সংজ্ঞায়িত করে। saveState() মেথডটি আপনার কম্পোনেন্টকে একটি Bundle রিটার্ন করার সুযোগ দেয়, যাতে সেই কম্পোনেন্ট থেকে সংরক্ষণযোগ্য যেকোনো স্টেট অন্তর্ভুক্ত থাকে। UI কন্ট্রোলারের লাইফসাইকেলের স্টেট সংরক্ষণ পর্যায়ে SavedStateRegistry এই মেথডটিকে কল করে।

class SearchManager implements SavedStateRegistry.SavedStateProvider {
    private static String QUERY = "query";
    private String query = null;
    ...

    @NonNull
    @Override
    public Bundle saveState() {
        Bundle bundle = new Bundle();
        bundle.putString(QUERY, query);
        return bundle;
    }
}

একটি SavedStateProvider রেজিস্টার করতে, SavedStateRegistry তে registerSavedStateProvider() কল করুন এবং প্রোভাইডারের ডেটার সাথে যুক্ত করার জন্য একটি কী (key) পাস করুন। প্রোভাইডারের ডেটার সাথে যুক্ত কী-টি পাস করে SavedStateRegistry তে consumeRestoredStateForKey() কল করার মাধ্যমে সেভড স্টেট থেকে প্রোভাইডারের পূর্বে সংরক্ষিত ডেটা পুনরুদ্ধার করা যায়।

একটি Activity বা Fragment মধ্যে, আপনি super.onCreate() কল করার পর onCreate() ফাংশনে একটি SavedStateProvider রেজিস্টার করতে পারেন। বিকল্পভাবে, আপনি একটি SavedStateRegistryOwner উপর একটি LifecycleObserver সেট করতে পারেন, যা LifecycleOwner ইমপ্লিমেন্ট করে, এবং ON_CREATE ইভেন্টটি ঘটলে SavedStateProvider টি রেজিস্টার করতে পারেন। একটি LifecycleObserver ব্যবহার করে, আপনি পূর্বে সংরক্ষিত স্টেটের রেজিস্ট্রেশন এবং পুনরুদ্ধারকে SavedStateRegistryOwner নিজস্ব প্রক্রিয়া থেকে পৃথক করতে পারেন।

কোটলিন

class SearchManager(registryOwner: SavedStateRegistryOwner) : SavedStateRegistry.SavedStateProvider {
    companion object {
        private const val PROVIDER = "search_manager"
        private const val QUERY = "query"
    }

    private val query: String? = null

    init {
        // Register a LifecycleObserver for when the Lifecycle hits ON_CREATE
        registryOwner.lifecycle.addObserver(LifecycleEventObserver { _, event ->
            if (event == Lifecycle.Event.ON_CREATE) {
                val registry = registryOwner.savedStateRegistry

                // Register this object for future calls to saveState()
                registry.registerSavedStateProvider(PROVIDER, this)

                // Get the previously saved state and restore it
                val state = registry.consumeRestoredStateForKey(PROVIDER)

                // Apply the previously saved state
                query = state?.getString(QUERY)
            }
        }
    }

    override fun saveState(): Bundle {
        return bundleOf(QUERY to query)
    }

    ...
}

class SearchFragment : Fragment() {
    private var searchManager = SearchManager(this)
    ...
}

জাভা

class SearchManager implements SavedStateRegistry.SavedStateProvider {
    private static String PROVIDER = "search_manager";
    private static String QUERY = "query";
    private String query = null;

    public SearchManager(SavedStateRegistryOwner registryOwner) {
        registryOwner.getLifecycle().addObserver((LifecycleEventObserver) (source, event) -> {
            if (event == Lifecycle.Event.ON_CREATE) {
                SavedStateRegistry registry = registryOwner.getSavedStateRegistry();

                // Register this object for future calls to saveState()
                registry.registerSavedStateProvider(PROVIDER, this);

                // Get the previously saved state and restore it
                Bundle state = registry.consumeRestoredStateForKey(PROVIDER);

                // Apply the previously saved state
                if (state != null) {
                    query = state.getString(QUERY);
                }
            }
        });
    }

    @NonNull
    @Override
    public Bundle saveState() {
        Bundle bundle = new Bundle();
        bundle.putString(QUERY, query);
        return bundle;
    }

    ...
}

class SearchFragment extends Fragment {
    private SearchManager searchManager = new SearchManager(this);
    ...
}

জটিল বা বৃহৎ ডেটার ক্ষেত্রে প্রসেস ডেথ সামাল দিতে লোকাল পারসিস্টেন্স ব্যবহার করুন।

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

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

UI স্টেট পরিচালনা: ভাগ করে জয় করা

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

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

উদাহরণস্বরূপ, এমন একটি কার্যকলাপের কথা ভাবুন যা আপনাকে আপনার গানের লাইব্রেরিতে অনুসন্ধান করতে দেয়। বিভিন্ন ইভেন্ট যেভাবে পরিচালনা করা উচিত তা নিচে দেওয়া হলো:

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

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

যখন অ্যাক্টিভিটি ব্যাকগ্রাউন্ডে চলে যায় এবং সিস্টেম সেভড ইনস্ট্যান্স স্টেট এপিআই (API) কল করে, তখন সার্চ কোয়েরিটি সেভড ইনস্ট্যান্স স্টেটে সংরক্ষণ করা উচিত, যাতে প্রসেসটি পুনরায় তৈরি হলে তা ব্যবহার করা যায়। যেহেতু এতে সংরক্ষিত অ্যাপ্লিকেশন ডেটা লোড করার জন্য এই তথ্য প্রয়োজন, তাই সার্চ কোয়েরিটি ViewModel-এর SavedStateHandle এ সংরক্ষণ করুন। ডেটা লোড করতে এবং UI-কে তার বর্তমান অবস্থায় ফিরিয়ে আনতে এইটুকুই যথেষ্ট।

জটিল অবস্থা পুনরুদ্ধার: খণ্ডগুলো পুনরায় একত্রিত করা

যখন ব্যবহারকারীর অ্যাক্টিভিটিতে ফিরে আসার সময় হয়, তখন অ্যাক্টিভিটিটি পুনরায় তৈরি করার দুটি সম্ভাব্য পরিস্থিতি রয়েছে:

  • সিস্টেম দ্বারা বন্ধ করার পর অ্যাক্টিভিটিটি পুনরায় তৈরি করা হয়। সিস্টেমটি কোয়েরিটি একটি সেভড ইনস্ট্যান্স স্টেট বান্ডেলে সংরক্ষণ করে রাখে, এবং যদি SavedStateHandle ব্যবহার করা না হয়, তবে UI-এর উচিত কোয়েরিটি ViewModel এ প্রেরণ করা। ViewModel দেখে যে তার কাছে কোনো সার্চ রেজাল্ট ক্যাশ করা নেই এবং প্রদত্ত সার্চ কোয়েরি ব্যবহার করে সার্চ রেজাল্ট লোড করার দায়িত্ব অর্পণ করে।
  • কনফিগারেশন পরিবর্তনের পর অ্যাক্টিভিটিটি পুনরায় তৈরি করা হয়। যেহেতু ViewModel ইনস্ট্যান্সটি নষ্ট হয়ে যায়নি, তাই ViewModel মেমরিতে সমস্ত তথ্য ক্যাশ করা থাকে এবং এটিকে পুনরায় ডাটাবেস কোয়েরি করার প্রয়োজন হয় না।

অতিরিক্ত সম্পদ

UI স্টেট সংরক্ষণ সম্পর্কে আরও জানতে, নিম্নলিখিত রিসোর্সগুলো দেখুন।

ব্লগ

কোডল্যাবস