Android 9 (API স্তর 28) হিসাবে লোডারগুলিকে অবমূল্যায়ন করা হয়েছে৷ Activity
এবং Fragment
লাইফসাইকেল পরিচালনা করার সময় লোডিং ডেটা নিয়ে কাজ করার জন্য প্রস্তাবিত বিকল্পটি হল ViewModel
অবজেক্ট এবং LiveData
এর সংমিশ্রণ ব্যবহার করা। দেখুন মডেলগুলি লোডারগুলির মতো কনফিগারেশন পরিবর্তনগুলি থেকে বেঁচে থাকে তবে কম বয়লারপ্লেট কোড সহ৷ LiveData
ডেটা লোড করার একটি জীবনচক্র-সচেতন উপায় প্রদান করে যা আপনি একাধিক ভিউ মডেলে পুনরায় ব্যবহার করতে পারেন। আপনি MediatorLiveData
ব্যবহার করে LiveData
একত্রিত করতে পারেন। যেকোন পর্যবেক্ষণযোগ্য প্রশ্ন, যেমন একটি রুম ডাটাবেস থেকে, ডেটাতে পরিবর্তনগুলি পর্যবেক্ষণ করতে ব্যবহার করা যেতে পারে।
ViewModel
এবং LiveData
এমন পরিস্থিতিতেও উপলব্ধ যেখানে আপনার LoaderManager
এ অ্যাক্সেস নেই, যেমন একটি Service
। UI লাইফসাইকেল মোকাবেলা না করেই আপনার অ্যাপের প্রয়োজনীয় ডেটা অ্যাক্সেস করার জন্য দুটিকে একসাথে ব্যবহার করা একটি সহজ উপায় প্রদান করে। LiveData
সম্পর্কে আরও জানতে, LiveData
ওভারভিউ দেখুন। ViewModel
সম্পর্কে আরও জানতে, ViewModel
ওভারভিউ দেখুন।
লোডার API আপনাকে FragmentActivity
বা Fragment
প্রদর্শনের জন্য একটি সামগ্রী প্রদানকারী বা অন্যান্য ডেটা উত্স থেকে ডেটা লোড করতে দেয়৷
লোডার ছাড়া, আপনি যে সমস্যার সম্মুখীন হতে পারেন তার মধ্যে নিম্নলিখিতগুলি অন্তর্ভুক্ত রয়েছে:
- যদি আপনি সরাসরি কার্যকলাপ বা খণ্ডে ডেটা আনেন, তাহলে আপনার ব্যবহারকারীরা UI থ্রেড থেকে সম্ভাব্য ধীরগতির প্রশ্নগুলি সম্পাদন করার কারণে প্রতিক্রিয়াশীলতার অভাবের সম্মুখীন হন।
- আপনি যদি অন্য থ্রেড থেকে ডেটা আনেন, সম্ভবত
AsyncTask
এর সাথে, তাহলে আপনি বিভিন্ন কার্যকলাপ বা ফ্র্যাগমেন্ট লাইফসাইকেল ইভেন্ট, যেমনonDestroy()
এবং কনফিগারেশন পরিবর্তনের মাধ্যমে সেই থ্রেড এবং UI থ্রেড উভয়ই পরিচালনা করার জন্য দায়ী।
লোডাররা এই সমস্যাগুলি সমাধান করে এবং অন্যান্য সুবিধাগুলি অন্তর্ভুক্ত করে:
- ধীরগতির বা প্রতিক্রিয়াশীল UI রোধ করতে লোডারগুলি পৃথক থ্রেডে চলে।
- ঘটনা ঘটলে লোডার কলব্যাক পদ্ধতি প্রদান করে থ্রেড পরিচালনাকে সহজ করে।
- ডুপ্লিকেট ক্যোয়ারী রোধ করতে লোডারগুলি স্থির থাকে এবং কনফিগারেশন পরিবর্তন জুড়ে ফলাফল ক্যাশে করে।
- অন্তর্নিহিত ডেটা উৎসের পরিবর্তনের জন্য নিরীক্ষণের জন্য লোডাররা একজন পর্যবেক্ষককে প্রয়োগ করতে পারে। উদাহরণস্বরূপ,
CursorLoader
স্বয়ংক্রিয়ভাবে একটিContentObserver
নিবন্ধন করে যখন ডেটা পরিবর্তন হয় তখন একটি পুনরায় লোড ট্রিগার করতে।
লোডার API সারাংশ
একটি অ্যাপে লোডার ব্যবহার করার সময় একাধিক ক্লাস এবং ইন্টারফেস জড়িত থাকতে পারে। সেগুলি নিম্নলিখিত সারণীতে সংক্ষিপ্ত করা হয়েছে:
ক্লাস/ইন্টারফেস | বর্ণনা |
---|---|
LoaderManager | এক বা একাধিক Loader দৃষ্টান্ত পরিচালনার জন্য একটি FragmentActivity বা Fragment সাথে যুক্ত একটি বিমূর্ত শ্রেণী। প্রতি কার্যকলাপ বা খণ্ডের জন্য শুধুমাত্র একটি LoaderManager আছে, কিন্তু একটি LoaderManager একাধিক লোডার পরিচালনা করতে পারে। একটি একটি লোডার থেকে ডেটা লোড করা শুরু করতে, হয় |
LoaderManager.LoaderCallbacks | এই ইন্টারফেসে কলব্যাক পদ্ধতি রয়েছে যা লোডার ঘটনা ঘটলে বলা হয়। ইন্টারফেস তিনটি কলব্যাক পদ্ধতি সংজ্ঞায়িত করে:
initLoader() বা restartLoader() কল করেন তখন এটি নিবন্ধিত হয়। |
Loader | লোডার ডাটা লোডিং সঞ্চালন. এই ক্লাসটি বিমূর্ত এবং সমস্ত লোডারের জন্য বেস ক্লাস হিসাবে কাজ করে। আপনি সরাসরি Loader সাবক্লাস করতে পারেন বা বাস্তবায়নকে সহজ করতে নিম্নলিখিত অন্তর্নির্মিত সাবক্লাসগুলির মধ্যে একটি ব্যবহার করতে পারেন:
|
নিম্নলিখিত বিভাগগুলি আপনাকে দেখায় কিভাবে একটি অ্যাপ্লিকেশনে এই ক্লাস এবং ইন্টারফেসগুলি ব্যবহার করতে হয়।
একটি অ্যাপ্লিকেশনে লোডার ব্যবহার করুন
এই বিভাগে বর্ণনা করা হয়েছে কিভাবে একটি Android অ্যাপ্লিকেশনে লোডার ব্যবহার করতে হয়। একটি অ্যাপ্লিকেশন যা লোডার ব্যবহার করে সাধারণত নিম্নলিখিতগুলি অন্তর্ভুক্ত করে:
- একটি
FragmentActivity
বাFragment
। -
LoaderManager
এর একটি উদাহরণ। - একটি
ContentProvider
দ্বারা ব্যাক করা ডেটা লোড করার জন্য একটিCursorLoader
৷ বিকল্পভাবে, আপনি অন্য কোনো উৎস থেকে ডেটা লোড করতেLoader
বাAsyncTaskLoader
এর নিজস্ব সাবক্লাস প্রয়োগ করতে পারেন। -
LoaderManager.LoaderCallbacks
এর জন্য একটি বাস্তবায়ন। এখানেই আপনি নতুন লোডার তৈরি করেন এবং বিদ্যমান লোডারগুলিতে আপনার রেফারেন্স পরিচালনা করেন। - লোডারের ডেটা প্রদর্শনের একটি উপায়, যেমন একটি
SimpleCursorAdapter
। - একটি ডেটা উৎস, যেমন একটি
ContentProvider
, যখন একটিCursorLoader
ব্যবহার করে।
একটি লোডার শুরু করুন
LoaderManager
একটি FragmentActivity
বা Fragment
মধ্যে এক বা একাধিক Loader
দৃষ্টান্ত পরিচালনা করে। কার্যকলাপ বা খণ্ড প্রতি শুধুমাত্র একটি LoaderManager
আছে.
আপনি সাধারণত কার্যকলাপের onCreate()
পদ্ধতি বা খণ্ডের onCreate()
পদ্ধতির মধ্যে একটি Loader
শুরু করেন। আপনি নিম্নলিখিত হিসাবে এটি করবেন:
কোটলিন
supportLoaderManager.initLoader(0, null, this)
জাভা
// Prepare the loader. Either re-connect with an existing one, // or start a new one. getSupportLoaderManager().initLoader(0, null, this);
initLoader()
পদ্ধতি নিম্নলিখিত পরামিতি গ্রহণ করে:
- একটি অনন্য আইডি যা লোডারকে শনাক্ত করে। এই উদাহরণে, ID হল
0
। - নির্মাণের সময় লোডারকে সরবরাহ করার জন্য ঐচ্ছিক আর্গুমেন্ট (এই উদাহরণে
null
)। - একটি
LoaderManager.LoaderCallbacks
বাস্তবায়ন, যাLoaderManager
লোডার ইভেন্ট রিপোর্ট করতে কল করে। এই উদাহরণে, স্থানীয় ক্লাসLoaderManager.LoaderCallbacks
ইন্টারফেস প্রয়োগ করে, তাই এটি নিজেই একটি রেফারেন্স পাস করে,this
।
initLoader()
কল নিশ্চিত করে যে একটি লোডার চালু এবং সক্রিয়। এর দুটি সম্ভাব্য ফলাফল রয়েছে:
- যদি আইডি দ্বারা নির্দিষ্ট করা লোডারটি ইতিমধ্যেই বিদ্যমান থাকে তবে সর্বশেষ তৈরি লোডারটি পুনরায় ব্যবহার করা হয়।
- যদি ID দ্বারা নির্দিষ্ট করা লোডারটি বিদ্যমান না থাকে,
initLoader()
LoaderManager.LoaderCallbacks
পদ্ধতিonCreateLoader()
ট্রিগার করে। এখানেই আপনি একটি নতুন লোডারকে তাৎক্ষণিক এবং ফেরত দেওয়ার জন্য কোডটি প্রয়োগ করেন। আরও আলোচনার জন্য,onCreateLoader
সম্পর্কে বিভাগটি দেখুন।
উভয় ক্ষেত্রেই, প্রদত্ত LoaderManager.LoaderCallbacks
ইমপ্লিমেন্টেশন লোডারের সাথে যুক্ত থাকে এবং লোডার স্টেট পরিবর্তন হলে ডাকা হয়। যদি, এই কলের সময়ে, কলকারী তার শুরু অবস্থায় থাকে এবং অনুরোধ করা লোডারটি ইতিমধ্যেই বিদ্যমান থাকে এবং তার ডেটা তৈরি করে থাকে, তাহলে initLoader()
চলাকালীন সিস্টেম অবিলম্বে onLoadFinished()
কল করে। এটি ঘটার জন্য আপনাকে প্রস্তুত থাকতে হবে। এই কলব্যাকের আরও আলোচনার জন্য, onLoadFinished
সম্পর্কে বিভাগটি দেখুন।
initLoader()
পদ্ধতিটি তৈরি করা Loader
ফেরত দেয়, তবে আপনাকে এটির একটি রেফারেন্স ক্যাপচার করতে হবে না। LoaderManager
স্বয়ংক্রিয়ভাবে লোডারের জীবন পরিচালনা করে। LoaderManager
যখন প্রয়োজন হয় তখন লোড হওয়া শুরু করে এবং বন্ধ করে এবং লোডার এবং এর সাথে সম্পর্কিত বিষয়বস্তুর অবস্থা বজায় রাখে।
এটি বোঝায়, আপনি খুব কমই সরাসরি লোডারদের সাথে যোগাযোগ করেন। বিশেষ ঘটনা ঘটলে লোডিং প্রক্রিয়ায় হস্তক্ষেপ করার জন্য আপনি সাধারণত LoaderManager.LoaderCallbacks
পদ্ধতি ব্যবহার করেন। এই বিষয়ে আরো আলোচনার জন্য, LoaderManager কলব্যাকস বিভাগটি দেখুন।
একটি লোডার পুনরায় চালু করুন
আপনি যখন initLoader()
ব্যবহার করেন, যেমনটি পূর্ববর্তী বিভাগে দেখানো হয়েছে, এটি নির্দিষ্ট আইডি সহ একটি বিদ্যমান লোডার ব্যবহার করে যদি একটি থাকে। যদি না থাকে তবে এটি একটি তৈরি করে। কিন্তু কখনও কখনও আপনি আপনার পুরানো ডেটা বাতিল করে আবার শুরু করতে চান।
আপনার পুরানো ডেটা বাতিল করতে, restartLoader()
ব্যবহার করুন। উদাহরণ স্বরূপ, SearchView.OnQueryTextListener
এর নিম্নলিখিত বাস্তবায়ন ব্যবহারকারীর ক্যোয়ারী পরিবর্তিত হলে লোডার পুনরায় চালু করে। লোডারটিকে পুনরায় চালু করতে হবে যাতে এটি একটি নতুন কোয়েরি করতে সংশোধিত অনুসন্ধান ফিল্টার ব্যবহার করতে পারে।
কোটলিন
fun onQueryTextChanged(newText: String?): Boolean { // Called when the action bar search text has changed. Update // the search filter and restart the loader to do a new query // with this filter. curFilter = if (newText?.isNotEmpty() == true) newText else null supportLoaderManager.restartLoader(0, null, this) return true }
জাভা
public boolean onQueryTextChanged(String newText) { // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. curFilter = !TextUtils.isEmpty(newText) ? newText : null; getSupportLoaderManager().restartLoader(0, null, this); return true; }
LoaderManager কলব্যাক ব্যবহার করুন
LoaderManager.LoaderCallbacks
হল একটি কলব্যাক ইন্টারফেস যা একটি ক্লায়েন্টকে LoaderManager
এর সাথে যোগাযোগ করতে দেয়।
লোডার, বিশেষ করে CursorLoader
, বন্ধ হওয়ার পরে তাদের ডেটা ধরে রাখার আশা করা হয়। এটি অ্যাপ্লিকেশানগুলিকে তাদের ডেটা অ্যাক্টিভিটি বা ফ্র্যাগমেন্টের onStop()
এবং onStart()
পদ্ধতিতে রাখতে দেয়, যাতে ব্যবহারকারীরা যখন একটি অ্যাপ্লিকেশনে ফিরে আসে, তখন তাদের ডেটা পুনরায় লোড হওয়ার জন্য অপেক্ষা করতে না হয়।
আপনি কখন একটি নতুন লোডার তৈরি করতে হবে তা জানতে এবং লোডারের ডেটা ব্যবহার বন্ধ করার সময় হলে অ্যাপ্লিকেশনটিকে জানাতে LoaderManager.LoaderCallbacks
পদ্ধতিগুলি ব্যবহার করুন৷
LoaderManager.LoaderCallbacks
এই পদ্ধতিগুলি অন্তর্ভুক্ত করে:
-
onCreateLoader()
: প্রদত্ত আইডির জন্য একটি নতুনLoader
ইনস্ট্যান্টিয়েট করে এবং ফেরত দেয়।
-
onLoadFinished()
: পূর্বে তৈরি করা লোডার তার লোড শেষ করলে বলা হয়।
-
onLoaderReset()
: বলা হয় যখন পূর্বে তৈরি করা লোডার রিসেট করা হয়, ফলে এটির ডেটা অনুপলব্ধ হয়।
এই পদ্ধতিগুলি নিম্নলিখিত বিভাগে আরও বিশদে বর্ণনা করা হয়েছে।
onCreateLoader
আপনি যখন একটি লোডার অ্যাক্সেস করার চেষ্টা করেন, যেমন initLoader()
এর মাধ্যমে, এটি আইডি দ্বারা নির্দিষ্ট করা লোডার বিদ্যমান কিনা তা পরীক্ষা করে। যদি এটি না হয়, এটি LoaderManager.LoaderCallbacks
পদ্ধতি onCreateLoader()
ট্রিগার করে। এখানেই আপনি একটি নতুন লোডার তৈরি করেন। সাধারণত এটি একটি CursorLoader
, কিন্তু আপনি আপনার নিজস্ব Loader
সাবক্লাস বাস্তবায়ন করতে পারেন।
নিম্নলিখিত উদাহরণে, onCreateLoader()
কলব্যাক পদ্ধতিটি তার কনস্ট্রাক্টর পদ্ধতি ব্যবহার করে একটি CursorLoader
তৈরি করে, যার জন্য প্রয়োজন সম্পূর্ণ তথ্যের সেটের প্রয়োজন যা ContentProvider
এর কাছে একটি ক্যোয়ারী করার জন্য। বিশেষত, এটি নিম্নলিখিত প্রয়োজন:
- uri : সামগ্রী পুনরুদ্ধার করার জন্য URI।
- অভিক্ষেপ : কোন কলামগুলি ফেরত দিতে হবে তার একটি তালিকা।
null
পাস করা সমস্ত কলাম ফেরত দেয়, যা অদক্ষ। - নির্বাচন : একটি ফিল্টার ঘোষণা করে যে কোন সারিগুলি ফিরতে হবে, একটি SQL WHERE ক্লজ হিসাবে ফর্ম্যাট করা হয়েছে (WHERE নিজেই বাদ দিয়ে)।
null
পাস করা প্রদত্ত URI-এর জন্য সমস্ত সারি প্রদান করে। - SelectionArgs : আপনি যদি নির্বাচনের মধ্যে ?s অন্তর্ভুক্ত করেন, তাহলে সেগুলি সিলেকশনআর্গের মানের দ্বারা প্রতিস্থাপিত হবে যে ক্রমে সেগুলি নির্বাচনে প্রদর্শিত হবে। মানগুলি স্ট্রিং হিসাবে আবদ্ধ।
- sortOrder : সারিগুলিকে কিভাবে অর্ডার করতে হয়, একটি SQL ORDER BY ক্লজ হিসাবে ফর্ম্যাট করা হয় (নিজের দ্বারা ORDER বাদ দিয়ে)।
null
পাস করা ডিফল্ট সাজানোর ক্রম ব্যবহার করে, যা অক্রমহীন হতে পারে।
কোটলিন
// If non-null, this is the current filter the user has provided. private var curFilter: String? = null ... override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. val baseUri: Uri = if (curFilter != null) { Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, Uri.encode(curFilter)) } else { ContactsContract.Contacts.CONTENT_URI } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. val select: String = "((${Contacts.DISPLAY_NAME} NOTNULL) AND (" + "${Contacts.HAS_PHONE_NUMBER}=1) AND (" + "${Contacts.DISPLAY_NAME} != ''))" return (activity as? Context)?.let { context -> CursorLoader( context, baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, "${Contacts.DISPLAY_NAME} COLLATE LOCALIZED ASC" ) } ?: throw Exception("Activity cannot be null") }
জাভা
// If non-null, this is the current filter the user has provided. String curFilter; ... public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. Uri baseUri; if (curFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(curFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); }
অনলোড সমাপ্ত
এই পদ্ধতিটি বলা হয় যখন পূর্বে তৈরি করা লোডার তার লোড শেষ করে। এই পদ্ধতিটি নিশ্চিত করা হয় যে এই লোডারের জন্য সরবরাহ করা শেষ ডেটা প্রকাশের আগে কল করা হবে। এই মুহুর্তে, পুরানো ডেটার সমস্ত ব্যবহার মুছে ফেলুন, যেহেতু এটি প্রকাশিত হতে চলেছে। কিন্তু নিজেই ডেটা প্রকাশ করবেন না-লোডার এটির মালিক এবং এটির যত্ন নেয়।
লোডার ডেটা প্রকাশ করে যখন এটি জানে যে অ্যাপ্লিকেশনটি আর এটি ব্যবহার করছে না। উদাহরণস্বরূপ, যদি ডেটা একটি CursorLoader
থেকে একটি কার্সার হয়, তাহলে এটিতে close()
কল করবেন না। যদি কার্সারটি CursorAdapter
স্থাপন করা হয়, swapCursor()
পদ্ধতিটি ব্যবহার করুন যাতে পুরানো Cursor
বন্ধ না হয়, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
কোটলিন
private lateinit var adapter: SimpleCursorAdapter ... override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor?) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) adapter.swapCursor(data) }
জাভা
// This is the Adapter being used to display the list's data. SimpleCursorAdapter adapter; ... public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) adapter.swapCursor(data); }
অনলোডার রিসেট
এই পদ্ধতিটি বলা হয় যখন পূর্বে তৈরি করা লোডার রিসেট করা হয়, ফলে এটির ডেটা অনুপলব্ধ হয়। এই কলব্যাক আপনাকে জানতে দেয় যে ডেটা কখন প্রকাশিত হতে চলেছে যাতে আপনি এটি থেকে আপনার রেফারেন্স মুছে ফেলতে পারেন৷
এই বাস্তবায়নটি swapCursor()
null
এর মান দিয়ে কল করে:
কোটলিন
private lateinit var adapter: SimpleCursorAdapter ... override fun onLoaderReset(loader: Loader<Cursor>) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. adapter.swapCursor(null) }
জাভা
// This is the Adapter being used to display the list's data. SimpleCursorAdapter adapter; ... public void onLoaderReset(Loader<Cursor> loader) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. adapter.swapCursor(null); }
উদাহরণ
উদাহরণ স্বরূপ, এখানে একটি Fragment
সম্পূর্ণ বাস্তবায়ন রয়েছে যা পরিচিতি সামগ্রী প্রদানকারীর বিরুদ্ধে একটি প্রশ্নের ফলাফল ধারণকারী একটি ListView
প্রদর্শন করে। এটি প্রদানকারীর উপর ক্যোয়ারী পরিচালনা করতে একটি CursorLoader
ব্যবহার করে।
যেহেতু এই উদাহরণটি একটি ব্যবহারকারীর পরিচিতি অ্যাক্সেস করার জন্য একটি অ্যাপ্লিকেশন থেকে এসেছে, তাই এর ম্যানিফেস্টে অবশ্যই READ_CONTACTS
অনুমতি অন্তর্ভুক্ত করতে হবে।
কোটলিন
private val CONTACTS_SUMMARY_PROJECTION: Array<String> = arrayOf( Contacts._ID, Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY ) class CursorLoaderListFragment : ListFragment(), SearchView.OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> { // This is the Adapter being used to display the list's data. private lateinit var mAdapter: SimpleCursorAdapter // If non-null, this is the current filter the user has provided. private var curFilter: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Prepare the loader. Either re-connect with an existing one, // or start a new one. loaderManager.initLoader(0, null, this) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // Give some text to display if there is no data. In a real // application, this would come from a resource. setEmptyText("No phone numbers") // We have a menu item to show in action bar. setHasOptionsMenu(true) // Create an empty adapter we will use to display the loaded data. mAdapter = SimpleCursorAdapter(activity, android.R.layout.simple_list_item_2, null, arrayOf(Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS), intArrayOf(android.R.id.text1, android.R.id.text2), 0 ) listAdapter = mAdapter } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { // Place an action bar item for searching. menu.add("Search").apply { setIcon(android.R.drawable.ic_menu_search) setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) actionView = SearchView(activity).apply { setOnQueryTextListener(this@CursorLoaderListFragment) } } } override fun onQueryTextChange(newText: String?): Boolean { // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. curFilter = if (newText?.isNotEmpty() == true) newText else null loaderManager.restartLoader(0, null, this) return true } override fun onQueryTextSubmit(query: String): Boolean { // Don't care about this. return true } override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) { // Insert desired behavior here. Log.i("FragmentComplexList", "Item clicked: $id") } override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. val baseUri: Uri = if (curFilter != null) { Uri.withAppendedPath(Contacts.CONTENT_URI, Uri.encode(curFilter)) } else { Contacts.CONTENT_URI } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. val select: String = "((${Contacts.DISPLAY_NAME} NOTNULL) AND (" + "${Contacts.HAS_PHONE_NUMBER}=1) AND (" + "${Contacts.DISPLAY_NAME} != ''))" return (activity as? Context)?.let { context -> CursorLoader( context, baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, "${Contacts.DISPLAY_NAME} COLLATE LOCALIZED ASC" ) } ?: throw Exception("Activity cannot be null") } override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data) } override fun onLoaderReset(loader: Loader<Cursor>) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null) } }
জাভা
public static class CursorLoaderListFragment extends ListFragment implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> { // This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; // If non-null, this is the current filter the user has provided. String curFilter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this); } @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // Give some text to display if there is no data. In a real // application, this would come from a resource. setEmptyText("No phone numbers"); // We have a menu item to show in action bar. setHasOptionsMenu(true); // Create an empty adapter we will use to display the loaded data. mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_2, null, new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, new int[] { android.R.id.text1, android.R.id.text2 }, 0); setListAdapter(mAdapter); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // Place an action bar item for searching. MenuItem item = menu.add("Search"); item.setIcon(android.R.drawable.ic_menu_search); item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); SearchView sv = new SearchView(getActivity()); sv.setOnQueryTextListener(this); item.setActionView(sv); } public boolean onQueryTextChange(String newText) { // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. curFilter = !TextUtils.isEmpty(newText) ? newText : null; getLoaderManager().restartLoader(0, null, this); return true; } @Override public boolean onQueryTextSubmit(String query) { // Don't care about this. return true; } @Override public void onListItemClick(ListView l, View v, int position, long id) { // Insert desired behavior here. Log.i("FragmentComplexList", "Item clicked: " + id); } // These are the Contacts rows that we will retrieve. static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY, }; public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. Uri baseUri; if (curFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(curFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); } public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data); } public void onLoaderReset(Loader<Cursor> loader) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null); } }
আরো উদাহরণ
নিম্নলিখিত উদাহরণগুলি কীভাবে লোডার ব্যবহার করতে হয় তা ব্যাখ্যা করে:
- LoaderCursor : পূর্ববর্তী স্নিপেটের একটি সম্পূর্ণ সংস্করণ।
- পরিচিতিগুলির একটি তালিকা পুনরুদ্ধার করুন : একটি ওয়াকথ্রু যা পরিচিতি প্রদানকারীর থেকে ডেটা পুনরুদ্ধার করতে
CursorLoader
ব্যবহার করে। - LoaderThrottle : একটি বিষয়বস্তু প্রদানকারী যখন তার ডেটা পরিবর্তিত হয় তখন প্রশ্নের সংখ্যা কমাতে থ্রটলিং ব্যবহার করার একটি উদাহরণ।
Android 9 (API স্তর 28) হিসাবে লোডারগুলিকে অবমূল্যায়ন করা হয়েছে৷ Activity
এবং Fragment
লাইফসাইকেল পরিচালনা করার সময় লোডিং ডেটা নিয়ে কাজ করার জন্য প্রস্তাবিত বিকল্পটি হল ViewModel
অবজেক্ট এবং LiveData
এর সংমিশ্রণ ব্যবহার করা। দেখুন মডেলগুলি লোডারগুলির মতো কনফিগারেশন পরিবর্তনগুলি থেকে বেঁচে থাকে তবে কম বয়লারপ্লেট কোড সহ৷ LiveData
ডেটা লোড করার একটি জীবনচক্র-সচেতন উপায় প্রদান করে যা আপনি একাধিক ভিউ মডেলে পুনরায় ব্যবহার করতে পারেন। আপনি MediatorLiveData
ব্যবহার করে LiveData
একত্রিত করতে পারেন। যেকোন পর্যবেক্ষণযোগ্য প্রশ্ন, যেমন একটি রুম ডাটাবেস থেকে, ডেটাতে পরিবর্তনগুলি পর্যবেক্ষণ করতে ব্যবহার করা যেতে পারে।
ViewModel
এবং LiveData
এমন পরিস্থিতিতেও উপলব্ধ যেখানে আপনার LoaderManager
এ অ্যাক্সেস নেই, যেমন একটি Service
। UI লাইফসাইকেল মোকাবেলা না করেই আপনার অ্যাপের প্রয়োজনীয় ডেটা অ্যাক্সেস করার জন্য দুটিকে একসাথে ব্যবহার করা একটি সহজ উপায় প্রদান করে। LiveData
সম্পর্কে আরও জানতে, LiveData
ওভারভিউ দেখুন। ViewModel
সম্পর্কে আরও জানতে, ViewModel
ওভারভিউ দেখুন।
লোডার API আপনাকে FragmentActivity
বা Fragment
প্রদর্শনের জন্য একটি সামগ্রী প্রদানকারী বা অন্যান্য ডেটা উত্স থেকে ডেটা লোড করতে দেয়৷
লোডার ছাড়া, আপনি যে সমস্যার সম্মুখীন হতে পারেন তার মধ্যে নিম্নলিখিতগুলি অন্তর্ভুক্ত রয়েছে:
- যদি আপনি সরাসরি কার্যকলাপ বা খণ্ডে ডেটা আনেন, তাহলে আপনার ব্যবহারকারীরা UI থ্রেড থেকে সম্ভাব্য ধীরগতির প্রশ্নগুলি সম্পাদন করার কারণে প্রতিক্রিয়াশীলতার অভাবের সম্মুখীন হন।
- আপনি যদি অন্য থ্রেড থেকে ডেটা আনেন, সম্ভবত
AsyncTask
এর সাথে, তাহলে আপনি বিভিন্ন কার্যকলাপ বা ফ্র্যাগমেন্ট লাইফসাইকেল ইভেন্ট, যেমনonDestroy()
এবং কনফিগারেশন পরিবর্তনের মাধ্যমে সেই থ্রেড এবং UI থ্রেড উভয়ই পরিচালনা করার জন্য দায়ী।
লোডাররা এই সমস্যাগুলি সমাধান করে এবং অন্যান্য সুবিধাগুলি অন্তর্ভুক্ত করে:
- ধীরগতির বা প্রতিক্রিয়াশীল UI রোধ করতে লোডারগুলি পৃথক থ্রেডে চলে।
- ঘটনা ঘটলে লোডার কলব্যাক পদ্ধতি প্রদান করে থ্রেড পরিচালনাকে সহজ করে।
- ডুপ্লিকেট ক্যোয়ারী রোধ করতে লোডারগুলি স্থির থাকে এবং কনফিগারেশন পরিবর্তন জুড়ে ফলাফল ক্যাশে করে।
- অন্তর্নিহিত ডেটা উৎসের পরিবর্তনের জন্য নিরীক্ষণের জন্য লোডাররা একজন পর্যবেক্ষককে প্রয়োগ করতে পারে। উদাহরণস্বরূপ,
CursorLoader
স্বয়ংক্রিয়ভাবে একটিContentObserver
নিবন্ধন করে যখন ডেটা পরিবর্তন হয় তখন একটি পুনরায় লোড ট্রিগার করতে।
লোডার API সারাংশ
একটি অ্যাপে লোডার ব্যবহার করার সময় একাধিক ক্লাস এবং ইন্টারফেস জড়িত থাকতে পারে। সেগুলি নিম্নলিখিত সারণীতে সংক্ষিপ্ত করা হয়েছে:
ক্লাস/ইন্টারফেস | বর্ণনা |
---|---|
LoaderManager | এক বা একাধিক Loader দৃষ্টান্ত পরিচালনার জন্য একটি FragmentActivity বা Fragment সাথে যুক্ত একটি বিমূর্ত শ্রেণী। প্রতি কার্যকলাপ বা খণ্ডের জন্য শুধুমাত্র একটি LoaderManager আছে, কিন্তু একটি LoaderManager একাধিক লোডার পরিচালনা করতে পারে। একটি একটি লোডার থেকে ডেটা লোড করা শুরু করতে, হয় |
LoaderManager.LoaderCallbacks | এই ইন্টারফেসে কলব্যাক পদ্ধতি রয়েছে যা লোডার ঘটনা ঘটলে বলা হয়। ইন্টারফেস তিনটি কলব্যাক পদ্ধতি সংজ্ঞায়িত করে:
initLoader() বা restartLoader() কল করেন তখন এটি নিবন্ধিত হয়। |
Loader | লোডার ডাটা লোডিং সঞ্চালন. এই ক্লাসটি বিমূর্ত এবং সমস্ত লোডারের জন্য বেস ক্লাস হিসাবে কাজ করে। আপনি সরাসরি Loader সাবক্লাস করতে পারেন বা বাস্তবায়নকে সহজ করতে নিম্নলিখিত অন্তর্নির্মিত সাবক্লাসগুলির মধ্যে একটি ব্যবহার করতে পারেন:
|
নিম্নলিখিত বিভাগগুলি আপনাকে দেখায় কিভাবে একটি অ্যাপ্লিকেশনে এই ক্লাস এবং ইন্টারফেসগুলি ব্যবহার করতে হয়।
একটি অ্যাপ্লিকেশনে লোডার ব্যবহার করুন
এই বিভাগে বর্ণনা করা হয়েছে কিভাবে একটি Android অ্যাপ্লিকেশনে লোডার ব্যবহার করতে হয়। একটি অ্যাপ্লিকেশন যা লোডার ব্যবহার করে সাধারণত নিম্নলিখিতগুলি অন্তর্ভুক্ত করে:
- একটি
FragmentActivity
বাFragment
। -
LoaderManager
এর একটি উদাহরণ। - একটি
ContentProvider
দ্বারা ব্যাক করা ডেটা লোড করার জন্য একটিCursorLoader
৷ বিকল্পভাবে, আপনি অন্য কোনো উৎস থেকে ডেটা লোড করতেLoader
বাAsyncTaskLoader
এর নিজের সাবক্লাস প্রয়োগ করতে পারেন। -
LoaderManager.LoaderCallbacks
এর জন্য একটি বাস্তবায়ন। এখানেই আপনি নতুন লোডার তৈরি করেন এবং বিদ্যমান লোডারগুলিতে আপনার রেফারেন্স পরিচালনা করেন। - লোডারের ডেটা প্রদর্শনের একটি উপায়, যেমন একটি
SimpleCursorAdapter
। - একটি ডেটা উৎস, যেমন একটি
ContentProvider
, যখন একটিCursorLoader
ব্যবহার করে।
একটি লোডার শুরু করুন
LoaderManager
একটি FragmentActivity
বা Fragment
মধ্যে এক বা একাধিক Loader
দৃষ্টান্ত পরিচালনা করে। কার্যকলাপ বা খণ্ড প্রতি শুধুমাত্র একটি LoaderManager
আছে.
আপনি সাধারণত কার্যকলাপের onCreate()
পদ্ধতি বা খণ্ডের onCreate()
পদ্ধতির মধ্যে একটি Loader
শুরু করেন। আপনি নিম্নলিখিত হিসাবে এটি করবেন:
কোটলিন
supportLoaderManager.initLoader(0, null, this)
জাভা
// Prepare the loader. Either re-connect with an existing one, // or start a new one. getSupportLoaderManager().initLoader(0, null, this);
initLoader()
পদ্ধতি নিম্নলিখিত পরামিতি গ্রহণ করে:
- একটি অনন্য আইডি যা লোডারকে শনাক্ত করে। এই উদাহরণে, ID হল
0
। - নির্মাণের সময় লোডারকে সরবরাহ করার জন্য ঐচ্ছিক আর্গুমেন্ট (এই উদাহরণে
null
)। - একটি
LoaderManager.LoaderCallbacks
বাস্তবায়ন, যাLoaderManager
লোডার ইভেন্ট রিপোর্ট করতে কল করে। এই উদাহরণে, স্থানীয় ক্লাসLoaderManager.LoaderCallbacks
ইন্টারফেস প্রয়োগ করে, তাই এটি নিজেই একটি রেফারেন্স পাস করে,this
।
initLoader()
কল নিশ্চিত করে যে একটি লোডার চালু এবং সক্রিয়। এর দুটি সম্ভাব্য ফলাফল রয়েছে:
- যদি আইডি দ্বারা নির্দিষ্ট করা লোডারটি ইতিমধ্যেই বিদ্যমান থাকে তবে সর্বশেষ তৈরি লোডারটি পুনরায় ব্যবহার করা হয়।
- যদি ID দ্বারা নির্দিষ্ট করা লোডারটি বিদ্যমান না থাকে,
initLoader()
LoaderManager.LoaderCallbacks
পদ্ধতিonCreateLoader()
ট্রিগার করে। এখানেই আপনি একটি নতুন লোডারকে তাৎক্ষণিক এবং ফেরত দেওয়ার জন্য কোডটি প্রয়োগ করেন। আরও আলোচনার জন্য,onCreateLoader
সম্পর্কে বিভাগটি দেখুন।
উভয় ক্ষেত্রেই, প্রদত্ত LoaderManager.LoaderCallbacks
ইমপ্লিমেন্টেশন লোডারের সাথে যুক্ত থাকে এবং লোডার স্টেট পরিবর্তন হলে ডাকা হয়। যদি, এই কলের সময়ে, কলকারী তার শুরু অবস্থায় থাকে এবং অনুরোধ করা লোডারটি ইতিমধ্যেই বিদ্যমান থাকে এবং তার ডেটা তৈরি করে থাকে, তাহলে initLoader()
চলাকালীন সিস্টেম অবিলম্বে onLoadFinished()
কল করে। এটি ঘটার জন্য আপনাকে প্রস্তুত থাকতে হবে। এই কলব্যাকের আরও আলোচনার জন্য, onLoadFinished
সম্পর্কে বিভাগটি দেখুন।
initLoader()
পদ্ধতিটি তৈরি করা Loader
ফেরত দেয়, তবে আপনাকে এটির একটি রেফারেন্স ক্যাপচার করতে হবে না। LoaderManager
স্বয়ংক্রিয়ভাবে লোডারের জীবন পরিচালনা করে। LoaderManager
যখন প্রয়োজন হয় তখন লোড হওয়া শুরু করে এবং বন্ধ করে এবং লোডার এবং এর সাথে সম্পর্কিত বিষয়বস্তুর অবস্থা বজায় রাখে।
এটি বোঝায়, আপনি খুব কমই সরাসরি লোডারদের সাথে যোগাযোগ করেন। বিশেষ ঘটনা ঘটলে লোডিং প্রক্রিয়ায় হস্তক্ষেপ করার জন্য আপনি সাধারণত LoaderManager.LoaderCallbacks
পদ্ধতি ব্যবহার করেন। এই বিষয়ে আরো আলোচনার জন্য, LoaderManager কলব্যাকস বিভাগটি দেখুন।
একটি লোডার পুনরায় চালু করুন
আপনি যখন initLoader()
ব্যবহার করেন, যেমনটি পূর্ববর্তী বিভাগে দেখানো হয়েছে, এটি নির্দিষ্ট আইডি সহ একটি বিদ্যমান লোডার ব্যবহার করে যদি একটি থাকে। যদি না থাকে তবে এটি একটি তৈরি করে। কিন্তু কখনও কখনও আপনি আপনার পুরানো ডেটা বাতিল করে আবার শুরু করতে চান।
আপনার পুরানো ডেটা বাতিল করতে, restartLoader()
ব্যবহার করুন। উদাহরণ স্বরূপ, SearchView.OnQueryTextListener
এর নিম্নলিখিত বাস্তবায়ন ব্যবহারকারীর ক্যোয়ারী পরিবর্তিত হলে লোডার পুনরায় চালু করে। লোডারটিকে পুনরায় চালু করতে হবে যাতে এটি একটি নতুন কোয়েরি করতে সংশোধিত অনুসন্ধান ফিল্টার ব্যবহার করতে পারে।
কোটলিন
fun onQueryTextChanged(newText: String?): Boolean { // Called when the action bar search text has changed. Update // the search filter and restart the loader to do a new query // with this filter. curFilter = if (newText?.isNotEmpty() == true) newText else null supportLoaderManager.restartLoader(0, null, this) return true }
জাভা
public boolean onQueryTextChanged(String newText) { // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. curFilter = !TextUtils.isEmpty(newText) ? newText : null; getSupportLoaderManager().restartLoader(0, null, this); return true; }
LoaderManager কলব্যাক ব্যবহার করুন
LoaderManager.LoaderCallbacks
হল একটি কলব্যাক ইন্টারফেস যা একটি ক্লায়েন্টকে LoaderManager
এর সাথে যোগাযোগ করতে দেয়।
লোডার, বিশেষ করে CursorLoader
, বন্ধ হওয়ার পরে তাদের ডেটা ধরে রাখার আশা করা হয়। এটি অ্যাপ্লিকেশানগুলিকে তাদের ডেটা অ্যাক্টিভিটি বা ফ্র্যাগমেন্টের onStop()
এবং onStart()
পদ্ধতিতে রাখতে দেয়, যাতে ব্যবহারকারীরা যখন একটি অ্যাপ্লিকেশনে ফিরে আসে, তখন তাদের ডেটা পুনরায় লোড হওয়ার জন্য অপেক্ষা করতে না হয়।
আপনি কখন একটি নতুন লোডার তৈরি করতে হবে তা জানতে এবং লোডারের ডেটা ব্যবহার বন্ধ করার সময় হলে অ্যাপ্লিকেশনটিকে জানাতে LoaderManager.LoaderCallbacks
পদ্ধতিগুলি ব্যবহার করুন৷
LoaderManager.LoaderCallbacks
এই পদ্ধতিগুলি অন্তর্ভুক্ত করে:
-
onCreateLoader()
: প্রদত্ত আইডির জন্য একটি নতুনLoader
ইনস্ট্যান্টিয়েট করে এবং ফেরত দেয়।
-
onLoadFinished()
: পূর্বে তৈরি করা লোডার তার লোড শেষ করলে বলা হয়।
-
onLoaderReset()
: বলা হয় যখন পূর্বে তৈরি করা লোডার রিসেট করা হয়, ফলে এটির ডেটা অনুপলব্ধ হয়।
এই পদ্ধতিগুলি নিম্নলিখিত বিভাগে আরও বিশদে বর্ণনা করা হয়েছে।
onCreateLoader
আপনি যখন একটি লোডার অ্যাক্সেস করার চেষ্টা করেন, যেমন initLoader()
এর মাধ্যমে, এটি আইডি দ্বারা নির্দিষ্ট করা লোডার বিদ্যমান কিনা তা পরীক্ষা করে। যদি এটি না হয়, এটি LoaderManager.LoaderCallbacks
পদ্ধতি onCreateLoader()
ট্রিগার করে। এখানেই আপনি একটি নতুন লোডার তৈরি করেন। সাধারণত এটি একটি CursorLoader
, কিন্তু আপনি আপনার নিজস্ব Loader
সাবক্লাস বাস্তবায়ন করতে পারেন।
নিম্নলিখিত উদাহরণে, onCreateLoader()
কলব্যাক পদ্ধতিটি তার কনস্ট্রাক্টর পদ্ধতি ব্যবহার করে একটি CursorLoader
তৈরি করে, যার জন্য প্রয়োজন সম্পূর্ণ তথ্যের সেটের প্রয়োজন যা ContentProvider
এর কাছে একটি ক্যোয়ারী করার জন্য। বিশেষত, এটি নিম্নলিখিত প্রয়োজন:
- uri : সামগ্রী পুনরুদ্ধার করার জন্য URI।
- অভিক্ষেপ : কোন কলামগুলি ফেরত দিতে হবে তার একটি তালিকা।
null
পাস করা সমস্ত কলাম ফেরত দেয়, যা অদক্ষ। - নির্বাচন : একটি ফিল্টার ঘোষণা করে যে কোন সারিগুলি ফিরতে হবে, একটি SQL WHERE ক্লজ হিসাবে ফর্ম্যাট করা হয়েছে (WHERE নিজেই বাদ দিয়ে)।
null
পাস করা প্রদত্ত URI-এর জন্য সমস্ত সারি প্রদান করে। - SelectionArgs : আপনি যদি নির্বাচনের মধ্যে ?s অন্তর্ভুক্ত করেন, তাহলে সেগুলি সিলেকশনআর্গের মানের দ্বারা প্রতিস্থাপিত হবে যে ক্রমে সেগুলি নির্বাচনে প্রদর্শিত হবে। মানগুলি স্ট্রিং হিসাবে আবদ্ধ।
- sortOrder : সারিগুলিকে কিভাবে অর্ডার করতে হয়, একটি SQL ORDER BY ক্লজ হিসাবে ফর্ম্যাট করা হয় (নিজের দ্বারা ORDER বাদ দিয়ে)।
null
পাস করা ডিফল্ট সাজানোর ক্রম ব্যবহার করে, যা অক্রমহীন হতে পারে।
কোটলিন
// If non-null, this is the current filter the user has provided. private var curFilter: String? = null ... override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. val baseUri: Uri = if (curFilter != null) { Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_URI, Uri.encode(curFilter)) } else { ContactsContract.Contacts.CONTENT_URI } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. val select: String = "((${Contacts.DISPLAY_NAME} NOTNULL) AND (" + "${Contacts.HAS_PHONE_NUMBER}=1) AND (" + "${Contacts.DISPLAY_NAME} != ''))" return (activity as? Context)?.let { context -> CursorLoader( context, baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, "${Contacts.DISPLAY_NAME} COLLATE LOCALIZED ASC" ) } ?: throw Exception("Activity cannot be null") }
জাভা
// If non-null, this is the current filter the user has provided. String curFilter; ... public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. Uri baseUri; if (curFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(curFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); }
অনলোড সমাপ্ত
এই পদ্ধতিটি বলা হয় যখন পূর্বে তৈরি করা লোডার তার লোড শেষ করে। এই পদ্ধতিটি নিশ্চিত করা হয় যে এই লোডারের জন্য সরবরাহ করা শেষ ডেটা প্রকাশের আগে কল করা হবে। এই মুহুর্তে, পুরানো ডেটার সমস্ত ব্যবহার মুছে ফেলুন, যেহেতু এটি প্রকাশিত হতে চলেছে। কিন্তু নিজেই ডেটা প্রকাশ করবেন না-লোডার এটির মালিক এবং এটির যত্ন নেয়।
লোডার ডেটা প্রকাশ করে যখন এটি জানে যে অ্যাপ্লিকেশনটি আর এটি ব্যবহার করছে না। উদাহরণস্বরূপ, যদি ডেটা একটি CursorLoader
থেকে একটি কার্সার হয়, তাহলে এটিতে close()
কল করবেন না। যদি কার্সারটি CursorAdapter
স্থাপন করা হয়, swapCursor()
পদ্ধতিটি ব্যবহার করুন যাতে পুরানো Cursor
বন্ধ না হয়, যেমনটি নিম্নলিখিত উদাহরণে দেখানো হয়েছে:
কোটলিন
private lateinit var adapter: SimpleCursorAdapter ... override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor?) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) adapter.swapCursor(data) }
জাভা
// This is the Adapter being used to display the list's data. SimpleCursorAdapter adapter; ... public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) adapter.swapCursor(data); }
অনলোডার রিসেট
এই পদ্ধতিটি বলা হয় যখন পূর্বে তৈরি করা লোডার রিসেট করা হয়, ফলে এটির ডেটা অনুপলব্ধ হয়। এই কলব্যাক আপনাকে জানতে দেয় যে ডেটা কখন প্রকাশিত হতে চলেছে যাতে আপনি এটি থেকে আপনার রেফারেন্স মুছে ফেলতে পারেন৷
এই বাস্তবায়নটি swapCursor()
null
এর মান দিয়ে কল করে:
কোটলিন
private lateinit var adapter: SimpleCursorAdapter ... override fun onLoaderReset(loader: Loader<Cursor>) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. adapter.swapCursor(null) }
জাভা
// This is the Adapter being used to display the list's data. SimpleCursorAdapter adapter; ... public void onLoaderReset(Loader<Cursor> loader) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. adapter.swapCursor(null); }
উদাহরণ
উদাহরণ স্বরূপ, এখানে একটি Fragment
সম্পূর্ণ বাস্তবায়ন রয়েছে যা পরিচিতি সামগ্রী প্রদানকারীর বিরুদ্ধে একটি প্রশ্নের ফলাফল ধারণকারী একটি ListView
প্রদর্শন করে। এটি প্রদানকারীর উপর ক্যোয়ারী পরিচালনা করতে একটি CursorLoader
ব্যবহার করে।
যেহেতু এই উদাহরণটি একটি ব্যবহারকারীর পরিচিতি অ্যাক্সেস করার জন্য একটি অ্যাপ্লিকেশন থেকে এসেছে, তাই এর ম্যানিফেস্টে অবশ্যই READ_CONTACTS
অনুমতি অন্তর্ভুক্ত করতে হবে।
কোটলিন
private val CONTACTS_SUMMARY_PROJECTION: Array<String> = arrayOf( Contacts._ID, Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY ) class CursorLoaderListFragment : ListFragment(), SearchView.OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> { // This is the Adapter being used to display the list's data. private lateinit var mAdapter: SimpleCursorAdapter // If non-null, this is the current filter the user has provided. private var curFilter: String? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // Prepare the loader. Either re-connect with an existing one, // or start a new one. loaderManager.initLoader(0, null, this) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) // Give some text to display if there is no data. In a real // application, this would come from a resource. setEmptyText("No phone numbers") // We have a menu item to show in action bar. setHasOptionsMenu(true) // Create an empty adapter we will use to display the loaded data. mAdapter = SimpleCursorAdapter(activity, android.R.layout.simple_list_item_2, null, arrayOf(Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS), intArrayOf(android.R.id.text1, android.R.id.text2), 0 ) listAdapter = mAdapter } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { // Place an action bar item for searching. menu.add("Search").apply { setIcon(android.R.drawable.ic_menu_search) setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM) actionView = SearchView(activity).apply { setOnQueryTextListener(this@CursorLoaderListFragment) } } } override fun onQueryTextChange(newText: String?): Boolean { // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. curFilter = if (newText?.isNotEmpty() == true) newText else null loaderManager.restartLoader(0, null, this) return true } override fun onQueryTextSubmit(query: String): Boolean { // Don't care about this. return true } override fun onListItemClick(l: ListView, v: View, position: Int, id: Long) { // Insert desired behavior here. Log.i("FragmentComplexList", "Item clicked: $id") } override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. val baseUri: Uri = if (curFilter != null) { Uri.withAppendedPath(Contacts.CONTENT_URI, Uri.encode(curFilter)) } else { Contacts.CONTENT_URI } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. val select: String = "((${Contacts.DISPLAY_NAME} NOTNULL) AND (" + "${Contacts.HAS_PHONE_NUMBER}=1) AND (" + "${Contacts.DISPLAY_NAME} != ''))" return (activity as? Context)?.let { context -> CursorLoader( context, baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, "${Contacts.DISPLAY_NAME} COLLATE LOCALIZED ASC" ) } ?: throw Exception("Activity cannot be null") } override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data) } override fun onLoaderReset(loader: Loader<Cursor>) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null) } }
জাভা
public static class CursorLoaderListFragment extends ListFragment implements OnQueryTextListener, LoaderManager.LoaderCallbacks<Cursor> { // This is the Adapter being used to display the list's data. SimpleCursorAdapter mAdapter; // If non-null, this is the current filter the user has provided. String curFilter; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // Prepare the loader. Either re-connect with an existing one, // or start a new one. getLoaderManager().initLoader(0, null, this); } @Override public void onViewCreated(@NonNull View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); // Give some text to display if there is no data. In a real // application, this would come from a resource. setEmptyText("No phone numbers"); // We have a menu item to show in action bar. setHasOptionsMenu(true); // Create an empty adapter we will use to display the loaded data. mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_2, null, new String[] { Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS }, new int[] { android.R.id.text1, android.R.id.text2 }, 0); setListAdapter(mAdapter); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { // Place an action bar item for searching. MenuItem item = menu.add("Search"); item.setIcon(android.R.drawable.ic_menu_search); item.setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); SearchView sv = new SearchView(getActivity()); sv.setOnQueryTextListener(this); item.setActionView(sv); } public boolean onQueryTextChange(String newText) { // Called when the action bar search text has changed. Update // the search filter, and restart the loader to do a new query // with this filter. curFilter = !TextUtils.isEmpty(newText) ? newText : null; getLoaderManager().restartLoader(0, null, this); return true; } @Override public boolean onQueryTextSubmit(String query) { // Don't care about this. return true; } @Override public void onListItemClick(ListView l, View v, int position, long id) { // Insert desired behavior here. Log.i("FragmentComplexList", "Item clicked: " + id); } // These are the Contacts rows that we will retrieve. static final String[] CONTACTS_SUMMARY_PROJECTION = new String[] { Contacts._ID, Contacts.DISPLAY_NAME, Contacts.CONTACT_STATUS, Contacts.CONTACT_PRESENCE, Contacts.PHOTO_ID, Contacts.LOOKUP_KEY, }; public Loader<Cursor> onCreateLoader(int id, Bundle args) { // This is called when a new Loader needs to be created. This // sample only has one Loader, so we don't care about the ID. // First, pick the base URI to use depending on whether we are // currently filtering. Uri baseUri; if (curFilter != null) { baseUri = Uri.withAppendedPath(Contacts.CONTENT_FILTER_URI, Uri.encode(curFilter)); } else { baseUri = Contacts.CONTENT_URI; } // Now create and return a CursorLoader that will take care of // creating a Cursor for the data being displayed. String select = "((" + Contacts.DISPLAY_NAME + " NOTNULL) AND (" + Contacts.HAS_PHONE_NUMBER + "=1) AND (" + Contacts.DISPLAY_NAME + " != '' ))"; return new CursorLoader(getActivity(), baseUri, CONTACTS_SUMMARY_PROJECTION, select, null, Contacts.DISPLAY_NAME + " COLLATE LOCALIZED ASC"); } public void onLoadFinished(Loader<Cursor> loader, Cursor data) { // Swap the new cursor in. (The framework will take care of closing the // old cursor once we return.) mAdapter.swapCursor(data); } public void onLoaderReset(Loader<Cursor> loader) { // This is called when the last Cursor provided to onLoadFinished() // above is about to be closed. We need to make sure we are no // longer using it. mAdapter.swapCursor(null); } }
আরো উদাহরণ
নিম্নলিখিত উদাহরণগুলি কীভাবে লোডার ব্যবহার করতে হয় তা ব্যাখ্যা করে:
- LoaderCursor : পূর্ববর্তী স্নিপেটের একটি সম্পূর্ণ সংস্করণ।
- পরিচিতিগুলির একটি তালিকা পুনরুদ্ধার করুন : একটি ওয়াকথ্রু যা পরিচিতি প্রদানকারীর থেকে ডেটা পুনরুদ্ধার করতে
CursorLoader
ব্যবহার করে। - LoaderThrottle : একটি বিষয়বস্তু প্রদানকারী যখন তার ডেটা পরিবর্তিত হয় তখন প্রশ্নের সংখ্যা কমাতে থ্রটলিং ব্যবহার করার একটি উদাহরণ।