Content Provider في أندرويد

بسم الله الرحمن الرحيم

في مقالة سابقة تحدثنا عن بناء تطبيق بقاعدة بيانات واستخدمنا sqlite اليوم سنأخذ هذا التطبيق إلى مرحلة أخرى بأن نضيف له Content provider.

المقالة ستكون مقسمة بالترتيب التالي:

  1. ماهو Content provider
  2. تحديث Contract class
  3. بناء Content provider class
  4. استخدامه داخل Activity

1- إذا ما هو الـ Content Provider ؟

نستطيع أن نقول هو طبقة -layer- تدير عمليات التعامل مع قواعد البيانات من داخل التطبيق وخارجه, هذه الجملة العامة تحتاج لقليل من التفصيل, لو كان لديك تطبيق فيه قاعدة بيانات وترغب بأن يقرأ تطبيق أخر من هذه البيانات هنا تأتي فائدة الـ Content provider, لو احتجت أن يعدل تطبيق أخر على قاعدة بيانات تطبيق هنا يأتي دور الـ Content provider, لو كان لديك أكثر من نوع من البيانات في التطبيق صور وفيديو وقاعدة بيانات وأردت أن يكون هناك مصدر واحد لهذه البيانات سيكون الـ Content provider خيار رائع لهذا, لو كنت تريد أن تكون عملية استرجاع البيانات من قاعدة البيانات غير متزامنة – asynchronous وفائدتها أن لا تتعطل main thread – ستحتاج لتعامل مع CursorLoader والذي يعتمد على الـ Content provider.

بعد هذه التقديم البسيط دعنا نتجه لتطبيق الـ Content provider, سنبدأ من مشروع تم بناء قاعدة البيانات فيه ونقوم نحن ببناء الـ Content provider لهذا التطبيق.

سيكون التطبيق عبارة عن تخزين وقراءة بيانات طلاب, لكل طالب اسم, و id ومادة فيكون شكل model class كالتالي

class contract تكلمنا عنه في المقالة السابقة سيكون شكلة قبل بناء الـ Content provider كالتالي:

class StudentHelper سيكون شكله كالتالي:

main activity في البداية سيكون بهذا الشكل سيتم تحديثه بنهاية بناء الـ Content provider:

هذه نقطة البداية في المشروع أفترض أنك الان قد قمت ببناء هذه الملفات وقمت بلصق الكود داخل كل ملف, في حال كان لديك أي تساؤل بخصوص أي سطر تأكد أنه تم شرحه بالتفصيل في المقالة السابقة هنا, لنبدأ بالخطوة الثانية.

2- تحديث Contract class

عندما تريد التعامل مع موقع معين فإنك تكتب رابط الموقع على سبيل المثال www.twitter.com, ماذا لو أردت التخصيص أكثر والبحث عن شيء معين داخل هذا الموقع ؟, ستضيف التالي www.twitter.com/3zcs الان تمكنت من الوصول لبيانات شخص في هذا الموقع, سنفكر بنفس هذه الطريقة في التعامل مع الـ Content provider.

بداية دعنا نقارن بناء الرابط الخاص الـ Content provider برابط twitter, أولا بدل www سيكون content://
ثانية بدل twitter.com سنضع أسم package مثلا com.example.android.students
سيكون شكل الرابط الذي نريده كالتالي content://com.example.android.students
هذا الرابط الخاص بالـ Content provider الخاص بناء لنضيفه في contract class
ليكون شكله كالتالي:

لاحظت أننا قمنا ببناء الرابط الذي يصل بناء إلى قاعدة البيانات الخاصة بناء وكان عبارة عن سطرين السطر الأول CONTENT_AUTHORITY وعرفنا فيه رابط القاعدة كـ String, والثاني BASE_CONTENT_URI هو عبارة عن رابط قاعدة بالبيانات وأضفنا فيه الرابط كامل بالسابقة التي ذكرناها content:// هذه هو الرابط الذي يستطيع أي شخص يستخدمه الوصول لقاعدة البيانات الخاصة بناء, كما تعلم قاعدة البيانات قد يكون بها أكثر من جدول – Table – سيتعين علينا بناء رابط لكل جدول وبما أن لدينا جدول واحد سنقوم ببناء رابط واحد لجدول Student ليكون شكل الـ contract class كالتالي:

رائع جدا قمنا بالتخصيص الان اصبح شكل الرابط كالتالي content://studentslist.azcs.me.studentdatabase/students ,الان تذكر رابط تويتر عندما احتجنا التخصيص للوصول لمستخدم بعينه, قمنا ببناء CONTENT_URI ليكون خاص بهذا الجدول فقط.

بقي نقطة أخيرة ونكون بعدها قد إنتهينا من بناء contract class ألا وهي, قد نقوم بعملية على الجدول كامل وقد نقوم بعملية على صف واحد من الجدول, مثلا قد نقوم بقراءة الجدول كامل, أو قراءة بيانات طالب واحد, قد نعدل الجدول كامل أو نقوم بتعديل على طالب واحد, سنقوم بخطوة بسيطة وهي إنشاء رابط إذا استخدمناه فإننا نريد التعامل مع الجدول كامل, ورابط أخر إذا استخدمناه سنقوم بالتعامل مع صف واحد من الجدول هما ببساطة كالتالي

هنا تفصيل سنتجاوزه ليستمر الشرح بهذه البساطة, سيكون شكل class Contract في النهاية كالتالي:

الان لننتقل إلى الخطوة الثالثة ألأ وهي

3- بناء Content provider class

سنقوم بإضافة class StudentProvider يعمل extends -يرث من- لـ ContenProvider, سيجبرك على أن تعمل implementation لـ 6 دوال وهي

  1. onCreate
  2. getType
  3. query
  4. insert
  5. delete
  6. update

إذا علمنا أن الأربع الأخيرة هي العمليات الأساسية لقواعد البيانات والتي عملنا عليها في الدرس السابق, وسنقوم في هذا الدرس بتعديل بسيط عليها فقط, يصبح لدينا دالتين جديدتين onCreate و getType.

لنقوم بإنشاء هذا class

قمت بتعريف بعض المتغيرات التي سأحتاجها في هذا الـ class, أولا

ذكرنا سابقا أن لدينا نوعين من العمليات

قد نقوم بعملية على الجدول كامل وقد نقوم بعملية على صف واحد من الجدول

ولهذا الغرض قمنا بإنشاء رابط لكل عملية

ولهذا عرفت المتغيرين في الأعلى فكل واحد منهما عبارة عن code لهذه العملية ببساطة كأن تقول إذا أرسلت الرابط الخاص بعملية على الجدول كامل فسأعطيك رقم 10 وإذا أرسلت الرابط الخاص بعملية على صف واحد فسأعطيك رقم 11, وسيتضح فائدة هذه الأرقام إذا أكملنا بناء هذا الـ class.
بعد ذلك قمنا بإستخدام uri matcher وأعطيناه قيمة ابتدائية NO_MATCH

الغرض من هذا الـ matcher أن يتحقق من الرابط الذي ترسله, ثم يعطيك الكود المناسب, بمعنى أنك قررت أن تستخدم content provider فستقوم بإرسال رابط نوع العملية هل هي على الجدول كامل أو على صف واحد, وسيقوم هذا الـ matcher بإرجاع الكود أو الرقم الخاص بالعملية في حالتنا 10 أو 11.

بعد تعريف هذا الـ matcher, سنقوم بتعريف static initialization blocks وهو بمعنى بسيط block يتم استدعاؤه مرة واحدة في حين إنشاء class الغرض منه أن يقوم بتنفيذ الأوامر التي بداخلة لمرة واحدة, وببساطة في سطرين سنقوم نحن بداخل هذا الـ block بربط الروابط بالأرقام

وبعد ذلك عرفت متغير من نوع StudentHelper في أعلى الـ class لأتمكن من إنشائه مرة واحدة وأستخدمه داخل الـ class كامل.

بعد هذه التعريفات نقوم بعمل implementation لدوال الست التي يطلبها الـ conent provider.

onCreate كالعادة فيها الإنشاءات للمتغيرات التي نحتاجها داخل هذا class بحكم أنها أول دالة تستدعى, ثم نقوم بإرجاع true.

 

getType تحدثنا سابقأ عن أن الـ Content provider هو المصدر الوحيد للبيانات داخل التطبيق سواء صور أو فيديو أو قواعد بيانات, وتحدثنا أنه قد يستخدم التطبيق تطبيق أخر خارج التطبيق, فعندما يريد التواصل مع نوع معين من البيانات داخل تطبيقنا, يستخدم getType ويرسل فيها Uri ونرد عليه بنوع البيانات المخصص

لو كان التطبيق الخاص بنا لا يشارك البيانات مع تبطيق أخر, نستطيع أن نجعل هذه الدالة تقوم بعمل throw exception كالتالي

 

الان نبدأ بالعمليات الخاصة بالقواعد البيانات CRUD.

نبدأ بعملية القراءة READ وهي دالة query

لو لاحظت هنا, لا يوجد فرق بين كبير بينها الدالة هنا ودالة query في sqlite بدون قواعد سوى في uri الذي من خلاله تستطيع تحديد نوع العملية هل هي على قاعدة البيانات كاملة أم على صف واحد, في السطر هذا سنسترجع إما 10 أو 11 وعلى ضوء ذلك ننفذ العملية على النوع الذي تم اختياره.

ربما يكون الكود واضح لكن سيتضح أكثر حينما نقوم باستدعاء هذه الدالة.

وبعد ذلك قمنا بإرجاع cursor يحوي البيانات الجدول أو الصف.

الان ننتقل لعملية أخرى من عمليات قواعد البيانات
ألا وهي CREATE والدالة التي تمثلها هنا هي insert
بحكم أنك ستقوم بإدخال صف واحد في كل مرة داخل جدول Students, فمن غير المنطقي أن تتعامل مع رابط صف لإن التعامل هنا سيكون مع الجدول, فسنتحقق فقط من أن رابط العملية يتم على الجدول أو سنقوم بعمل throw Excption, نقوم مباشرة بإدخال الصف, وإرجاع Uri مضاف إليه رقم Id الخاص بالصف المدخل في حالة نجاح عملية إضافة الصف وإرجاع null في حالة الفشل.

لو كان لديك أكثر من جدول, ربما تحتاج لـ switch لمعرفة أي جدول سيتم فيه الإضافة, لكن في حالتنا نكتفي بهذا.

العملية التالية هي UPDATE مع دالة update

وهي في غاية البساطة والتشابه مع delete

ونقوم بإرجاع عدد الصفوف التي تم التعديل عليها.

أما في DELETE

فسنقوم بإرجاع عدد السطور التي حذفت

هنا نكون قد أتممنا بناء هذا الـ class وشكله النهائي كالتالي

ربما يبدو طويل بعض الشيء لكن عند تقسيمه ستجد أنه واضح, بعد ذلك نحتاج أن نسجل الـ provider في ملف manifests داخل application tag

في حال كنت تنوي أن يشارك قاعدة البيانات الخاصة بك مع تطبيقات أخرى فتجعل قيمة exported تساوي true

الأن اتممنا البناء بالكامل لم يتبقى إلا جزئية استخدمه داخل الـ Activity
أول خطوة قم بحذف StudentHelper من الـ Activity فالمفترض أن لا يكون هناك أي تواصل مع قاعدة البيانات إلا عن طريق الـ Content Provider, ليصبح شكل الجدول قبل إضافة الـ Content Provider كالتالي

نضيف الـ getContentResolver في جميع العمليات والذي سيقودنا مباشرة إلى قاعدة البيانات الخاصة بنا إذا قمنا بإعطائه الرابط الصحيح فيكون شكل الـ Activity

تلاحظ لا يوجد ذلك إختلاف كبير بين طريقة إدخال البيانات وقراءتها من قاعدة البيانات بين content provider و sqlite على سبيل المثال سطر الإضافة

الاختلاف يأتي في إضافة رابط كأول argument, ,والرابط يأتي على شكلين الأول لتعامل مع صف والثاني لتعامل مع الجدول

إلى هنا نكون وصلنا إلى نهاية هذه المقالة هنا رابط المشروع على github

في حال كان هناك أي نقطة تحتاج إلى تفصيل أكثر أو أي خطأ لا تتردد في التواصل معي إما عبر التعليق هنا أو عبر حسابي في تويتر

المراجع:

اترك تعليقاً