UI এর ভূমিকা হল স্ক্রিনে অ্যাপ্লিকেশন ডেটা প্রদর্শন করা এবং ব্যবহারকারীর ইন্টারঅ্যাকশনের প্রাথমিক পয়েন্ট হিসাবে পরিবেশন করা। যখনই ডেটা পরিবর্তিত হয়, হয় ব্যবহারকারীর ইন্টারঅ্যাকশনের কারণে (যেমন একটি বোতাম টিপে) বা বাহ্যিক ইনপুট (যেমন নেটওয়ার্ক প্রতিক্রিয়া), সেই পরিবর্তনগুলি প্রতিফলিত করার জন্য UI আপডেট করা উচিত। কার্যকরভাবে, UI হল অ্যাপ্লিকেশন অবস্থার একটি ভিজ্যুয়াল উপস্থাপনা যা ডেটা স্তর থেকে পুনরুদ্ধার করা হয়েছে।
যাইহোক, ডেটা স্তর থেকে আপনি যে অ্যাপ্লিকেশন ডেটা পান তা সাধারণত আপনার প্রদর্শিত তথ্যের চেয়ে আলাদা বিন্যাসে থাকে। উদাহরণস্বরূপ, আপনার শুধুমাত্র UI-এর জন্য ডেটার অংশের প্রয়োজন হতে পারে, অথবা ব্যবহারকারীর সাথে প্রাসঙ্গিক তথ্য উপস্থাপন করতে আপনাকে দুটি ভিন্ন ডেটা উৎস একত্রিত করতে হতে পারে। আপনি যে যুক্তিই প্রয়োগ করুন না কেন, আপনাকে UI কে সম্পূর্ণরূপে রেন্ডার করার জন্য প্রয়োজনীয় সমস্ত তথ্য পাস করতে হবে। UI স্তর হল একটি পাইপলাইন যা অ্যাপ্লিকেশন ডেটা পরিবর্তনগুলিকে একটি ফর্মে রূপান্তর করে যা UI উপস্থাপন করতে পারে এবং তারপর এটি প্রদর্শন করে।
![একটি সাধারণ আর্কিটেকচারে, UI স্তরের UI উপাদানগুলি স্টেট হোল্ডারদের উপর নির্ভর করে, যা ফলস্বরূপ ডেটা স্তর বা ঐচ্ছিক ডোমেন স্তর থেকে ক্লাসের উপর নির্ভর করে।](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-ui-overview.png?authuser=1&hl=bn)
একটি মৌলিক কেস স্টাডি
এমন একটি অ্যাপ বিবেচনা করুন যা ব্যবহারকারীর পড়ার জন্য সংবাদ নিবন্ধ নিয়ে আসে। অ্যাপটিতে একটি নিবন্ধের স্ক্রীন রয়েছে যা পড়ার জন্য উপলব্ধ নিবন্ধগুলি উপস্থাপন করে এবং সাইন-ইন করা ব্যবহারকারীদের নিবন্ধগুলি বুকমার্ক করার অনুমতি দেয় যা সত্যিই আলাদা। প্রদত্ত যে কোন মুহূর্তে প্রচুর নিবন্ধ থাকতে পারে, পাঠক বিভাগ অনুসারে নিবন্ধগুলি ব্রাউজ করতে সক্ষম হওয়া উচিত। সংক্ষেপে, অ্যাপটি ব্যবহারকারীদের নিম্নলিখিতগুলি করতে দেয়:
- পড়ার জন্য উপলব্ধ নিবন্ধ দেখুন.
- বিভাগ দ্বারা নিবন্ধ ব্রাউজ করুন.
- সাইন ইন করুন এবং নির্দিষ্ট নিবন্ধ বুকমার্ক করুন.
- যোগ্য হলে কিছু প্রিমিয়াম বৈশিষ্ট্য অ্যাক্সেস করুন।
![](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-ui-basic-case-study.png?authuser=1&hl=bn)
নিম্নলিখিত বিভাগগুলি এই উদাহরণটিকে একটি কেস স্টাডি হিসাবে ব্যবহার করে একমুখী ডেটা প্রবাহের নীতিগুলি প্রবর্তন করার জন্য, সেইসাথে এই নীতিগুলি UI স্তরের জন্য অ্যাপ আর্কিটেকচারের প্রেক্ষাপটে যে সমস্যাগুলি সমাধান করতে সহায়তা করে তা চিত্রিত করে৷
UI লেয়ার আর্কিটেকচার
UI শব্দটি UI উপাদানগুলিকে বোঝায় যেমন কার্যকলাপ এবং খণ্ডগুলি যা ডেটা প্রদর্শন করে, তারা এটি করার জন্য কোন API ব্যবহার করে তা থেকে স্বাধীন (ভিউ বা জেটপ্যাক রচনা )। যেহেতু ডেটা স্তরের ভূমিকা হল অ্যাপের ডেটা ধরে রাখা, পরিচালনা করা এবং অ্যাক্সেস প্রদান করা, তাই UI স্তরকে অবশ্যই নিম্নলিখিত পদক্ষেপগুলি সম্পাদন করতে হবে:
- অ্যাপ ডেটা ব্যবহার করুন এবং UI সহজেই রেন্ডার করতে পারে এমন ডেটাতে রূপান্তর করুন।
- UI-রেন্ডারযোগ্য ডেটা ব্যবহার করুন এবং ব্যবহারকারীর কাছে উপস্থাপনের জন্য এটিকে UI উপাদানে রূপান্তর করুন।
- সেই একত্রিত UI উপাদানগুলি থেকে ব্যবহারকারীর ইনপুট ইভেন্টগুলি গ্রহণ করুন এবং প্রয়োজন অনুসারে UI ডেটাতে তাদের প্রভাবগুলি প্রতিফলিত করুন।
- যতক্ষণ প্রয়োজন ততক্ষণ পদক্ষেপ 1 থেকে 3 পুনরাবৃত্তি করুন।
এই নির্দেশিকাটির বাকি অংশগুলি দেখায় যে কীভাবে একটি UI স্তর প্রয়োগ করতে হয় যা এই পদক্ষেপগুলি সম্পাদন করে। বিশেষ করে, এই নির্দেশিকাটি নিম্নলিখিত কাজ এবং ধারণাগুলিকে কভার করে:
- কিভাবে UI অবস্থা সংজ্ঞায়িত করা যায়।
- ইউনিডাইরেশনাল ডাটা ফ্লো (UDF) UI স্টেট তৈরি ও পরিচালনার একটি মাধ্যম হিসেবে।
- UDF নীতি অনুসারে পর্যবেক্ষণযোগ্য ডেটা টাইপের সাথে UI অবস্থা কীভাবে প্রকাশ করা যায়।
- পর্যবেক্ষণযোগ্য UI অবস্থা ব্যবহার করে এমন UI কীভাবে প্রয়োগ করবেন।
এর মধ্যে সবচেয়ে মৌলিক হল UI অবস্থার সংজ্ঞা।
UI অবস্থা সংজ্ঞায়িত করুন
পূর্বে বর্ণিত কেস স্টাডি পড়ুন। সংক্ষেপে, UI প্রতিটি নিবন্ধের জন্য কিছু মেটাডেটা সহ নিবন্ধগুলির একটি তালিকা দেখায়। অ্যাপটি ব্যবহারকারীর কাছে যে তথ্যটি উপস্থাপন করে সেটি হল UI অবস্থা।
অন্য কথায়: ব্যবহারকারী যা দেখেন তা যদি UI হয়, তাহলে অ্যাপটি তাদের দেখতে হবে বলে UI অবস্থা। একই মুদ্রার দুটি বাহুর মতো, UI হল UI অবস্থার ভিজ্যুয়াল উপস্থাপনা। UI অবস্থার যেকোনো পরিবর্তন অবিলম্বে UI-তে প্রতিফলিত হয়।
![UI হল UI স্টেট সহ স্ক্রিনে UI উপাদানগুলিকে আবদ্ধ করার ফলাফল।](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-ui-elements-state.png?authuser=1&hl=bn)
কেস স্টাডি বিবেচনা করুন; নিউজ অ্যাপের প্রয়োজনীয়তাগুলি পূরণ করার জন্য, UI সম্পূর্ণরূপে রেন্ডার করার জন্য প্রয়োজনীয় তথ্যগুলি নিম্নরূপ সংজ্ঞায়িত একটি NewsUiState
ডেটা ক্লাসে এনক্যাপসুলেট করা যেতে পারে:
data class NewsUiState(
val isSignedIn: Boolean = false,
val isPremium: Boolean = false,
val newsItems: List<NewsItemUiState> = listOf(),
val userMessages: List<Message> = listOf()
)
data class NewsItemUiState(
val title: String,
val body: String,
val bookmarked: Boolean = false,
...
)
অপরিবর্তনীয়তা
উপরের উদাহরণে UI রাজ্যের সংজ্ঞা অপরিবর্তনীয়। এর মূল সুবিধা হল যে অপরিবর্তনীয় বস্তুগুলি তাত্ক্ষণিক সময়ে আবেদনের অবস্থা সম্পর্কে গ্যারান্টি প্রদান করে। এটি একটি একক ভূমিকায় ফোকাস করার জন্য UI-কে মুক্ত করে: রাজ্যটি পড়তে এবং সেই অনুযায়ী এর UI উপাদানগুলি আপডেট করতে। ফলস্বরূপ, আপনার কখনই UI-তে UI অবস্থাটি সরাসরি পরিবর্তন করা উচিত নয় যদি না UI নিজেই এর ডেটার একমাত্র উত্স হয়। এই নীতি লঙ্ঘনের ফলে তথ্যের একই অংশের জন্য সত্যের একাধিক উত্স পাওয়া যায়, যা ডেটার অসঙ্গতি এবং সূক্ষ্ম বাগগুলির দিকে পরিচালিত করে।
উদাহরণ স্বরূপ, যদি কেস স্টাডিতে UI স্টেট থেকে একটি NewsItemUiState
অবজেক্টে bookmarked
পতাকা Activity
ক্লাসে আপডেট করা হয়, তাহলে সেই পতাকাটি কোনো নিবন্ধের বুকমার্ক করা স্ট্যাটাসের উৎস হিসেবে ডেটা স্তরের সাথে প্রতিযোগিতা করবে। অপরিবর্তনীয় ডেটা ক্লাস এই ধরনের অ্যান্টিপ্যাটার্ন প্রতিরোধের জন্য খুব দরকারী।
এই নির্দেশিকায় নামকরণের নিয়মাবলী
এই নির্দেশিকায়, UI স্টেট ক্লাসগুলির নামকরণ করা হয়েছে স্ক্রিনের কার্যকারিতা বা স্ক্রিনের অংশের উপর ভিত্তি করে যা তারা বর্ণনা করেছে। কনভেনশনটি নিম্নরূপ:
কার্যকারিতা + UiState ।
উদাহরণস্বরূপ, সংবাদ প্রদর্শনকারী একটি স্ক্রিনের অবস্থাকে বলা হতে পারে NewsUiState
, এবং সংবাদ আইটেমগুলির একটি তালিকায় একটি সংবাদ আইটেমের অবস্থা একটি NewsItemUiState
হতে পারে৷
ইউনিডাইরেশানাল ডেটা ফ্লো সহ রাজ্য পরিচালনা করুন
পূর্ববর্তী বিভাগটি প্রতিষ্ঠিত করেছে যে UI স্টেট হল UI-এর রেন্ডার করার জন্য প্রয়োজনীয় বিবরণগুলির একটি অপরিবর্তনীয় স্ন্যাপশট। যাইহোক, অ্যাপ্লিকেশানগুলিতে ডেটার গতিশীল প্রকৃতির মানে হল যে রাজ্য সময়ের সাথে পরিবর্তিত হতে পারে। এটি ব্যবহারকারীর মিথস্ক্রিয়া বা অন্যান্য ইভেন্টের কারণে হতে পারে যা অন্তর্নিহিত ডেটা পরিবর্তন করে যা অ্যাপটি তৈরি করতে ব্যবহৃত হয়।
এই মিথস্ক্রিয়াগুলি তাদের প্রক্রিয়া করার জন্য একজন মধ্যস্থতার কাছ থেকে উপকৃত হতে পারে, প্রতিটি ইভেন্টে প্রয়োগ করার যুক্তি সংজ্ঞায়িত করে এবং UI অবস্থা তৈরি করার জন্য ব্যাকিং ডেটা উত্সগুলিতে প্রয়োজনীয় রূপান্তরগুলি সম্পাদন করে৷ এই মিথস্ক্রিয়া এবং তাদের যুক্তিগুলি UI তেই রাখা হতে পারে, তবে এটি দ্রুত অপ্রীতিকর হয়ে উঠতে পারে কারণ UI এর নাম থেকে যা বোঝায় তার চেয়ে বেশি হতে শুরু করে: এটি ডেটা মালিক, প্রযোজক, ট্রান্সফরমার এবং আরও অনেক কিছু হয়ে যায়। তদ্ব্যতীত, এটি পরীক্ষাযোগ্যতাকে প্রভাবিত করতে পারে কারণ ফলস্বরূপ কোডটি একটি দৃঢ়ভাবে সংযুক্ত অ্যামালগাম যার কোন সীমানা নেই। শেষ পর্যন্ত, UI কম বোঝা থেকে উপকৃত হয়। UI স্টেট খুব সহজ না হলে, UI এর একমাত্র দায়িত্ব UI স্টেট ব্যবহার করা এবং প্রদর্শন করা।
এই বিভাগে ইউনিডাইরেশনাল ডেটা ফ্লো (UDF) নিয়ে আলোচনা করা হয়েছে, একটি আর্কিটেকচার প্যাটার্ন যা দায়িত্বের এই সুস্থ বিচ্ছেদ কার্যকর করতে সাহায্য করে।
রাজ্য ধারক
যে ক্লাসগুলি UI স্টেট তৈরির জন্য দায়ী এবং সেই কাজের জন্য প্রয়োজনীয় যুক্তি ধারণ করে তাদেরকে স্টেট হোল্ডার বলা হয়। স্টেট হোল্ডাররা তাদের ম্যানেজ করা সংশ্লিষ্ট UI উপাদানগুলির সুযোগের উপর নির্ভর করে বিভিন্ন আকারে আসে, একটি একক উইজেট যেমন নীচের অ্যাপ বার থেকে শুরু করে পুরো স্ক্রীন বা একটি নেভিগেশন গন্তব্য।
পরবর্তী ক্ষেত্রে, সাধারণ বাস্তবায়ন একটি ViewModel এর একটি উদাহরণ, যদিও অ্যাপ্লিকেশনের প্রয়োজনীয়তার উপর নির্ভর করে, একটি সাধারণ ক্লাস যথেষ্ট হতে পারে। উদাহরণ স্বরূপ, কেস স্টাডি থেকে News অ্যাপটি সেই বিভাগে প্রদর্শিত স্ক্রিনের জন্য UI অবস্থা তৈরি করতে একটি রাজ্য ধারক হিসাবে একটি NewsViewModel
ক্লাস ব্যবহার করে।
UI এবং এর রাষ্ট্র প্রযোজকের মধ্যে সহনির্ভরতা মডেল করার অনেক উপায় রয়েছে। যাইহোক, যেহেতু UI এবং এর ViewModel
ক্লাসের মধ্যে মিথস্ক্রিয়াটি মূলত ইভেন্ট ইনপুট এবং এর পরবর্তী স্টেট আউটপুট হিসাবে বোঝা যায়, সম্পর্কটিকে নিম্নলিখিত চিত্রে দেখানো হিসাবে উপস্থাপন করা যেতে পারে:
![অ্যাপ্লিকেশন ডেটা ডেটা স্তর থেকে ভিউমডেলে প্রবাহিত হয়। UI অবস্থা ভিউমডেল থেকে UI উপাদানগুলিতে প্রবাহিত হয় এবং ইভেন্টগুলি UI উপাদানগুলি থেকে ViewModel-এ ফিরে আসে৷](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-ui-udf.png?authuser=1&hl=bn)
যে প্যাটার্নে রাষ্ট্র নিচে প্রবাহিত হয় এবং ঘটনাগুলি উপরে প্রবাহিত হয় তাকে ইউনিডাইরেশনাল ডাটা ফ্লো (UDF) বলে। অ্যাপ আর্কিটেকচারের জন্য এই প্যাটার্নের প্রভাবগুলি নিম্নরূপ:
- ভিউমডেল UI দ্বারা গ্রাস করা অবস্থাকে ধরে রাখে এবং প্রকাশ করে। UI স্টেট হল ViewModel দ্বারা রূপান্তরিত অ্যাপ্লিকেশন ডেটা।
- UI ব্যবহারকারী ইভেন্টের ভিউমডেলকে অবহিত করে।
- ViewModel ব্যবহারকারীর ক্রিয়াগুলি পরিচালনা করে এবং অবস্থা আপডেট করে।
- আপডেট করা অবস্থা রেন্ডার করার জন্য UI-তে ফেরত দেওয়া হয়।
- রাষ্ট্রের মিউটেশন ঘটায় এমন যেকোনো ঘটনার জন্য উপরেরটি পুনরাবৃত্তি করা হয়।
নেভিগেশন গন্তব্য বা স্ক্রিনের জন্য, ভিউমডেল ভান্ডারের সাথে কাজ করে বা ডেটা পাওয়ার জন্য কেস ক্লাস ব্যবহার করে এবং রাষ্ট্রের মিউটেশনের কারণ হতে পারে এমন ঘটনাগুলির প্রভাবগুলিকে অন্তর্ভুক্ত করার সময় এটিকে UI অবস্থায় রূপান্তরিত করে। পূর্বে উল্লিখিত কেস স্টাডিতে নিবন্ধগুলির একটি তালিকা রয়েছে, প্রতিটিতে একটি শিরোনাম, বিবরণ, উত্স, লেখকের নাম, প্রকাশের তারিখ এবং এটি বুকমার্ক করা হয়েছে কিনা। প্রতিটি নিবন্ধ আইটেমের জন্য UI এই মত দেখায়:
![](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-ui-basic-case-study-item.png?authuser=1&hl=bn)
একজন ব্যবহারকারী একটি নিবন্ধ বুকমার্ক করার অনুরোধ করছেন এমন একটি ইভেন্টের উদাহরণ যা রাষ্ট্রীয় পরিবর্তন ঘটাতে পারে। রাষ্ট্রের প্রযোজক হিসাবে, UI রাজ্যের সমস্ত ক্ষেত্রগুলিকে পপুলেট করার জন্য প্রয়োজনীয় সমস্ত যুক্তি সংজ্ঞায়িত করা এবং UI সম্পূর্ণরূপে রেন্ডার করার জন্য প্রয়োজনীয় ইভেন্টগুলি প্রক্রিয়া করার জন্য এটি ভিউ মডেলের দায়িত্ব৷
![একটি UI ইভেন্ট ঘটে যখন ব্যবহারকারী একটি artcile বুকমার্ক করে। ভিউমডেল রাজ্যের পরিবর্তনের ডেটা স্তরকে অবহিত করে। ডেটা স্তরটি ডেটা পরিবর্তনকে অব্যাহত রাখে এবং অ্যাপ্লিকেশন ডেটা আপডেট করে। বুকমার্ক করা নিবন্ধের সাথে নতুন অ্যাপের ডেটা ভিউমডেল পর্যন্ত পাঠানো হয়, যা তারপর নতুন UI অবস্থা তৈরি করে এবং প্রদর্শনের জন্য UI উপাদানগুলিতে পাস করে।](https://developer.android.com/static/topic/libraries/architecture/images/mad-arch-ui-udf-in-action.png?authuser=1&hl=bn)
নিম্নলিখিত বিভাগগুলি রাষ্ট্রের পরিবর্তন ঘটায় এবং কীভাবে সেগুলি UDF ব্যবহার করে প্রক্রিয়া করা যেতে পারে সেগুলি ঘনিষ্ঠভাবে পর্যবেক্ষণ করে৷
যুক্তির প্রকারভেদ
একটি নিবন্ধ বুকমার্ক করা ব্যবসায়িক যুক্তির একটি উদাহরণ কারণ এটি আপনার অ্যাপকে মূল্য দেয়। এই সম্পর্কে আরও জানতে, ডেটা স্তর পৃষ্ঠা দেখুন। যাইহোক, বিভিন্ন ধরনের যুক্তি আছে যা সংজ্ঞায়িত করা গুরুত্বপূর্ণ:
- ব্যবসায়িক যুক্তি হল অ্যাপ ডেটার জন্য পণ্যের প্রয়োজনীয়তা বাস্তবায়ন। ইতিমধ্যে উল্লিখিত হিসাবে, একটি উদাহরণ হল কেস স্টাডি অ্যাপে একটি নিবন্ধ বুকমার্ক করা। ব্যবসায়িক যুক্তি সাধারণত ডোমেইন বা ডেটা স্তরগুলিতে স্থাপন করা হয়, তবে কখনই UI স্তরে নয়।
- UI আচরণের যুক্তি বা UI লজিক হল কীভাবে স্ক্রিনে রাজ্যের পরিবর্তনগুলি প্রদর্শন করা যায়। উদাহরণগুলির মধ্যে রয়েছে অ্যান্ড্রয়েড
Resources
ব্যবহার করে স্ক্রিনে দেখানোর জন্য সঠিক পাঠ্য পাওয়া, ব্যবহারকারী যখন একটি বোতামে ক্লিক করেন তখন একটি নির্দিষ্ট স্ক্রিনে নেভিগেট করা, অথবা টোস্ট বা স্ন্যাকবার ব্যবহার করে স্ক্রিনে ব্যবহারকারীর বার্তা প্রদর্শন করা।
UI লজিক, বিশেষ করে যখন এটি Context
এর মতো UI প্রকারগুলিকে জড়িত করে, তখন UI-তে থাকা উচিত, ViewModel-এ নয়৷ যদি UI জটিলতায় বৃদ্ধি পায় এবং আপনি পরীক্ষাযোগ্যতা এবং উদ্বেগের বিচ্ছিন্নতার পক্ষে অন্য শ্রেণীতে UI যুক্তি অর্পণ করতে চান, আপনি একটি সাধারণ শ্রেণী তৈরি করতে পারেন একজন রাষ্ট্র ধারক হিসাবে । UI-তে তৈরি করা সাধারণ ক্লাসগুলি Android SDK নির্ভরতা নিতে পারে কারণ তারা UI-এর জীবনচক্র অনুসরণ করে; ViewModel অবজেক্টের আয়ু বেশি থাকে।
স্টেট হোল্ডারদের সম্পর্কে আরও তথ্যের জন্য এবং কীভাবে তারা UI তৈরিতে সাহায্য করার প্রসঙ্গে ফিট করে, জেটপ্যাক কম্পোজ স্টেট গাইড দেখুন।
কেন UDF ব্যবহার করবেন?
চিত্র 4-এ দেখানো হিসাবে UDF রাষ্ট্রীয় উৎপাদনের চক্রকে মডেল করে। এটি সেই স্থানকেও আলাদা করে যেখানে রাষ্ট্রের পরিবর্তনের উৎপত্তি হয়, সেই স্থান যেখানে তারা রূপান্তরিত হয় এবং শেষ পর্যন্ত যেখানে সেগুলি খাওয়া হয়। এই বিচ্ছেদটি UI-কে ঠিক তার নামটি যা বোঝায় তা করতে দেয়: অবস্থার পরিবর্তনগুলি পর্যবেক্ষণ করে তথ্য প্রদর্শন করে এবং সেই পরিবর্তনগুলিকে ভিউমডেলে পাস করে ব্যবহারকারীর অভিপ্রায় রিলে করে৷
অন্য কথায়, UDF নিম্নলিখিতগুলির জন্য অনুমতি দেয়:
- ডেটা সামঞ্জস্য। UI এর জন্য সত্যের একটি একক উৎস রয়েছে।
- পরীক্ষাযোগ্যতা। রাষ্ট্রের উৎস বিচ্ছিন্ন এবং তাই UI থেকে স্বাধীন।
- রক্ষণাবেক্ষণযোগ্যতা। রাষ্ট্রের মিউটেশন একটি সু-সংজ্ঞায়িত প্যাটার্ন অনুসরণ করে যেখানে মিউটেশনগুলি ব্যবহারকারীর ইভেন্ট এবং তারা যে ডেটার উৎস থেকে টেনে নেয় উভয়ের ফলাফল।
UI অবস্থা প্রকাশ করুন
আপনি আপনার UI অবস্থা সংজ্ঞায়িত করার পরে এবং আপনি কীভাবে সেই রাজ্যের উত্পাদন পরিচালনা করবেন তা নির্ধারণ করার পরে, পরবর্তী পদক্ষেপটি UI-তে উত্পাদিত অবস্থা উপস্থাপন করা। যেহেতু আপনি রাষ্ট্রের উৎপাদন পরিচালনা করতে ইউডিএফ ব্যবহার করছেন, আপনি উত্পাদিত রাজ্যটিকে একটি স্ট্রিম হিসাবে বিবেচনা করতে পারেন - অন্য কথায়, সময়ের সাথে সাথে রাজ্যের একাধিক সংস্করণ তৈরি করা হবে। ফলস্বরূপ, আপনি LiveData
বা StateFlow
এর মতো একটি পর্যবেক্ষণযোগ্য ডেটা ধারকের মধ্যে UI অবস্থাটি প্রকাশ করতে হবে। এর কারণ হল যাতে UI সরাসরি ভিউমডেল থেকে ম্যানুয়ালি ডেটা টেনে না নিয়ে রাজ্যে করা কোনও পরিবর্তনের প্রতিক্রিয়া জানাতে পারে। এই ধরনেরগুলির মধ্যে সর্বদা UI স্টেটের সর্বশেষ সংস্করণ ক্যাশে থাকার সুবিধা রয়েছে, যা কনফিগারেশন পরিবর্তনের পরে দ্রুত স্থিতি পুনরুদ্ধারের জন্য দরকারী।
ভিউ
class NewsViewModel(...) : ViewModel() {
val uiState: StateFlow<NewsUiState> = …
}
রচনা করুন
class NewsViewModel(...) : ViewModel() {
val uiState: NewsUiState = …
}
একটি পর্যবেক্ষণযোগ্য ডেটা ধারক হিসাবে LiveData
এর ভূমিকার জন্য, এই কোডল্যাবটি দেখুন। কোটলিন প্রবাহের অনুরূপ ভূমিকার জন্য, অ্যান্ড্রয়েডে কোটলিন প্রবাহ দেখুন।
যে ক্ষেত্রে UI-তে প্রকাশিত ডেটা তুলনামূলকভাবে সহজ, এটি প্রায়শই একটি UI স্টেট টাইপে ডেটা মোড়ানো মূল্যবান কারণ এটি স্টেট হোল্ডারের নির্গমন এবং এর সম্পর্কিত স্ক্রীন বা UI উপাদানের মধ্যে সম্পর্ক প্রকাশ করে। তদ্ব্যতীত, UI উপাদানটি আরও জটিল হওয়ার সাথে সাথে UI উপাদান রেন্ডার করার জন্য প্রয়োজনীয় অতিরিক্ত তথ্য মিটমাট করার জন্য UI অবস্থার সংজ্ঞায় যোগ করা সর্বদা সহজ।
UiState
এর একটি স্ট্রীম তৈরি করার একটি সাধারণ উপায় হল ViewModel থেকে একটি অপরিবর্তনীয় স্ট্রীম হিসাবে একটি ব্যাকিং মিউটেবল স্ট্রীমকে প্রকাশ করা—উদাহরণস্বরূপ, একটি MutableStateFlow<UiState>
একটি StateFlow<UiState>
হিসাবে প্রকাশ করা।
ভিউ
class NewsViewModel(...) : ViewModel() {
private val _uiState = MutableStateFlow(NewsUiState())
val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()
...
}
রচনা করুন
class NewsViewModel(...) : ViewModel() {
var uiState by mutableStateOf(NewsUiState())
private set
...
}
ভিউমডেল তারপরে এমন পদ্ধতিগুলিকে প্রকাশ করতে পারে যা অভ্যন্তরীণভাবে রাজ্যকে পরিবর্তন করে, UI ব্যবহার করার জন্য আপডেট প্রকাশ করে। উদাহরণস্বরূপ, যে ক্ষেত্রে একটি অ্যাসিঙ্ক্রোনাস অ্যাকশন সঞ্চালিত করা প্রয়োজন তা নিন; viewModelScope
ব্যবহার করে একটি coroutine চালু করা যেতে পারে, এবং পরিবর্তিত অবস্থা সম্পূর্ণ হওয়ার পরে আপডেট করা যেতে পারে।
ভিউ
class NewsViewModel(
private val repository: NewsRepository,
...
) : ViewModel() {
private val _uiState = MutableStateFlow(NewsUiState())
val uiState: StateFlow<NewsUiState> = _uiState.asStateFlow()
private var fetchJob: Job? = null
fun fetchArticles(category: String) {
fetchJob?.cancel()
fetchJob = viewModelScope.launch {
try {
val newsItems = repository.newsItemsForCategory(category)
_uiState.update {
it.copy(newsItems = newsItems)
}
} catch (ioe: IOException) {
// Handle the error and notify the UI when appropriate.
_uiState.update {
val messages = getMessagesFromThrowable(ioe)
it.copy(userMessages = messages)
}
}
}
}
}
রচনা করুন
class NewsViewModel(
private val repository: NewsRepository,
...
) : ViewModel() {
var uiState by mutableStateOf(NewsUiState())
private set
private var fetchJob: Job? = null
fun fetchArticles(category: String) {
fetchJob?.cancel()
fetchJob = viewModelScope.launch {
try {
val newsItems = repository.newsItemsForCategory(category)
uiState = uiState.copy(newsItems = newsItems)
} catch (ioe: IOException) {
// Handle the error and notify the UI when appropriate.
val messages = getMessagesFromThrowable(ioe)
uiState = uiState.copy(userMessages = messages)
}
}
}
}
উপরের উদাহরণে, NewsViewModel
ক্লাস একটি নির্দিষ্ট বিভাগের জন্য নিবন্ধগুলি আনার চেষ্টা করে এবং তারপর চেষ্টার ফলাফলকে প্রতিফলিত করে—সে সফল হোক বা ব্যর্থতা—ইউআই স্টেটে যেখানে UI যথাযথভাবে প্রতিক্রিয়া জানাতে পারে৷ ত্রুটি পরিচালনা সম্পর্কে আরও জানতে পর্দায় ত্রুটিগুলি দেখান বিভাগে দেখুন৷
অতিরিক্ত বিবেচনা
পূর্ববর্তী নির্দেশিকা ছাড়াও, UI অবস্থা প্রকাশ করার সময় নিম্নলিখিতগুলি বিবেচনা করুন:
একটি UI স্টেট অবজেক্টকে একে অপরের সাথে সম্পর্কিত রাজ্যগুলি পরিচালনা করা উচিত। এটি কম অসঙ্গতির দিকে পরিচালিত করে এবং এটি কোডটিকে বোঝা সহজ করে তোলে। আপনি যদি দুটি ভিন্ন স্ট্রীমে সংবাদ আইটেমগুলির তালিকা এবং বুকমার্কের সংখ্যা প্রকাশ করেন, তাহলে আপনি এমন পরিস্থিতিতে শেষ হতে পারেন যেখানে একটি আপডেট করা হয়েছিল এবং অন্যটি ছিল না। আপনি যখন একটি একক স্ট্রিম ব্যবহার করেন, তখন উভয় উপাদানই আপ টু ডেট থাকে। তদুপরি, কিছু ব্যবসায়িক যুক্তির জন্য উত্সগুলির সংমিশ্রণের প্রয়োজন হতে পারে। উদাহরণস্বরূপ, আপনাকে একটি বুকমার্ক বোতাম দেখাতে হতে পারে শুধুমাত্র যদি ব্যবহারকারী সাইন ইন করে থাকেন এবং সেই ব্যবহারকারী একটি প্রিমিয়াম সংবাদ পরিষেবার গ্রাহক হন৷ আপনি নিম্নরূপ একটি UI রাজ্য শ্রেণী সংজ্ঞায়িত করতে পারেন:
data class NewsUiState( val isSignedIn: Boolean = false, val isPremium: Boolean = false, val newsItems: List<NewsItemUiState> = listOf() ) val NewsUiState.canBookmarkNews: Boolean get() = isSignedIn && isPremium
এই ঘোষণায়, বুকমার্ক বোতামের দৃশ্যমানতা অন্য দুটি বৈশিষ্ট্যের একটি প্রাপ্ত সম্পত্তি। ব্যবসায়িক যুক্তি আরও জটিল হয়ে উঠলে, একটি একক
UiState
ক্লাস থাকা যেখানে সমস্ত বৈশিষ্ট্য অবিলম্বে উপলব্ধ থাকে তা ক্রমশ গুরুত্বপূর্ণ হয়ে ওঠে।UI স্টেটস: একক স্ট্রীম নাকি একাধিক স্ট্রীম? একটি একক স্ট্রীম বা একাধিক স্ট্রীমে UI অবস্থা প্রকাশ করার মধ্যে বেছে নেওয়ার মূল গাইডিং নীতি হল আগের বুলেট পয়েন্ট: নির্গত আইটেমগুলির মধ্যে সম্পর্ক। একটি একক-স্ট্রিম এক্সপোজারের সবচেয়ে বড় সুবিধা হল সুবিধা এবং ডেটা সামঞ্জস্যতা: রাজ্যের ভোক্তাদের কাছে সর্বদা যে কোনো তাত্ক্ষণিক সময়ে সর্বশেষ তথ্য উপলব্ধ থাকে। যাইহোক, এমন কিছু উদাহরণ রয়েছে যেখানে ভিউমডেল থেকে রাজ্যের পৃথক স্ট্রিম উপযুক্ত হতে পারে:
অসংলগ্ন ডেটা প্রকার: UI রেন্ডার করার জন্য প্রয়োজনীয় কিছু রাজ্য একে অপরের থেকে সম্পূর্ণ স্বাধীন হতে পারে। এই জাতীয় ক্ষেত্রে, এই ভিন্ন রাজ্যগুলিকে একত্রিত করার খরচগুলি সুবিধার চেয়ে বেশি হতে পারে, বিশেষ করে যদি এই রাজ্যগুলির একটি অন্যটির চেয়ে বেশি ঘন ঘন আপডেট করা হয়।
UiState
ডিফিং:UiState
অবজেক্টে যত বেশি ক্ষেত্র থাকবে, স্ট্রীমটি তার একটি ক্ষেত্র আপডেট হওয়ার ফলে নির্গত হওয়ার সম্ভাবনা তত বেশি। পরপর নির্গমন ভিন্ন বা একই কিনা তা বোঝার জন্য ভিউগুলির একটি ভিন্ন পদ্ধতি নেই, তাই প্রতিটি নির্গমন দৃশ্যে একটি আপডেট ঘটায়। এর মানে হল যেFlow
এপিআই ব্যবহার করে বাLiveData
distinctUntilChanged()
এর মতো পদ্ধতি ব্যবহার করে প্রশমন প্রয়োজন হতে পারে।
UI অবস্থা ব্যবহার করুন
UI-তে UiState
অবজেক্টের স্ট্রীম গ্রাস করতে, আপনি যে পর্যবেক্ষণযোগ্য ডেটা টাইপ ব্যবহার করছেন তার জন্য আপনি টার্মিনাল অপারেটর ব্যবহার করেন। উদাহরণস্বরূপ, LiveData
জন্য আপনি observe()
পদ্ধতি ব্যবহার করেন এবং কোটলিন প্রবাহের জন্য আপনি collect()
পদ্ধতি বা এর বিভিন্নতা ব্যবহার করেন।
UI-তে পর্যবেক্ষণযোগ্য ডেটা হোল্ডার ব্যবহার করার সময়, নিশ্চিত করুন যে আপনি UI-এর জীবনচক্রকে বিবেচনায় নিয়েছেন। এটি গুরুত্বপূর্ণ কারণ ব্যবহারকারীর কাছে দৃশ্যটি প্রদর্শিত না হলে UI-এর UI অবস্থা পর্যবেক্ষণ করা উচিত নয়। এই বিষয় সম্পর্কে আরো জানতে, এই ব্লগ পোস্ট দেখুন. LiveData
ব্যবহার করার সময়, LifecycleOwner
পরোক্ষভাবে লাইফসাইকেলের উদ্বেগের যত্ন নেয়। ফ্লো ব্যবহার করার সময়, উপযুক্ত coroutine স্কোপ এবং repeatOnLifecycle
API এর সাথে এটি পরিচালনা করা ভাল:
ভিউ
class NewsActivity : AppCompatActivity() {
private val viewModel: NewsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
...
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.uiState.collect {
// Update UI elements
}
}
}
}
}
রচনা করুন
@Composable
fun LatestNewsScreen(
viewModel: NewsViewModel = viewModel()
) {
// Show UI elements based on the viewModel.uiState
}
চলমান অপারেশন দেখান
একটি UiState
ক্লাসে লোডিং রাজ্যগুলিকে উপস্থাপন করার একটি সহজ উপায় হল একটি বুলিয়ান ক্ষেত্র:
data class NewsUiState(
val isFetchingArticles: Boolean = false,
...
)
এই পতাকার মান UI-তে একটি অগ্রগতি দণ্ডের উপস্থিতি বা অনুপস্থিতির প্রতিনিধিত্ব করে৷
ভিউ
class NewsActivity : AppCompatActivity() {
private val viewModel: NewsViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) {
...
lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
// Bind the visibility of the progressBar to the state
// of isFetchingArticles.
viewModel.uiState
.map { it.isFetchingArticles }
.distinctUntilChanged()
.collect { progressBar.isVisible = it }
}
}
}
}
রচনা করুন
@Composable
fun LatestNewsScreen(
modifier: Modifier = Modifier,
viewModel: NewsViewModel = viewModel()
) {
Box(modifier.fillMaxSize()) {
if (viewModel.uiState.isFetchingArticles) {
CircularProgressIndicator(Modifier.align(Alignment.Center))
}
// Add other UI elements. For example, the list.
}
}
পর্দায় ত্রুটি দেখান
UI-তে ত্রুটিগুলি প্রদর্শন করা চলমান ক্রিয়াকলাপগুলি দেখানোর অনুরূপ কারণ এগুলি উভয়ই সহজেই বুলিয়ান মান দ্বারা উপস্থাপন করা হয় যা তাদের উপস্থিতি বা অনুপস্থিতি নির্দেশ করে৷ যাইহোক, ত্রুটিগুলির মধ্যে ব্যবহারকারীর কাছে ফিরে আসার জন্য একটি সম্পর্কিত বার্তা বা তাদের সাথে যুক্ত একটি ক্রিয়া অন্তর্ভুক্ত থাকতে পারে যা ব্যর্থ অপারেশনের পুনরায় চেষ্টা করে। অতএব, যখন একটি চলমান অপারেশন হয় লোড হচ্ছে বা লোড হচ্ছে না, ত্রুটির অবস্থাগুলিকে ডেটা ক্লাসের সাথে মডেল করা প্রয়োজন হতে পারে যা ত্রুটির প্রেক্ষাপটের জন্য উপযুক্ত মেটাডেটা হোস্ট করে।
উদাহরণস্বরূপ, পূর্ববর্তী বিভাগের উদাহরণটি বিবেচনা করুন যা নিবন্ধগুলি আনার সময় একটি অগ্রগতি বার দেখায়৷ যদি এই ক্রিয়াকলাপের ফলে একটি ত্রুটি হয়, তাহলে আপনি ব্যবহারকারীর কাছে এক বা একাধিক বার্তা প্রদর্শন করতে চাইতে পারেন যা বিশদভাবে কী ভুল হয়েছে।
data class Message(val id: Long, val message: String)
data class NewsUiState(
val userMessages: List<Message> = listOf(),
...
)
ত্রুটির বার্তাগুলি ব্যবহারকারীর কাছে স্ন্যাকবারগুলির মতো UI উপাদানগুলির আকারে উপস্থাপন করা যেতে পারে। কারণ এটি কীভাবে UI ইভেন্টগুলি তৈরি এবং ব্যবহার করা হয় তার সাথে সম্পর্কিত, আরও জানতে UI ইভেন্ট পৃষ্ঠাটি দেখুন।
থ্রেডিং এবং কনকারেন্সি
একটি ViewModel-এ সম্পাদিত যেকোনো কাজ প্রধান-নিরাপদ হওয়া উচিত—মূল থ্রেড থেকে কল করা নিরাপদ। এর কারণ হল ডেটা এবং ডোমেন স্তরগুলি একটি ভিন্ন থ্রেডে কাজ সরানোর জন্য দায়ী৷
যদি একটি ViewModel দীর্ঘ-চলমান ক্রিয়াকলাপ সম্পাদন করে, তাহলে সেই যুক্তিটিকে একটি পটভূমির থ্রেডে স্থানান্তর করার জন্যও এটি দায়ী৷ কোটলিন কোরোটিনগুলি সমসাময়িক ক্রিয়াকলাপগুলি পরিচালনা করার একটি দুর্দান্ত উপায় এবং জেটপ্যাক আর্কিটেকচার উপাদানগুলি তাদের জন্য অন্তর্নির্মিত সমর্থন সরবরাহ করে। অ্যান্ড্রয়েড অ্যাপ্লিকেশানগুলিতে কোরোটিনগুলি ব্যবহার করার বিষয়ে আরও জানতে, অ্যান্ড্রয়েডে কোটলিন কোরোটিনগুলি দেখুন৷
নেভিগেশন
অ্যাপ নেভিগেশন পরিবর্তন প্রায়ই ইভেন্ট মত নির্গমন দ্বারা চালিত হয়. উদাহরণস্বরূপ, একটি SignInViewModel
ক্লাস একটি সাইন-ইন করার পরে, UiState
একটি isSignedIn
ক্ষেত্র true
সেট থাকতে পারে। উপরের কনজিউম UI স্টেট বিভাগে কভার করা বিষয়গুলির মতোই এই জাতীয় ট্রিগারগুলি গ্রহণ করা উচিত, ব্যতীত যে খরচ বাস্তবায়নটি নেভিগেশন উপাদানে স্থগিত করা উচিত।
পেজিং
পেজিং লাইব্রেরি PagingData
নামক একটি প্রকারের সাথে UI-তে ব্যবহার করা হয়। কারণ PagingData
প্রতিনিধিত্ব করে এবং এমন আইটেম ধারণ করে যা সময়ের সাথে সাথে পরিবর্তিত হতে পারে - অন্য কথায়, এটি একটি অপরিবর্তনীয় প্রকার নয় - এটি একটি অপরিবর্তনীয় UI অবস্থায় উপস্থাপন করা উচিত নয়। পরিবর্তে, আপনার এটিকে ViewModel থেকে স্বাধীনভাবে এর নিজস্ব প্রবাহে প্রকাশ করা উচিত। এর একটি নির্দিষ্ট উদাহরণের জন্য অ্যান্ড্রয়েড পেজিং কোডল্যাব দেখুন।
অ্যানিমেশন
তরল এবং মসৃণ শীর্ষ-স্তরের নেভিগেশন ট্রানজিশন প্রদান করার জন্য, আপনি অ্যানিমেশন শুরু করার আগে দ্বিতীয় স্ক্রিনে ডেটা লোড করার জন্য অপেক্ষা করতে চাইতে পারেন। অ্যান্ড্রয়েড ভিউ ফ্রেমওয়ার্ক postponeEnterTransition()
এবং startPostponedEnterTransition()
API-এর সাথে খণ্ড গন্তব্যের মধ্যে স্থানান্তর বিলম্বিত করার জন্য হুক প্রদান করে। এই APIগুলি নিশ্চিত করার একটি উপায় সরবরাহ করে যে দ্বিতীয় স্ক্রিনে UI উপাদানগুলি (সাধারণত নেটওয়ার্ক থেকে আনা একটি চিত্র) UI সেই স্ক্রিনে রূপান্তরকে অ্যানিমেট করার আগে প্রদর্শিত হওয়ার জন্য প্রস্তুত। আরো বিশদ বিবরণ এবং বাস্তবায়ন সুনির্দিষ্টের জন্য, Android মোশন নমুনা দেখুন।
নমুনা
নিম্নলিখিত Google নমুনাগুলি UI স্তরের ব্যবহার প্রদর্শন করে৷ অনুশীলনে এই নির্দেশিকা দেখতে তাদের অন্বেষণ করুন:
আপনার জন্য প্রস্তাবিত
- দ্রষ্টব্য: জাভাস্ক্রিপ্ট বন্ধ থাকলে লিঙ্ক টেক্সট প্রদর্শিত হয়
- UI রাজ্য উত্পাদন
- রাজ্য ধারক এবং UI রাজ্য {:#mad-arch}
- অ্যাপ আর্কিটেকচারের জন্য গাইড