আপনার স্টেট কোথায় হোস্ট করা হয়েছে এবং প্রয়োজনীয় লজিকের উপর নির্ভর করে, আপনার UI স্টেট সেভ ও রিস্টোর করতে, আপনি বিভিন্ন API ব্যবহার করতে পারেন। প্রতিটি অ্যাপ এটি সবচেয়ে ভালোভাবে করার জন্য একাধিক API-এর কম্বিনেশন ব্যবহার করে।
অ্যাক্টিভিটি বা প্রসেস রিক্রিয়েশনের কারণে যেকোনও Android অ্যাপ তার UI স্টেট হারাতে পারে। নিম্নলিখিত ইভেন্টের কারণে স্ট্যাটাস সংক্রান্ত তথ্য মুছে যেতে পারে:
- কনফিগারেশনে পরিবর্তন। কনফিগারেশন পরিবর্তন ম্যানুয়ালি ম্যানেজ না করা হলে, অ্যাক্টিভিটি নষ্ট হয়ে যায় এবং আবার তৈরি হয়।
- সিস্টেমের মাধ্যমে শুরু করা প্রসেস বন্ধ করা। অ্যাপটি ব্যাকগ্রাউন্ডে চলছে এবং ডিভাইসটি অন্যান্য প্রসেসের জন্য রিসোর্স (যেমন মেমরি) খালি করে।
ব্যবহারকারীর ইতিবাচক অভিজ্ঞতা বজায় রাখার জন্য এইসব ইভেন্টের পরে স্টেট সংরক্ষণ করা প্রয়োজনীয়। কোন স্টেটটি বজায় রাখতে হবে তা বেছে নেওয়া আপনার অ্যাপের অনন্য ব্যবহারকারী ফ্লোর উপর নির্ভর করে। পেশাদার পদ্ধতি হিসেবে, আপনাকে অন্তত ব্যবহারকারীর ইনপুট এবং নেভিগেশন-সম্পর্কিত স্টেট প্রিজার্ভ করতে হবে। এর উদাহরণের মধ্যে রয়েছে, তালিকার স্ক্রল পজিশন, ব্যবহারকারী যে আইটেম সম্পর্কে আরও জানতে চান সেটির আইডি, ব্যবহারকারীর পছন্দের প্রসেসে থাকা বাছাই বা টেক্সট ফিল্ডে ইনপুট।
আপনার স্টেট কোথায় হোস্ট করা হয়েছে এবং যে লজিকের এটি প্রয়োজন তার উপর নির্ভর করে UI স্টেট স্টোর করার জন্য উপলভ্য API-এর সারসংক্ষেপ এই পৃষ্ঠায় দেওয়া হয়েছে।
UI লজিক
আপনার স্টেট যদি UI-তে হোস্ট করা থাকে, কম্পোজেবল ফাংশনে অথবা কম্পোজিশনের স্কোপে থাকা প্লেন
স্টেট হোল্ডার ক্লাসে, তাহলে আপনি অ্যাক্টিভিটি এবং প্রসেস রিক্রিয়েশন জুড়ে স্টেট ধরে রাখতে
rememberSaveable ব্যবহার করতে পারেন।
নিম্নলিখিত স্নিপেটে, একটি বুলিয়ান UI এলিমেন্ট স্টেট স্টোর করতে rememberSaveable ব্যবহার করা হয়েছে:
@Composable fun ChatBubble( message: Message ) { var showDetails by rememberSaveable { mutableStateOf(false) } ClickableText( text = AnnotatedString(message.content), onClick = { showDetails = !showDetails } ) if (showDetails) { Text(message.timestamp) } }
showDetails হল একটি বুলিয়ান ভেরিয়েবল যা চ্যাট বাবলটি ছোট বা বড় করা হয়েছে কিনা তা স্টোর করে।
rememberSaveable সেভ করা ইনস্ট্যান্স স্টেট মেকানিজমের মাধ্যমে Bundle-এ UI এলিমেন্ট স্টেট স্টোর করে।
এটি বান্ডেলে অটোমেটিক প্রিমিটিভ টাইপ স্টোর করতে পারে। আপনার স্টেট
যদি এমন কোনও টাইপে থাকে যা প্রিমিটিভ নয়, যেমন ডেটা ক্লাস, তাহলে আপনি
বিভিন্ন স্টোরিং মেকানিজম ব্যবহার করতে পারেন, যেমন Parcelize অ্যানোটেশন ব্যবহার করা,
listSaver এবং mapSaver-এর মতো Compose API ব্যবহার করা অথবা Compose রানটাইম Saver ক্লাস এক্সটেন্ড করে
কাস্টম সেভার ক্লাস প্রয়োগ করা। এইসব পদ্ধতি সম্পর্কে আরও জানতে স্টেট স্টোর করার
পদ্ধতি ডকুমেন্টেশন দেখুন।
নিম্নলিখিত স্নিপেটে, rememberLazyListState Compose
API LazyListState স্টোর করে, যার মধ্যে rememberSaveable ব্যবহার করে
LazyColumn বা LazyRow-এর স্ক্রল স্টেট থাকে। এটি একটি
LazyListState.Saver ব্যবহার করে, যা একটি কাস্টম সেভার যা স্ক্রোল স্টেট
স্টোর এবং রিস্টোর করতে সক্ষম। কোনও অ্যাক্টিভিটি বা প্রসেস রিক্রিয়েশনের পরে (যেমন, ডিভাইস ওরিয়েন্টেশন পরিবর্তন করার মতো কনফিগারেশন পরিবর্তনের পরে), স্ক্রল স্টেট সংরক্ষিত থাকে।
@Composable fun rememberLazyListState( initialFirstVisibleItemIndex: Int = 0, initialFirstVisibleItemScrollOffset: Int = 0 ): LazyListState { return rememberSaveable(saver = LazyListState.Saver) { LazyListState( initialFirstVisibleItemIndex, initialFirstVisibleItemScrollOffset ) } }
পেশাদার পদ্ধতি
rememberSaveable UI স্টেট স্টোর করতে Bundle ব্যবহার করে, যা অন্যান্য API-এর মাধ্যমে শেয়ার করা হয়,
যেগুলি এতে লেখে, যেমন আপনার অ্যাক্টিভিটিতে
onSaveInstanceState() কল। তবে, এই Bundle-এর সাইজ সীমিত এবং বড় অবজেক্ট স্টোর করলে
রানটাইমে TransactionTooLarge ব্যতিক্রম হতে পারে। এটি
বিশেষ করে একটি Activity অ্যাপের ক্ষেত্রে সমস্যাজনক হতে পারে যেখানে একই
Bundle পুরো অ্যাপ জুড়ে ব্যবহার করা হচ্ছে।
এই ধরনের ক্র্যাশ এড়াতে, আপনাকে বান্ডেলে বড় জটিল অবজেক্ট বা অবজেক্টের তালিকা স্টোর করা উচিত নয়।
পরিবর্তে, আইডি বা কী-এর মতো প্রয়োজনীয় ন্যূনতম স্টেট স্টোর করুন এবং আরও জটিল UI স্টেট রিস্টোর করার জন্য এগুলি ব্যবহার করুন, যেমন পারসিস্টেন্ট স্টোরেজ।
এই ডিজাইনের বিকল্পগুলি আপনার অ্যাপের নির্দিষ্ট ব্যবহারের কেস এবং আপনার ব্যবহারকারীরা এটি কীভাবে আচরণ করবে বলে আশা করে তার উপর নির্ভর করে।
স্টেট রিস্টোরেশন যাচাই করুন
অ্যাক্টিভিটি বা প্রসেস আবার তৈরি করা হলে, আপনার কম্পোজ এলিমেন্টে rememberSaveable-এর সাথে স্টোর করা স্টেট সঠিকভাবে রিস্টোর করা হয়েছে কিনা তা আপনি যাচাই করতে পারবেন। এটি করার জন্য নির্দিষ্ট API আছে, যেমন
StateRestorationTester। আরও জানতে টেস্টিং ডকুমেন্টেশন
চেক করুন।
বিজনেস লজিক
আপনার UI এলিমেন্ট স্টেট যদি ViewModel-এ হোইস্ট করা হয় কারণ এটি
বিজনেস লজিকের জন্য প্রয়োজন, তাহলে আপনি ViewModel-এর API ব্যবহার করতে পারবেন।
আপনার Android অ্যাপ্লিকেশনে ViewModel ব্যবহার করার অন্যতম প্রধান সুবিধা হল
এটি ফ্রিতে কনফিগারেশন পরিবর্তন ম্যানেজ করে। কনফিগারেশন পরিবর্তন হলে এবং অ্যাক্টিভিটি নষ্ট হয়ে আবার তৈরি হলে, ViewModel-এ হোস্ট করা UI স্টেট মেমরিতে রাখা হয়। আবার তৈরি করার পরে, পুরনো ViewModel
ইনস্ট্যান্স নতুন অ্যাক্টিভিটি ইনস্ট্যান্সের সাথে অ্যাটাচ করা হয়।
তবে, সিস্টেম-ইনিশিয়েটেড প্রসেস বন্ধ করার ক্ষেত্রে ViewModel ইনস্ট্যান্স কাজ করে না।
UI স্টেট যাতে এর পরেও কাজ করে, তার জন্য ViewModel-এর জন্য সেভ করা স্টেট মডিউল ব্যবহার করুন,
এতে SavedStateHandle API থাকে।
পেশাদার পদ্ধতি
SavedStateHandle-ও UI স্টেট স্টোর করার জন্য Bundle মেকানিজম ব্যবহার করে, তাই
আপনাকে শুধু সাধারণ UI এলিমেন্ট স্টেট স্টোর করার জন্য এটি ব্যবহার করতে হবে।
স্ক্রিন UI স্টেট, যা UI ছাড়া আপনার অ্যাপ্লিকেশনের লেয়ার অ্যাক্সেস করে এবং ব্যবসায়িক নিয়ম প্রয়োগ করে তৈরি করা হয়, সেটি SavedStateHandle-এ স্টোর করা উচিত নয় কারণ এটি জটিল এবং আকারে বড় হতে পারে। আপনি জটিল বা বড় ডেটা স্টোর করার জন্য
বিভিন্ন মেকানিজম ব্যবহার করতে পারেন, যেমন লোকাল পারসিস্টেন্ট
স্টোরেজ। প্রসেস রিক্রিয়েশনের পরে, SavedStateHandle-এ (যদি থাকে) স্টোর করা রিস্টোর করা ট্রানজিয়েন্ট স্টেট সহ স্ক্রিনটি আবার তৈরি করা হয় এবং
স্ক্রিন UI স্টেট ডেটা লেয়ার থেকে আবার তৈরি করা হয়।
SavedStateHandleটি API
UI এলিমেন্ট স্টেট স্টোর করার জন্য SavedStateHandle-এর বিভিন্ন API আছে, যার মধ্যে
উল্লেখযোগ্য হল:
State কম্পোজ করুন |
saveable() |
|---|---|
StateFlow |
getStateFlow() |
State কম্পোজ করুন
UI এলিমেন্ট স্টেট MutableState হিসেবে পড়তে ও লিখতে SavedStateHandle-এর saveable API ব্যবহার করুন, যাতে এটি ন্যূনতম কোড সেট-আপের মাধ্যমে অ্যাক্টিভিটি ও প্রসেস রিক্রিয়েশন সারভাইভ করে।
saveable API প্রিমিটিভ টাইপকে সরাসরি সাপোর্ট করে এবং কাস্টম সেভার ব্যবহার করার জন্য stateSaver প্যারামিটার গ্রহণ করে, ঠিক rememberSaveable()-এর মতো।
নিম্নলিখিত স্নিপেটে, message ব্যবহারকারীর ইনপুট স্টোর করে যা TextField-এ টাইপ করা হয়:
class ConversationViewModel( savedStateHandle: SavedStateHandle ) : ViewModel() { var message by savedStateHandle.saveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) } private set fun update(newMessage: TextFieldValue) { message = newMessage } /*...*/ } val viewModel = ConversationViewModel(SavedStateHandle()) @Composable fun UserInput(/*...*/) { TextField( value = viewModel.message, onValueChange = { viewModel.update(it) } ) }
saveable API ব্যবহার করার ব্যাপারে আরও তথ্য পেতে
SavedStateHandle ডকুমেন্টেশন দেখুন।
StateFlow
UI এলিমেন্ট স্টেট স্টোর করতে getStateFlow() ব্যবহার করুন এবং SavedStateHandle থেকে
ফ্লো হিসেবে এটি ব্যবহার করুন। StateFlow শুধু পড়া যায়,
এবং API-এর জন্য আপনাকে একটি কী উল্লেখ করতে হবে যাতে আপনি নতুন মান নির্গত করার জন্য
ফ্লোটি প্রতিস্থাপন করতে পারেন। আপনার কনফিগার করা 'কী' ব্যবহার করে, আপনি StateFlow
পুনরুদ্ধার করতে এবং লেটেস্ট ভ্যালু সংগ্রহ করতে পারবেন।
নিম্নলিখিত স্নিপেটে, savedFilterType হল একটি StateFlow ভেরিয়েবল যা
চ্যাট অ্যাপে চ্যাট চ্যানেলের তালিকায় প্রয়োগ করা ফিল্টারের ধরন স্টোর করে:
private const val CHANNEL_FILTER_SAVED_STATE_KEY = "ChannelFilterKey" class ChannelViewModel( channelsRepository: ChannelsRepository, private val savedStateHandle: SavedStateHandle ) : ViewModel() { private val savedFilterType: StateFlow<ChannelsFilterType> = savedStateHandle.getStateFlow( key = CHANNEL_FILTER_SAVED_STATE_KEY, initialValue = ChannelsFilterType.ALL_CHANNELS ) private val filteredChannels: Flow<List<Channel>> = combine(channelsRepository.getAll(), savedFilterType) { channels, type -> filter(channels, type) }.onStart { emit(emptyList()) } fun setFiltering(requestType: ChannelsFilterType) { savedStateHandle[CHANNEL_FILTER_SAVED_STATE_KEY] = requestType } /*...*/ } enum class ChannelsFilterType { ALL_CHANNELS, RECENT_CHANNELS, ARCHIVED_CHANNELS }
ব্যবহারকারী প্রতিবার নতুন ফিল্টারের ধরন বেছে নিলে, setFiltering কল করা হয়। এটি
_CHANNEL_FILTER_SAVED_STATE_KEY_ কী-এর সাথে স্টোর করা SavedStateHandle-এ নতুন মান সেভ
করে। savedFilterType হল একটি ফ্লো যা 'কী'-তে সেভ করা
লেটেস্ট ভ্যালু এমিট করে। চ্যানেল ফিল্টার করার জন্য filteredChannels ফ্লোতে সাবস্ক্রাইব করা হয়েছে।
getStateFlow() API সম্পর্কে আরও তথ্য পেতে SavedStateHandle ডকুমেন্টেশন দেখুন।
সারাংশ
নিম্নলিখিত সারণীতে এই বিভাগে কভার করা API-এর সারসংক্ষেপ এবং UI স্টেট সেভ করার জন্য প্রতিটি কখন ব্যবহার করতে হবে তা দেওয়া হয়েছে:
| ইভেন্ট | UI লজিক | ViewModel-এ বিজনেস লজিক |
|---|---|---|
| কনফিগারেশনে পরিবর্তন | rememberSaveable |
অটোমেটিক |
| সিস্টেম-ইনিশিয়েটেড প্রসেস বন্ধ করা | rememberSaveable |
SavedStateHandle |
কোন API ব্যবহার করা হবে তা নির্ভর করে স্টেট কোথায় রাখা হয়েছে এবং এর জন্য কী লজিক
প্রয়োজন তার উপর। UI লজিক-এ ব্যবহৃত স্টেট-এর জন্য, rememberSaveable ব্যবহার করুন। বিজনেস লজিকে ব্যবহার করা হয় এমন
স্টেটের জন্য, আপনি যদি এটি ViewModel-এ রাখেন,
তাহলে SavedStateHandle ব্যবহার করে এটি সেভ করুন।
অল্প পরিমাণে UI স্টেট স্টোর করতে
আপনার বান্ডেল API (rememberSaveable এবং SavedStateHandle) ব্যবহার করা উচিত। UI-কে তার আগের অবস্থায় ফিরিয়ে আনার জন্য এই ডেটা হল ন্যূনতম প্রয়োজনীয় ডেটা, এর সাথে অন্যান্য স্টোরিং মেকানিজমও রয়েছে। যেমন,
ব্যবহারকারী যে প্রোফাইলের আইডি দেখছিলেন সেটি বান্ডেলে স্টোর করলে,
আপনি ডেটা লেয়ার থেকে প্রোফাইলের বিবরণের মতো ভারী ডেটা ফেচ করতে পারবেন।
UI স্টেট সেভ করার বিভিন্ন উপায় সম্পর্কে আরও তথ্য পেতে, সাধারণ UI স্টেট সেভ করা সংক্রান্ত ডকুমেন্টেশন এবং আর্কিটেকচার গাইডের ডেটা লেয়ার পৃষ্ঠা দেখুন।
আপনার জন্য সাজেস্ট করা হয়েছে
- মনে রাখবেন: জাভাস্ক্রিপ্ট বন্ধ থাকলে লিঙ্ক টেক্সট দেখানো হয়
- স্টেট কোথায় হোইস্ট করতে হবে
- State ও Jetpack Compose
- তালিকা ও গ্রিড