একটি বিষয়বস্তু প্রদানকারী তৈরি করুন

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

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

এই পৃষ্ঠাটিতে একটি বিষয়বস্তু প্রদানকারী তৈরির প্রাথমিক প্রক্রিয়া এবং ব্যবহার করার জন্য API-এর একটি তালিকা রয়েছে৷

আপনি নির্মাণ শুরু করার আগে

আপনি একটি প্রদানকারী নির্মাণ শুরু করার আগে, নিম্নলিখিত বিবেচনা করুন:

  • আপনি একটি বিষয়বস্তু প্রদানকারী প্রয়োজন কিনা তা স্থির করুন. আপনি যদি নিম্নলিখিত বৈশিষ্ট্যগুলির মধ্যে এক বা একাধিক প্রদান করতে চান তবে আপনাকে একটি সামগ্রী প্রদানকারী তৈরি করতে হবে:
    • আপনি অন্যান্য অ্যাপ্লিকেশনগুলিতে জটিল ডেটা বা ফাইলগুলি অফার করতে চান৷
    • আপনি ব্যবহারকারীদের আপনার অ্যাপ থেকে জটিল ডেটা অন্য অ্যাপে কপি করতে দিতে চান।
    • আপনি অনুসন্ধান কাঠামো ব্যবহার করে কাস্টম অনুসন্ধান পরামর্শ প্রদান করতে চান।
    • আপনি উইজেটগুলিতে আপনার অ্যাপ্লিকেশন ডেটা প্রকাশ করতে চান৷
    • আপনি AbstractThreadedSyncAdapter , CursorAdapter , বা CursorLoader ক্লাসগুলি বাস্তবায়ন করতে চান৷

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

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

পরবর্তী, আপনার প্রদানকারী তৈরি করতে এই পদক্ষেপগুলি অনুসরণ করুন:

  1. আপনার ডেটার জন্য কাঁচা স্টোরেজ ডিজাইন করুন। একটি বিষয়বস্তু প্রদানকারী দুটি উপায়ে ডেটা অফার করে:
    ফাইল ডেটা
    ডেটা যা সাধারণত ফাইলগুলিতে যায়, যেমন ফটো, অডিও বা ভিডিও৷ আপনার অ্যাপ্লিকেশনের ব্যক্তিগত জায়গায় ফাইল সংরক্ষণ করুন. অন্য অ্যাপ্লিকেশন থেকে একটি ফাইলের জন্য একটি অনুরোধের প্রতিক্রিয়া হিসাবে, আপনার প্রদানকারী ফাইলটিতে একটি হ্যান্ডেল অফার করতে পারে।
    "স্ট্রাকচার্ড" ডেটা
    ডেটা যা সাধারণত একটি ডাটাবেস, অ্যারে বা অনুরূপ কাঠামোতে যায়। সারি এবং কলামের টেবিলের সাথে সামঞ্জস্যপূর্ণ একটি ফর্মে ডেটা সংরক্ষণ করুন। একটি সারি একটি সত্তাকে প্রতিনিধিত্ব করে, যেমন একটি ব্যক্তি বা তালিকার একটি আইটেম। একটি কলাম সত্তার জন্য কিছু ডেটা উপস্থাপন করে, যেমন একজন ব্যক্তির নাম বা একটি আইটেমের মূল্য। এই ধরনের ডেটা সঞ্চয় করার একটি সাধারণ উপায় হল একটি SQLite ডাটাবেসে, কিন্তু আপনি যেকোনো ধরনের স্থায়ী স্টোরেজ ব্যবহার করতে পারেন। অ্যান্ড্রয়েড সিস্টেমে উপলব্ধ স্টোরেজ প্রকারগুলি সম্পর্কে আরও জানতে, ডিজাইন ডেটা স্টোরেজ বিভাগটি দেখুন।
  2. ContentProvider ক্লাস এবং এর প্রয়োজনীয় পদ্ধতিগুলির একটি সুনির্দিষ্ট বাস্তবায়ন সংজ্ঞায়িত করুন। এই ক্লাসটি আপনার ডেটা এবং বাকি অ্যান্ড্রয়েড সিস্টেমের মধ্যে ইন্টারফেস। এই ক্লাস সম্পর্কে আরও তথ্যের জন্য, বিষয়বস্তু প্রদানকারী শ্রেণির প্রয়োগ করুন বিভাগটি দেখুন।
  3. প্রদানকারীর অথরিটি স্ট্রিং, কন্টেন্ট ইউআরআই এবং কলামের নাম নির্ধারণ করুন। আপনি যদি চান যে প্রদানকারীর অ্যাপ্লিকেশনটি ইন্টেন্টগুলি পরিচালনা করতে পারে, তবে অভিপ্রায় ক্রিয়া, অতিরিক্ত ডেটা এবং পতাকাগুলিও সংজ্ঞায়িত করুন৷ আপনার ডেটা অ্যাক্সেস করতে চায় এমন অ্যাপ্লিকেশনগুলির জন্য আপনার প্রয়োজনীয় অনুমতিগুলিও সংজ্ঞায়িত করুন৷ একটি পৃথক চুক্তি শ্রেণীতে ধ্রুবক হিসাবে এই সমস্ত মান সংজ্ঞায়িত বিবেচনা করুন। পরে, আপনি এই ক্লাসটিকে অন্যান্য বিকাশকারীদের কাছে প্রকাশ করতে পারেন। কন্টেন্ট ইউআরআই সম্পর্কে আরও তথ্যের জন্য, ডিজাইন কন্টেন্ট ইউআরআই বিভাগটি দেখুন। অভিপ্রায় সম্পর্কে আরও তথ্যের জন্য, ইন্টেন্টস এবং ডেটা অ্যাক্সেস বিভাগটি দেখুন।
  4. অন্যান্য ঐচ্ছিক অংশ যোগ করুন, যেমন নমুনা ডেটা বা AbstractThreadedSyncAdapter এর বাস্তবায়ন যা প্রদানকারী এবং ক্লাউড-ভিত্তিক ডেটার মধ্যে ডেটা সিঙ্ক্রোনাইজ করতে পারে।

ডিজাইন ডেটা স্টোরেজ

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

এগুলি অ্যান্ড্রয়েডে উপলব্ধ কিছু ডেটা স্টোরেজ প্রযুক্তি:

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

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

  • ফাইল ডেটা সংরক্ষণের জন্য, অ্যান্ড্রয়েডের বিভিন্ন ধরনের ফাইল-ভিত্তিক API রয়েছে। ফাইল স্টোরেজ সম্পর্কে আরও জানতে, ডেটা এবং ফাইল স্টোরেজ ওভারভিউ পড়ুন। আপনি যদি এমন একটি প্রদানকারী ডিজাইন করছেন যা মিডিয়া-সম্পর্কিত ডেটা যেমন সঙ্গীত বা ভিডিও অফার করে, তাহলে আপনার কাছে এমন একটি প্রদানকারী থাকতে পারে যা টেবিল ডেটা এবং ফাইলগুলিকে একত্রিত করে৷
  • বিরল ক্ষেত্রে, আপনি একটি একক অ্যাপ্লিকেশনের জন্য একাধিক বিষয়বস্তু প্রদানকারী প্রয়োগ করে উপকৃত হতে পারেন। উদাহরণস্বরূপ, আপনি একটি বিষয়বস্তু প্রদানকারী ব্যবহার করে একটি উইজেটের সাথে কিছু ডেটা ভাগ করতে এবং অন্যান্য অ্যাপ্লিকেশনগুলির সাথে ভাগ করার জন্য ডেটার একটি ভিন্ন সেট প্রকাশ করতে চাইতে পারেন৷
  • নেটওয়ার্ক-ভিত্তিক ডেটা নিয়ে কাজ করার জন্য, java.net এবং android.net এ ক্লাস ব্যবহার করুন। আপনি একটি স্থানীয় ডেটা স্টোর যেমন একটি ডাটাবেসের সাথে নেটওয়ার্ক-ভিত্তিক ডেটা সিঙ্ক্রোনাইজ করতে পারেন এবং তারপরে টেবিল বা ফাইল হিসাবে ডেটা অফার করতে পারেন।

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

ডেটা ডিজাইন বিবেচনা

আপনার প্রদানকারীর ডেটা স্ট্রাকচার ডিজাইন করার জন্য এখানে কিছু টিপস রয়েছে:

  • সারণী ডেটাতে সর্বদা একটি "প্রাথমিক কী" কলাম থাকতে হবে যা প্রদানকারী প্রতিটি সারির জন্য একটি অনন্য সাংখ্যিক মান হিসাবে বজায় রাখে। আপনি এই মানটি অন্যান্য সারণির সাথে সম্পর্কিত সারির সাথে লিঙ্ক করতে ব্যবহার করতে পারেন (এটি "বিদেশী কী" হিসাবে ব্যবহার করে)। যদিও আপনি এই কলামের জন্য যেকোন নাম ব্যবহার করতে পারেন, BaseColumns._ID ব্যবহার করা হল সর্বোত্তম পছন্দ, কারণ একটি ListView এর সাথে প্রোভাইডার কোয়েরির ফলাফল লিঙ্ক করার জন্য পুনরুদ্ধার করা কলামগুলির মধ্যে একটির প্রয়োজন _ID
  • আপনি যদি বিটম্যাপ ইমেজ বা ফাইল-ভিত্তিক ডেটার অন্যান্য খুব বড় টুকরা প্রদান করতে চান, একটি ফাইলে ডেটা সংরক্ষণ করুন এবং তারপর এটি সরাসরি টেবিলে সংরক্ষণ করার পরিবর্তে পরোক্ষভাবে প্রদান করুন। আপনি যদি এটি করেন তবে আপনাকে আপনার সরবরাহকারীর ব্যবহারকারীদের বলতে হবে যে তাদের ডেটা অ্যাক্সেস করার জন্য একটি ContentResolver ফাইল পদ্ধতি ব্যবহার করতে হবে।
  • বাইনারি লার্জ অবজেক্ট (BLOB) ডেটা টাইপ ব্যবহার করুন ডেটা সঞ্চয় করতে যা আকারে পরিবর্তিত হয় বা একটি ভিন্ন কাঠামো রয়েছে। উদাহরণস্বরূপ, আপনি একটি প্রোটোকল বাফার বা JSON কাঠামো সংরক্ষণ করতে একটি BLOB কলাম ব্যবহার করতে পারেন।

    আপনি একটি স্কিমা-স্বাধীন টেবিল বাস্তবায়ন করতে একটি BLOB ব্যবহার করতে পারেন। এই ধরনের টেবিলে, আপনি একটি প্রাথমিক কী কলাম, একটি MIME টাইপ কলাম এবং এক বা একাধিক জেনেরিক কলামকে BLOB হিসাবে সংজ্ঞায়িত করেন। BLOB কলামের ডেটার অর্থ MIME টাইপ কলামের মান দ্বারা নির্দেশিত হয়। এটি আপনাকে একই টেবিলে বিভিন্ন ধরনের সারি সংরক্ষণ করতে দেয়। পরিচিতি প্রদানকারীর "ডেটা" টেবিল ContactsContract.Data একটি স্কিমা-স্বাধীন টেবিলের উদাহরণ।

ডিজাইন কন্টেন্ট URI

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

বিষয়বস্তু URI সম্পর্কে তথ্যের জন্য, বিষয়বস্তু প্রদানকারীর মৌলিক বিষয়গুলি দেখুন।

একটি কর্তৃপক্ষ নকশা

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

উদাহরণস্বরূপ, যদি আপনার অ্যান্ড্রয়েড প্যাকেজের নাম হয় com.example.<appname> , তাহলে আপনার প্রদানকারীকে com.example.<appname>.provider এর অথরিটি দিন।

একটি পথ কাঠামো ডিজাইন করুন

বিকাশকারীরা সাধারণত পৃথক টেবিলের দিকে নির্দেশ করে এমন পাথ যুক্ত করে কর্তৃপক্ষের কাছ থেকে সামগ্রী URI তৈরি করে। উদাহরণ স্বরূপ, যদি আপনার দুটি টেবিল থাকে, table1 এবং table2 , তাহলে আপনি পূর্ববর্তী উদাহরণ থেকে com.example.<appname>.provider/table1 এবং com.example.<appname>.provider/table2 । পাথগুলি একটি একক অংশে সীমাবদ্ধ নয় এবং পথের প্রতিটি স্তরের জন্য একটি টেবিল থাকতে হবে না।

কন্টেন্ট ইউআরআই আইডিগুলি পরিচালনা করুন

নিয়ম অনুসারে, প্রদানকারীরা URI-এর শেষে সারির জন্য একটি ID মান সহ একটি বিষয়বস্তু URI গ্রহণ করে একটি টেবিলের একটি একক সারিতে অ্যাক্সেস অফার করে। এছাড়াও নিয়ম অনুসারে, সরবরাহকারীরা টেবিলের _ID কলামের সাথে ID মান মেলে এবং মেলে এমন সারির বিপরীতে অনুরোধ করা অ্যাক্সেস সম্পাদন করে।

এই কনভেনশনটি একটি প্রদানকারীকে অ্যাক্সেস করার জন্য অ্যাপগুলির জন্য একটি সাধারণ ডিজাইন প্যাটার্নের সুবিধা দেয়৷ অ্যাপটি প্রদানকারীর বিরুদ্ধে একটি ক্যোয়ারী করে এবং একটি CursorAdapter ব্যবহার করে একটি ListView এ ফলাফল Cursor প্রদর্শন করে। CursorAdapter সংজ্ঞার জন্য Cursor কলামগুলির একটি _ID হতে হবে

ব্যবহারকারী তারপরে ডেটা দেখতে বা পরিবর্তন করার জন্য UI থেকে প্রদর্শিত সারিগুলির মধ্যে একটি বেছে নেয়। অ্যাপটি ListView কে সমর্থনকারী Cursor থেকে সংশ্লিষ্ট সারি পায়, এই সারির জন্য _ID মান পায়, এটিকে কন্টেন্ট URI-তে যুক্ত করে এবং প্রদানকারীর কাছে অ্যাক্সেসের অনুরোধ পাঠায়। প্রদানকারী তারপর ব্যবহারকারীর বাছাই করা সঠিক সারিটির বিপরীতে প্রশ্ন বা পরিবর্তন করতে পারে।

বিষয়বস্তু URI নিদর্শন

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

একটি বিষয়বস্তু URI প্যাটার্ন ওয়াইল্ডকার্ড অক্ষর ব্যবহার করে সামগ্রী URI-এর সাথে মেলে:

  • * যে কোনো দৈর্ঘ্যের কোনো বৈধ অক্ষরের একটি স্ট্রিংয়ের সাথে মেলে।
  • # যেকোনো দৈর্ঘ্যের সাংখ্যিক অক্ষরের একটি স্ট্রিং মেলে।

কন্টেন্ট ইউআরআই হ্যান্ডলিং ডিজাইন এবং কোডিং করার উদাহরণ হিসাবে, com.example.app.provider কর্তৃপক্ষের সাথে একজন প্রদানকারীর কথা বিবেচনা করুন যেটি টেবিলের দিকে নির্দেশ করে নিম্নলিখিত বিষয়বস্তু URIগুলিকে চিনতে পারে:

  • content://com.example.app.provider/table1 : table1 নামক একটি টেবিল।
  • content://com.example.app.provider/table2/dataset1 : dataset1 নামে একটি টেবিল।
  • content://com.example.app.provider/table2/dataset2 : dataset2 নামে একটি টেবিল।
  • content://com.example.app.provider/table3 : table3 নামক একটি টেবিল।

সরবরাহকারী এই বিষয়বস্তু URIগুলিকেও চিনতে পারে যদি তাদের সাথে একটি সারি ID যুক্ত থাকে, যেমন content://com.example.app.provider/table3/1 সারির জন্য 1 দ্বারা চিহ্নিত সারি table3

নিম্নলিখিত বিষয়বস্তু URI নিদর্শন সম্ভব:

content://com.example.app.provider/*
প্রদানকারীর যেকোনো বিষয়বস্তুর URI-এর সাথে মেলে।
content://com.example.app.provider/table2/*
dataset1 এবং dataset2 টেবিলের জন্য একটি বিষয়বস্তু URI-এর সাথে মেলে, কিন্তু table1 বা table3 এর জন্য সামগ্রীর URI-এর সাথে মেলে না।
content://com.example.app.provider/table3/#
table3 -তে একক সারির জন্য একটি বিষয়বস্তুর URI-এর সাথে মেলে, যেমন 6 দ্বারা চিহ্নিত সারির জন্য content://com.example.app.provider/table3/6

নিম্নলিখিত কোড স্নিপেট দেখায় কিভাবে UriMatcher এর পদ্ধতিগুলি কাজ করে। এই কোডটি সারণি এবং সামগ্রীর জন্য URI প্যাটার্ন content://<authority>/<path> ব্যবহার করে একটি একক সারির URI থেকে আলাদাভাবে একটি সম্পূর্ণ টেবিলের জন্য URIগুলি পরিচালনা করে content://<authority>/<path>/<id> একক সারি জন্য।

পদ্ধতি addURI() একটি পূর্ণসংখ্যা মানের একটি কর্তৃপক্ষ এবং পথ ম্যাপ করে। মেথড match() একটি URI-এর জন্য পূর্ণসংখ্যার মান প্রদান করে। একটি switch স্টেটমেন্ট সম্পূর্ণ টেবিলের অনুসন্ধান এবং একটি একক রেকর্ডের জন্য অনুসন্ধানের মধ্যে বেছে নেয়।

কোটলিন

private val sUriMatcher = UriMatcher(UriMatcher.NO_MATCH).apply {
    /*
     * The calls to addURI() go here for all the content URI patterns that the provider
     * recognizes. For this snippet, only the calls for table 3 are shown.
     */

    /*
     * Sets the integer value for multiple rows in table 3 to 1. Notice that no wildcard is used
     * in the path.
     */
    addURI("com.example.app.provider", "table3", 1)

    /*
     * Sets the code for a single row to 2. In this case, the # wildcard is
     * used. content://com.example.app.provider/table3/3 matches, but
     * content://com.example.app.provider/table3 doesn't.
     */
    addURI("com.example.app.provider", "table3/#", 2)
}
...
class ExampleProvider : ContentProvider() {
    ...
    // Implements ContentProvider.query()
    override fun query(
            uri: Uri?,
            projection: Array<out String>?,
            selection: String?,
            selectionArgs: Array<out String>?,
            sortOrder: String?
    ): Cursor? {
        var localSortOrder: String = sortOrder ?: ""
        var localSelection: String = selection ?: ""
        when (sUriMatcher.match(uri)) {
            1 -> { // If the incoming URI was for all of table3
                if (localSortOrder.isEmpty()) {
                    localSortOrder = "_ID ASC"
                }
            }
            2 -> {  // If the incoming URI was for a single row
                /*
                 * Because this URI was for a single row, the _ID value part is
                 * present. Get the last path segment from the URI; this is the _ID value.
                 * Then, append the value to the WHERE clause for the query.
                 */
                localSelection += "_ID ${uri?.lastPathSegment}"
            }
            else -> { // If the URI isn't recognized,
                // do some error handling here
            }
        }

        // Call the code to actually do the query
    }
}

জাভা

public class ExampleProvider extends ContentProvider {
...
    // Creates a UriMatcher object.
    private static final UriMatcher uriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

    static {
        /*
         * The calls to addURI() go here for all the content URI patterns that the provider
         * recognizes. For this snippet, only the calls for table 3 are shown.
         */

        /*
         * Sets the integer value for multiple rows in table 3 to one. No wildcard is used
         * in the path.
         */
        uriMatcher.addURI("com.example.app.provider", "table3", 1);

        /*
         * Sets the code for a single row to 2. In this case, the # wildcard is
         * used. content://com.example.app.provider/table3/3 matches, but
         * content://com.example.app.provider/table3 doesn't.
         */
        uriMatcher.addURI("com.example.app.provider", "table3/#", 2);
    }
...
    // Implements ContentProvider.query()
    public Cursor query(
        Uri uri,
        String[] projection,
        String selection,
        String[] selectionArgs,
        String sortOrder) {
...
        /*
         * Choose the table to query and a sort order based on the code returned for the incoming
         * URI. Here, too, only the statements for table 3 are shown.
         */
        switch (uriMatcher.match(uri)) {


            // If the incoming URI was for all of table3
            case 1:

                if (TextUtils.isEmpty(sortOrder)) sortOrder = "_ID ASC";
                break;

            // If the incoming URI was for a single row
            case 2:

                /*
                 * Because this URI was for a single row, the _ID value part is
                 * present. Get the last path segment from the URI; this is the _ID value.
                 * Then, append the value to the WHERE clause for the query.
                 */
                selection = selection + "_ID = " + uri.getLastPathSegment();
                break;

            default:
            ...
                // If the URI isn't recognized, do some error handling here
        }
        // Call the code to actually do the query
    }

আরেকটি শ্রেণী, ContentUris , সামগ্রী URI-এর id অংশের সাথে কাজ করার জন্য সুবিধার পদ্ধতি প্রদান করে। Uri এবং Uri.Builder ক্লাসে বিদ্যমান Uri অবজেক্ট পার্সিং এবং নতুন তৈরি করার সুবিধার পদ্ধতি রয়েছে।

ContentProvider ক্লাস বাস্তবায়ন করুন

ContentProvider দৃষ্টান্ত অন্যান্য অ্যাপ্লিকেশনের অনুরোধগুলি পরিচালনা করে ডেটার একটি কাঠামোগত সেটে অ্যাক্সেস পরিচালনা করে। সমস্ত ধরণের অ্যাক্সেস অবশেষে ContentResolver কল করে, যা তারপরে অ্যাক্সেস পাওয়ার জন্য ContentProvider এর একটি নির্দিষ্ট পদ্ধতিকে কল করে।

প্রয়োজনীয় পদ্ধতি

বিমূর্ত শ্রেণীর ContentProvider ছয়টি বিমূর্ত পদ্ধতি সংজ্ঞায়িত করে যা আপনি আপনার কংক্রিট সাবক্লাসের অংশ হিসাবে প্রয়োগ করেন। onCreate() ব্যতীত এই সমস্ত পদ্ধতিগুলি একটি ক্লায়েন্ট অ্যাপ্লিকেশন দ্বারা কল করা হয় যা আপনার সামগ্রী প্রদানকারীকে অ্যাক্সেস করার চেষ্টা করছে।

query()
আপনার প্রদানকারী থেকে তথ্য পুনরুদ্ধার করুন. ক্যোয়ারী করার জন্য টেবিল, রিটার্ন করার জন্য সারি এবং কলাম এবং ফলাফলের সাজানোর ক্রম নির্বাচন করতে আর্গুমেন্ট ব্যবহার করুন। Cursor অবজেক্ট হিসাবে ডেটা ফেরত দিন।
insert()
আপনার প্রদানকারীতে একটি নতুন সারি ঢোকান। গন্তব্য সারণী নির্বাচন করতে এবং কলামের মানগুলি ব্যবহার করার জন্য আর্গুমেন্টগুলি ব্যবহার করুন। নতুন সন্নিবেশিত সারির জন্য একটি বিষয়বস্তু URI ফেরত দিন।
update()
আপনার প্রদানকারীর মধ্যে বিদ্যমান সারি আপডেট করুন. আপডেট করার জন্য টেবিল এবং সারি নির্বাচন করতে এবং আপডেট হওয়া কলামের মান পেতে আর্গুমেন্ট ব্যবহার করুন। আপডেট করা সারির সংখ্যা ফেরত দিন।
delete()
আপনার প্রদানকারী থেকে সারি মুছুন. সারণি নির্বাচন করতে আর্গুমেন্ট এবং মুছে ফেলার জন্য সারি ব্যবহার করুন. মুছে ফেলা সারির সংখ্যা ফেরত দিন।
getType()
একটি বিষয়বস্তু URI-এর সাথে সম্পর্কিত MIME প্রকারটি ফেরত দিন। এই পদ্ধতিটি ইমপ্লিমেন্ট কন্টেন্ট প্রদানকারী MIME প্রকার বিভাগে আরও বিশদে বর্ণনা করা হয়েছে।
onCreate()
আপনার প্রদানকারী শুরু করুন. অ্যান্ড্রয়েড সিস্টেম আপনার প্রদানকারী তৈরি করার সাথে সাথে এই পদ্ধতিটিকে কল করে। আপনার প্রদানকারী তৈরি করা হয় না যতক্ষণ না একটি ContentResolver অবজেক্ট এটি অ্যাক্সেস করার চেষ্টা করে।

এই পদ্ধতিগুলির অভিন্ন নামযুক্ত ContentResolver পদ্ধতিগুলির মতো একই স্বাক্ষর রয়েছে৷

এই পদ্ধতিগুলির আপনার বাস্তবায়নের জন্য নিম্নলিখিতগুলির জন্য অ্যাকাউন্ট করা প্রয়োজন:

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

query() পদ্ধতি প্রয়োগ করুন

ContentProvider.query() পদ্ধতিটি অবশ্যই একটি Cursor অবজেক্ট ফেরত দিতে হবে বা, যদি এটি ব্যর্থ হয়, একটি Exception নিক্ষেপ করুন। আপনি যদি আপনার ডেটা স্টোরেজ হিসাবে একটি SQLite ডাটাবেস ব্যবহার করেন, তাহলে আপনি SQLiteDatabase ক্লাসের query() পদ্ধতিগুলির একটি দ্বারা ফিরে আসা Cursor ফেরত দিতে পারেন।

যদি ক্যোয়ারী কোন সারির সাথে মেলে না, তাহলে একটি Cursor ইনস্ট্যান্স ফেরত দিন যার getCount() পদ্ধতিটি 0 প্রদান করে। ক্যোয়ারী প্রক্রিয়া চলাকালীন একটি অভ্যন্তরীণ ত্রুটি দেখা দিলেই শুধুমাত্র null রিটার্ন করুন।

আপনি যদি আপনার ডেটা সঞ্চয়স্থান হিসাবে একটি SQLite ডাটাবেস ব্যবহার না করেন, তাহলে Cursor কংক্রিট সাবক্লাসগুলির একটি ব্যবহার করুন। উদাহরণস্বরূপ, MatrixCursor ক্লাস একটি কার্সার প্রয়োগ করে যেখানে প্রতিটি সারি Object ইনস্ট্যান্সের একটি অ্যারে। এই ক্লাসের সাথে, একটি নতুন সারি যোগ করতে addRow() ব্যবহার করুন।

অ্যান্ড্রয়েড সিস্টেম অবশ্যই প্রক্রিয়া সীমানা জুড়ে Exception যোগাযোগ করতে সক্ষম হবে। অ্যান্ড্রয়েড নিম্নলিখিত ব্যতিক্রমগুলির জন্য এটি করতে পারে যা ক্যোয়ারী ত্রুটিগুলি পরিচালনা করতে কার্যকর:

  • IllegalArgumentException । যদি আপনার প্রদানকারী একটি অবৈধ কন্টেন্ট ইউআরআই পায় তাহলে আপনি এটি নিক্ষেপ করতে বেছে নিতে পারেন।
  • NullPointerException

সন্নিবেশ() পদ্ধতি প্রয়োগ করুন

insert() পদ্ধতিটি উপযুক্ত সারণীতে একটি নতুন সারি যোগ করে, ContentValues আর্গুমেন্টের মান ব্যবহার করে। যদি একটি কলামের নাম ContentValues আর্গুমেন্টে না থাকে, আপনি হয়ত আপনার প্রদানকারী কোডে বা আপনার ডাটাবেস স্কিমাতে এটির জন্য একটি ডিফল্ট মান প্রদান করতে চাইতে পারেন।

এই পদ্ধতিটি নতুন সারির জন্য সামগ্রী URI প্রদান করে। এটি তৈরি করতে, withAppendedId() ব্যবহার করে টেবিলের সামগ্রী URI-তে নতুন সারির প্রাথমিক কী, সাধারণত _ID মান যুক্ত করুন।

ডিলিট() পদ্ধতি প্রয়োগ করুন

delete() পদ্ধতিতে আপনার ডেটা স্টোরেজ থেকে সারি মুছতে হবে না। আপনি যদি আপনার প্রদানকারীর সাথে একটি সিঙ্ক অ্যাডাপ্টার ব্যবহার করেন, তবে সারিটিকে সম্পূর্ণরূপে অপসারণ করার পরিবর্তে একটি "মুছুন" পতাকা দিয়ে একটি মুছে ফেলা সারি চিহ্নিত করার কথা বিবেচনা করুন৷ সিঙ্ক অ্যাডাপ্টার মুছে ফেলা সারিগুলি পরীক্ষা করতে পারে এবং প্রদানকারীর থেকে মুছে ফেলার আগে সার্ভার থেকে তাদের সরাতে পারে৷

আপডেট() পদ্ধতি প্রয়োগ করুন

update() পদ্ধতিটি insert() দ্বারা ব্যবহৃত একই ContentValues ​​আর্গুমেন্ট এবং delete() এবং ContentProvider.query() দ্বারা ব্যবহৃত একই selection এবং selectionArgs আর্গুমেন্ট নেয়। এটি আপনাকে এই পদ্ধতিগুলির মধ্যে কোড পুনরায় ব্যবহার করতে দিতে পারে।

onCreate() পদ্ধতি প্রয়োগ করুন

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

নিম্নলিখিত দুটি স্নিপেট ContentProvider.onCreate() এবং Room.databaseBuilder() এর মধ্যে মিথস্ক্রিয়া প্রদর্শন করে। প্রথম স্নিপেটটি ContentProvider.onCreate() এর বাস্তবায়ন দেখায় যেখানে ডেটাবেস অবজেক্ট তৈরি করা হয় এবং ডেটা অ্যাক্সেস অবজেক্টের হ্যান্ডেলগুলি তৈরি করা হয়:

কোটলিন

// Defines the database name
private const val DBNAME = "mydb"
...
class ExampleProvider : ContentProvider() {

    // Defines a handle to the Room database
    private lateinit var appDatabase: AppDatabase

    // Defines a Data Access Object to perform the database operations
    private var userDao: UserDao? = null

    override fun onCreate(): Boolean {

        // Creates a new database object
        appDatabase = Room.databaseBuilder(context, AppDatabase::class.java, DBNAME).build()

        // Gets a Data Access Object to perform the database operations
        userDao = appDatabase.userDao

        return true
    }
    ...
    // Implements the provider's insert method
    override fun insert(uri: Uri, values: ContentValues?): Uri? {
        // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc.
    }
}

জাভা

public class ExampleProvider extends ContentProvider

    // Defines a handle to the Room database
    private AppDatabase appDatabase;

    // Defines a Data Access Object to perform the database operations
    private UserDao userDao;

    // Defines the database name
    private static final String DBNAME = "mydb";

    public boolean onCreate() {

        // Creates a new database object
        appDatabase = Room.databaseBuilder(getContext(), AppDatabase.class, DBNAME).build();

        // Gets a Data Access Object to perform the database operations
        userDao = appDatabase.getUserDao();

        return true;
    }
    ...
    // Implements the provider's insert method
    public Cursor insert(Uri uri, ContentValues values) {
        // Insert code here to determine which DAO to use when inserting data, handle error conditions, etc.
    }
}

ContentProvider MIME প্রকারগুলি প্রয়োগ করুন৷

ContentProvider ক্লাসে MIME প্রকারগুলি ফেরত দেওয়ার জন্য দুটি পদ্ধতি রয়েছে:

getType()
প্রয়োজনীয় পদ্ধতিগুলির মধ্যে একটি যা আপনি যে কোনও প্রদানকারীর জন্য প্রয়োগ করেন।
getStreamTypes()
আপনার প্রদানকারী ফাইল অফার করলে এমন একটি পদ্ধতি যা আপনি বাস্তবায়ন করবেন বলে আশা করা হচ্ছে।

টেবিলের জন্য MIME প্রকার

getType() পদ্ধতিটি MIME ফরম্যাটে একটি String প্রদান করে যা বিষয়বস্তু URI আর্গুমেন্ট দ্বারা প্রত্যাবর্তিত ডেটার ধরণ বর্ণনা করে। Uri যুক্তি একটি নির্দিষ্ট URI এর পরিবর্তে একটি প্যাটার্ন হতে পারে। এই ক্ষেত্রে, প্যাটার্নের সাথে মেলে এমন কন্টেন্ট URI-এর সাথে যুক্ত ডেটার ধরন ফেরত দিন।

টেক্সট, এইচটিএমএল বা JPEG এর মতো সাধারণ ধরনের ডেটার জন্য getType() সেই ডেটার জন্য স্ট্যান্ডার্ড MIME টাইপ প্রদান করে। এই স্ট্যান্ডার্ড ধরনের একটি সম্পূর্ণ তালিকা IANA MIME মিডিয়া টাইপস ওয়েবসাইটে উপলব্ধ।

কন্টেন্ট URIগুলির জন্য যেগুলি সারি বা সারি সারণির ডেটা নির্দেশ করে, getType() Android-এর বিক্রেতা-নির্দিষ্ট MIME ফর্ম্যাটে একটি MIME প্রকার প্রদান করে:

  • টাইপ অংশ: vnd
  • উপপ্রকার অংশ:
    • যদি URI প্যাটার্ন একটি একক সারির জন্য হয়: android.cursor. item /
    • যদি URI প্যাটার্ন একাধিক সারির জন্য হয়: android.cursor. dir /
  • প্রদানকারী-নির্দিষ্ট অংশ: vnd.<name><type>

    আপনি <name> এবং <type> সরবরাহ করুন। <name> মান বিশ্বব্যাপী অনন্য, এবং <type> মানটি সংশ্লিষ্ট URI প্যাটার্নের জন্য অনন্য। <name> এর জন্য একটি ভাল পছন্দ হল আপনার কোম্পানির নাম বা আপনার অ্যাপ্লিকেশনের Android প্যাকেজের নামের কিছু অংশ। <type> এর জন্য একটি ভাল পছন্দ হল একটি স্ট্রিং যা URI-এর সাথে যুক্ত টেবিলটিকে চিহ্নিত করে।

উদাহরণ স্বরূপ, যদি কোনো প্রদানকারীর কর্তৃপক্ষ com.example.app.provider হয় এবং এটি table1 নামের একটি সারণী প্রকাশ করে, তাহলে table1 এ একাধিক সারির জন্য MIME প্রকারটি হল:

vnd.android.cursor.dir/vnd.com.example.provider.table1

table1 এর একটি একক সারির জন্য, MIME প্রকার হল:

vnd.android.cursor.item/vnd.com.example.provider.table1

ফাইলের জন্য MIME প্রকার

যদি আপনার প্রদানকারী ফাইল অফার করে, তাহলে getStreamTypes() প্রয়োগ করুন। পদ্ধতিটি MIME প্রকারের একটি String অ্যারে প্রদান করে যে ফাইলগুলি আপনার প্রদানকারী একটি প্রদত্ত সামগ্রী URI-এর জন্য ফেরত দিতে পারে৷ MIME প্রকার ফিল্টার আর্গুমেন্ট দ্বারা আপনি যে MIME প্রকারগুলি অফার করেন তা ফিল্টার করুন, যাতে আপনি শুধুমাত্র সেই MIME প্রকারগুলি ফেরত দেন যা ক্লায়েন্ট পরিচালনা করতে চায়৷

উদাহরণস্বরূপ, একটি প্রদানকারীর কথা বিবেচনা করুন যেটি JPG, PNG, এবং GIF ফর্ম্যাটে ফাইল হিসাবে ছবির ছবি অফার করে। যদি একটি অ্যাপ্লিকেশন "ইমেজ" এমন কিছুর জন্য ফিল্টার স্ট্রিং image/* সহ ContentResolver.getStreamTypes() কে কল করে, তাহলে ContentProvider.getStreamTypes() পদ্ধতিটি অ্যারেটি ফেরত দেয়:

{ "image/jpeg", "image/png", "image/gif"}

যদি অ্যাপটি শুধুমাত্র JPG ফাইলগুলিতে আগ্রহী হয়, তাহলে এটি ফিল্টার স্ট্রিং *\/jpeg সহ ContentResolver.getStreamTypes() কল করতে পারে এবং getStreamTypes() রিটার্ন করতে পারে:

{"image/jpeg"}

যদি আপনার প্রদানকারী ফিল্টার স্ট্রিং-এ অনুরোধ করা MIME প্রকারের কোনো অফার না করে, getStreamTypes() null প্রদান করে।

একটি চুক্তি বর্গ বাস্তবায়ন

একটি চুক্তি শ্রেণী হল একটি public final শ্রেণী যাতে URI, কলামের নাম, MIME প্রকার এবং অন্যান্য মেটা-ডেটা প্রদানকারীর সাথে সম্পর্কিত ধ্রুবক সংজ্ঞা থাকে। ইউআরআই, কলামের নাম ইত্যাদির প্রকৃত মানগুলিতে পরিবর্তন থাকলেও প্রদানকারীকে সঠিকভাবে অ্যাক্সেস করা যায় তা নিশ্চিত করে ক্লাসটি প্রদানকারী এবং অন্যান্য অ্যাপ্লিকেশনের মধ্যে একটি চুক্তি স্থাপন করে।

একটি কন্ট্রাক্ট ক্লাস ডেভেলপারদেরকেও সাহায্য করে কারণ এতে সাধারণত এর ধ্রুবকের জন্য স্মৃতি সংক্রান্ত নাম থাকে, তাই ডেভেলপারদের কলামের নাম বা URI-এর জন্য ভুল মান ব্যবহার করার সম্ভাবনা কম থাকে। যেহেতু এটি একটি ক্লাস, এতে Javadoc ডকুমেন্টেশন থাকতে পারে। ইন্টিগ্রেটেড ডেভেলপমেন্ট এনভায়রনমেন্ট যেমন অ্যান্ড্রয়েড স্টুডিও চুক্তি ক্লাস থেকে ধ্রুবক নামগুলি স্বয়ংসম্পূর্ণ করতে পারে এবং ধ্রুবকের জন্য Javadoc প্রদর্শন করতে পারে।

বিকাশকারীরা আপনার অ্যাপ্লিকেশন থেকে চুক্তি ক্লাসের ক্লাস ফাইলটি অ্যাক্সেস করতে পারে না, তবে তারা আপনার সরবরাহ করা একটি JAR ফাইল থেকে তাদের অ্যাপ্লিকেশনে এটি স্থিরভাবে কম্পাইল করতে পারে।

ContactsContract ক্লাস এবং এর নেস্টেড ক্লাস হল কন্ট্রাক্ট ক্লাসের উদাহরণ।

বিষয়বস্তু প্রদানকারীর অনুমতি প্রয়োগ করুন

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

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

আপনি যদি আপনার ডেটাতে অ্যাক্সেস নিয়ন্ত্রণ করতে সামগ্রী প্রদানকারীর অনুমতিগুলি ব্যবহার করতে চান, তাহলে আপনার ডেটা অভ্যন্তরীণ ফাইল, SQLite ডেটাবেস বা ক্লাউডে সংরক্ষণ করুন, যেমন একটি দূরবর্তী সার্ভারে, এবং ফাইল এবং ডেটাবেসগুলিকে আপনার অ্যাপ্লিকেশনে ব্যক্তিগত রাখুন৷

অনুমতি বাস্তবায়ন

ডিফল্টরূপে, সমস্ত অ্যাপ্লিকেশন আপনার প্রদানকারীর কাছ থেকে পড়তে বা লিখতে পারে, এমনকি অন্তর্নিহিত ডেটা ব্যক্তিগত হলেও, কারণ ডিফল্টরূপে আপনার প্রদানকারীর অনুমতি সেট নেই। এটি পরিবর্তন করতে, <provider> উপাদানের বৈশিষ্ট্য বা চাইল্ড এলিমেন্ট ব্যবহার করে আপনার ম্যানিফেস্ট ফাইলে আপনার প্রদানকারীর জন্য অনুমতি সেট করুন। আপনি অনুমতি সেট করতে পারেন যা সম্পূর্ণ প্রদানকারীর জন্য প্রযোজ্য, নির্দিষ্ট টেবিলে, নির্দিষ্ট রেকর্ডে, বা তিনটিতে প্রযোজ্য।

আপনি আপনার ম্যানিফেস্ট ফাইলে এক বা একাধিক <permission> উপাদান দিয়ে আপনার প্রদানকারীর জন্য অনুমতি নির্ধারণ করেন। অনুমতি আপনার প্রদানকারীর জন্য অনন্য করতে, android:name বৈশিষ্ট্যের জন্য Java-শৈলী স্কোপিং ব্যবহার করুন। উদাহরণস্বরূপ, পড়ার অনুমতির নাম দিন com.example.app.provider.permission.READ_PROVIDER

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

একক পঠন-লেখা প্রদানকারী-স্তরের অনুমতি
একটি অনুমতি যা <provider> উপাদানের android:permission এট্রিবিউট দিয়ে নির্দিষ্ট করা সম্পূর্ণ প্রদানকারীর রিড এবং রাইট উভয় অ্যাক্সেস নিয়ন্ত্রণ করে।
প্রদানকারী-স্তরের অনুমতিগুলি আলাদা করে পড়া এবং লিখুন
সম্পূর্ণ প্রদানকারীর জন্য একটি পড়ার অনুমতি এবং একটি লেখার অনুমতি। আপনি <provider> এলিমেন্টের android:readPermission এবং android:writePermission বৈশিষ্ট্যগুলির সাথে তাদের নির্দিষ্ট করুন। তারা android:permission এর জন্য প্রয়োজনীয় অনুমতির চেয়ে অগ্রাধিকার নেয়।
পাথ-স্তরের অনুমতি
আপনার প্রদানকারীতে একটি বিষয়বস্তুর URI-এর জন্য পড়ুন, লিখুন বা পড়ার/লেখার অনুমতি নিন। আপনি <provider> এলিমেন্টের একটি <path-permission> চাইল্ড এলিমেন্ট দিয়ে নিয়ন্ত্রণ করতে চান এমন প্রতিটি URI নির্দিষ্ট করুন। আপনার নির্দিষ্ট করা প্রতিটি কন্টেন্ট URI-এর জন্য, আপনি একটি পঠন/লেখার অনুমতি, একটি পড়ার অনুমতি, একটি লেখার অনুমতি বা তিনটিই নির্দিষ্ট করতে পারেন। পঠন এবং লেখার অনুমতিগুলি পঠন/লেখার অনুমতির চেয়ে অগ্রাধিকার পায়। এছাড়াও, পাথ-স্তরের অনুমতি প্রদানকারী-স্তরের অনুমতিগুলির চেয়ে অগ্রাধিকার নেয়।
অস্থায়ী অনুমতি
একটি অনুমতি স্তর যা একটি অ্যাপ্লিকেশনে অস্থায়ী অ্যাক্সেস মঞ্জুর করে, এমনকি যদি অ্যাপ্লিকেশনটিতে সাধারণত প্রয়োজনীয় অনুমতি না থাকে। অস্থায়ী অ্যাক্সেস বৈশিষ্ট্যটি একটি অ্যাপ্লিকেশনকে তার ম্যানিফেস্টে অনুরোধ করার অনুমতির সংখ্যা হ্রাস করে। আপনি যখন অস্থায়ী অনুমতিগুলি চালু করেন, শুধুমাত্র আপনার প্রদানকারীর জন্য স্থায়ী অনুমতির প্রয়োজন এমন অ্যাপ্লিকেশনগুলিই ক্রমাগত আপনার সমস্ত ডেটা অ্যাক্সেস করে৷

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

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

অস্থায়ী অনুমতি চালু করতে, হয় <provider> এলিমেন্টের android:grantUriPermissions অ্যাট্রিবিউট সেট করুন অথবা আপনার <provider> এলিমেন্টে এক বা একাধিক <grant-uri-permission> চাইল্ড এলিমেন্ট যোগ করুন। যখনই আপনি আপনার প্রদানকারীর কাছ থেকে অস্থায়ী অনুমতির সাথে যুক্ত কোনো সামগ্রী URI-এর জন্য সমর্থন সরান তখন Context.revokeUriPermission() এ কল করুন।

অ্যাট্রিবিউটের মান নির্ধারণ করে আপনার প্রদানকারীর কতটা অ্যাক্সেসযোগ্য করা হয়েছে। যদি অ্যাট্রিবিউটটি "true" তে সেট করা থাকে, তাহলে সিস্টেমটি আপনার প্রদানকারী-স্তরের বা পাথ-স্তরের অনুমতিগুলির জন্য প্রয়োজনীয় অন্য কোনও অনুমতিকে ওভাররাইড করে আপনার সম্পূর্ণ প্রদানকারীকে অস্থায়ী অনুমতি দেয়।

যদি এই পতাকাটি "false" এ সেট করা থাকে, তাহলে আপনার <provider> উপাদানে <grant-uri-permission> চাইল্ড উপাদান যোগ করুন। প্রতিটি চাইল্ড এলিমেন্ট কন্টেন্ট URI বা URI উল্লেখ করে যার জন্য অস্থায়ী অ্যাক্সেস দেওয়া হয়।

একটি অ্যাপ্লিকেশনে অস্থায়ী অ্যাক্সেস অর্পণ করতে, একটি অভিপ্রায়ে FLAG_GRANT_READ_URI_PERMISSION পতাকা, FLAG_GRANT_WRITE_URI_PERMISSION পতাকা বা উভয়ই থাকতে হবে৷ এগুলো setFlags() পদ্ধতিতে সেট করা হয়।

যদি android:grantUriPermissions অ্যাট্রিবিউটটি উপস্থিত না থাকে তবে এটিকে "false" বলে ধরে নেওয়া হয়।

<provider> উপাদান

Activity এবং Service কম্পোনেন্টের মতো, ContentProvider একটি সাবক্লাস <provider> এলিমেন্ট ব্যবহার করে তার প্রয়োগের জন্য ম্যানিফেস্ট ফাইলে সংজ্ঞায়িত করা হয়েছে। অ্যান্ড্রয়েড সিস্টেম উপাদান থেকে নিম্নলিখিত তথ্য পায়:

কর্তৃপক্ষ ( android:authorities )
প্রতীকী নাম যা সিস্টেমের মধ্যে সমগ্র প্রদানকারীকে সনাক্ত করে। ডিজাইন বিষয়বস্তু URIs বিভাগে এই বৈশিষ্ট্যটি আরও বিশদে বর্ণনা করা হয়েছে।
প্রদানকারীর শ্রেণীর নাম ( android:name )
যে শ্রেণীটি ContentProvider প্রয়োগ করে। ContentProvider ক্লাস প্রয়োগ করুন বিভাগে এই ক্লাসটি আরও বিশদে বর্ণনা করা হয়েছে।
অনুমতি
প্রদানকারীর ডেটা অ্যাক্সেস করার জন্য অন্যান্য অ্যাপ্লিকেশানগুলির অবশ্যই থাকা আবশ্যক অনুমতিগুলি নির্দিষ্ট করে এমন বৈশিষ্ট্যগুলি:

অনুমতি এবং তাদের সংশ্লিষ্ট গুণাবলী বিষয়বস্তু প্রদানকারীর অনুমতি প্রয়োগ করুন বিভাগে আরো বিস্তারিতভাবে বর্ণনা করা হয়েছে।

স্টার্টআপ এবং নিয়ন্ত্রণ বৈশিষ্ট্য
এই বৈশিষ্ট্যগুলি নির্ধারণ করে কিভাবে এবং কখন Android সিস্টেম প্রদানকারীকে শুরু করে, প্রদানকারীর প্রক্রিয়া বৈশিষ্ট্য এবং অন্যান্য রানটাইম সেটিংস:
  • android:enabled : ফ্ল্যাগ সিস্টেমকে প্রদানকারী শুরু করতে দেয়
  • android:exported : পতাকা অন্যান্য অ্যাপ্লিকেশন এই প্রদানকারী ব্যবহার করতে দেয়
  • android:initOrder : যে ক্রমে এই প্রদানকারীটি শুরু করা হয়েছে, একই প্রক্রিয়ার অন্যান্য প্রদানকারীদের তুলনায়
  • android:multiProcess : ফ্ল্যাগ সিস্টেমটিকে কলিং ক্লায়েন্টের মতো একই প্রক্রিয়ায় সরবরাহকারীকে শুরু করতে দেয়
  • android:process : প্রোভাইডার যে প্রক্রিয়ায় চলে তার নাম
  • android:syncable : পতাকা নির্দেশ করে যে প্রদানকারীর ডেটা সার্ভারের ডেটার সাথে সিঙ্ক করা হবে

এই বৈশিষ্ট্যগুলি সম্পূর্ণরূপে <provider> উপাদানের গাইডে নথিভুক্ত করা হয়েছে।

তথ্যগত বৈশিষ্ট্য
প্রদানকারীর জন্য একটি ঐচ্ছিক আইকন এবং লেবেল:
  • android:icon : প্রদানকারীর জন্য একটি আইকন ধারণকারী একটি অঙ্কনযোগ্য সম্পদ। আইকনটি সেটিংস > অ্যাপস > সব-এ অ্যাপের তালিকায় প্রদানকারীর লেবেলের পাশে প্রদর্শিত হয়।
  • android:label : প্রদানকারী, তার ডেটা বা উভয়ের বর্ণনা দেয় এমন একটি তথ্যমূলক লেবেল। লেবেলটি সেটিংস > Apps > All- এ অ্যাপের তালিকায় উপস্থিত হয়।

এই বৈশিষ্ট্যগুলি সম্পূর্ণরূপে <provider> উপাদানের গাইডে নথিভুক্ত করা হয়েছে।

দ্রষ্টব্য: আপনি যদি Android 11 বা উচ্চতরকে লক্ষ্য করে থাকেন, তাহলে আরও কনফিগারেশনের প্রয়োজনের জন্য প্যাকেজ দৃশ্যমানতা ডকুমেন্টেশন দেখুন।

উদ্দেশ্য এবং ডেটা অ্যাক্সেস

অ্যাপ্লিকেশনগুলি একটি Intent সহ পরোক্ষভাবে একটি সামগ্রী প্রদানকারীকে অ্যাক্সেস করতে পারে৷ অ্যাপ্লিকেশনটি ContentResolver বা ContentProvider এর কোনো পদ্ধতিকে কল করে না। পরিবর্তে, এটি একটি উদ্দেশ্য পাঠায় যা একটি কার্যকলাপ শুরু করে, যা প্রায়শই প্রদানকারীর নিজস্ব অ্যাপ্লিকেশনের অংশ। গন্তব্য কার্যকলাপ তার UI এ ডেটা পুনরুদ্ধার এবং প্রদর্শনের দায়িত্বে রয়েছে।

অভিপ্রায়ের কর্মের উপর নির্ভর করে, গন্তব্য কার্যকলাপ ব্যবহারকারীকে প্রদানকারীর ডেটাতে পরিবর্তন করতেও প্ররোচিত করতে পারে। একটি অভিপ্রায়ে "অতিরিক্ত" ডেটাও থাকতে পারে যা UI-তে গন্তব্য কার্যকলাপ প্রদর্শন করে। ব্যবহারকারীর তখন প্রদানকারীতে ডেটা পরিবর্তন করতে ব্যবহার করার আগে এই ডেটা পরিবর্তন করার বিকল্প রয়েছে।

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

আপনি যদি ডেভেলপারদের অভিপ্রায় অ্যাক্সেস ব্যবহার করতে চান, তাহলে এটি পুঙ্খানুপুঙ্খভাবে নথিভুক্ত করতে ভুলবেন না। ব্যাখ্যা করুন কেন আপনার অ্যাপ্লিকেশনের UI ব্যবহার করে অভিপ্রায় অ্যাক্সেস তাদের কোড দিয়ে ডেটা পরিবর্তন করার চেষ্টা করার চেয়ে ভাল।

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

অতিরিক্ত সম্পর্কিত তথ্যের জন্য, ক্যালেন্ডার প্রদানকারী ওভারভিউ পড়ুন।