Android Webservices

Android Webservices بالعربية – الدرس الثانى

فى الدرس السابق ِAndroid Webservices بالعربية – الدرس الأول  أخذنا نظرة عامة على ما سيتم فعله بالتحديد وعرفنا اننا سنتعامل مع php ومع MySQL وسيكون هناك تخاطب بين php وتطبيقنا وكذلك بين php والـ MySQL واليوم سنركز  على موضوع اتصال تطبيق الأندرويد بملف php موجود على الانترنت وسيككون موضوع اتصال الـ PHP بالـ MySQL فى التدوينة القادمة فى هذه السلسلة

Php mysql android

سنقوم الان بإنشاء ملف php بسيط جدا جدا  وكل مهمته أن يطبع على الشاشة عبارة “Hello from the other side ”  لأنشاء اى ملف php تقوم بفتح ملف جديد بأى محرر نصوص وليكن الـ notepad اذا اردت لكنى احب sublime text يمكنك تحميله من هنا   ستقوم بكتابة التالى <?php فى اول سطر كالتالى :

وهى عباره عن علامة تعريف الملف أن هذا الملف هو مستند php .
Screen Shot 2016-01-21 at 2.15.36 AM

عندما تريد طباعة نص فى جافا تستخدم System.out.println();  وتضع الجملة بين قوسين لكن فى php لطباعة نص نستخدم الأمر echo ولطباعة جملة Hello from The other sideسيكون الامر كالتالى :

sublime text php

ثم نقوم بحفظ الملف ولا تنسى وضع .php بعد اسم الملف كالتالى : وقمت هنا بتسمية الملف hello

save file sublime text android web ser

الان لدينا ملف php كل ما يفعله هو طباعة جملة Hello from the other side ونريد رفع هذا الملف على الانترنت لكى نستطيع التواصل معه من تطبيق الأندرويد وجلب النص الموجود وعرضه داخل الموبايل

نريد وضع هذا الملف بمكان يمكن الوصول اليه من اى مكان فى العالم هنا يأتى دور الاستضافة أو الـ Host .

اذا لم يسبق لك التعامل مع الاستضافات ابدا ولا تعرف ما هى يمكنك اجراء بحث سريع فى جوجل وستفهم كل شىء عنها  ان كان لديك استضافة قمت بشرائها يمكنك رفع الملف عليها وان لم يكن لديك استضافة قم بالتسجيل فى استضافة مجانية وأفضل الاستضافات المجانية المتوفرة باللغة العربية من تجربتى  هى hostinager  قم بالتسجيل وقم بتفعيل حسابك عن طريق الايميل (قم بمشاهدة هذا الشرح على يوتيوب اذا كنت تريد مساعدة ) ثم قم بالدخول الى لوحة التحكم ورفع ملف الـ php الذى انشأته ولقد قمت برفع ملف php الذى انشاته هذا على استضافتى المجانية القديمة على hostinager والان هذا هو رابط الملف  http://developerhendy.16mb.com/hello.php

وكما ترون يعرض عبارة hello from the other side الان اريد أن اتصل بهذا الملف من خلال أندرويد لكى اقوم بسحب العباره وعرضها فى Textview داخل الأندرويد

قم بانشاء مشروع جديد فى أندرويد ستوديو .. لقد قمت بانشاء مشروع جديد وأطلقت عليه Get Messaga

يحتوى على TextView سوف نقوم بجعله يستقبل جملة Hello from other side ويعرضها .

android studio screen shot

وسنقوم بتعريف هذا الـ Text فى ملف Main Activity بالجافا كالعادة عند التعامل مع اى مكون .

Android studio screen shot

الى الان كل شىء تمام فى الأندرويد  وكما ترى هناك زر FloatActionButton الافتراضى عند انشاء مشروع جديد فى الاندرويد سنقوم باستخدامه لتنفيذ كود الاتصال عند الضغط عليه .

سنبدأ بكتابة كود الاتصال بالانترنت وجلب العبارة من الملف hello.php الموجود على الرابط http://developerhendy.16mb.com/hello.php

عندما نتعامل مع نص فى الجافا نقوم بتعريفه على أنه نص String وعندما نتعامل مع عدد نقوم بتعريفه كـ int  والان نحن نتعامل مع رابط انترنت وبالتالى سنقوم بتعريفه كـ URL وهو الكلاس المسؤول عن وصلات الأنترنت فى الجافا كالتالى

URL hellofileurl = new URL("http://developerhendy.16mb.com/hello.php");

وبمجرد كتابة السطر السابق ستجد ان هناك خط أحمر ظهر اسفل اللينك

URL

ويطلب منك عمل try catch اتركه الان وكما يقول اخوانا المصريين ” اعمل نفسك من بنها ” ودعنا نتابع

كلاس الـ URL كما قلنا تعرف الجافا بأن هذا URL قد تسألنى اليس هذا نص عادى 🙁 لقد وضعته بين “” وهذا مجرد متغير نصى لماذا اقوم بوضعه فى كلاس الـ URL  وتعريفه على أنه URL  وسأجيبك بأن http://developerhendy.16mb.com/hello.php عباره عن جملة مكونة من بروتكول وهو http ومسار وهو developerhendy.16mb.com/hello.php والبروتوكول ليس شرطا بأن يكون http يمكن أن يكون ftp او https المهم أنه بروتوكول ورابط وقد يكون الرابط اكثر تعقيدا وبه علامات استفهام و & و = الخ .. قم بالبحث فى جوجل عن اى شىء وانظر الى الرابط فى المتصفح بالاعلى وستجد انه رابط كبير وبه اشياء كثيرة وليس مجرد بروتكول ومسار .. على كل حال كلاس الـ URL تقوم بتقسيم الرابط هذا وتحديد الـ protocol وتحديد المسار النصى وتقوم بتنسيق هذا النص بطريقة معينة ليصبح جاهز للتعامل معه من خلال الجافا .

لكن لمااذا يضع أندرويد ستوديو خطا أحمر ويطلب منا عمل try catch  ؟

هذا لان هذه الكلاس تقوم بعمل Exeption يسمى MalformedURLException

وهذا الـ Exception يحدث عندما تكون الـ URL تحتوى على رابط خاطىء على سبيل المثال يمكن أن تنسى t وتكتب htp://developerhendy.16mb.com/hello.php أو يمكن أن تنسى // أو تكتب واحده فقط أو تقوم بعملها معكوسة كالتالى http:\\developerhendy.16mb.com/hello.php وغيرها وهو يطلب منك معالجة الـ Exeption هذا عن طريق try catch للـ MalformedURLException

اى بكل بساطة ماذا ينفذ او يفعل اذا كانت الـ URL خاطئة وسنتطرق لهذا الامر لاحقا لكن الان اتركه بخطه الاحمر هذا 😀  سنعالجه لاحقا والان تابع معنا  .

 

الان نريد الاتصال بهذا اللينك الموجود فى الكلاس URL يوجد Method تابعة لكلاس الـ URL تسمى openConnection() وتقوم بفتح اتصال مع اللينك السابق وعندا استدعائها ستجد أن صديقنا الأندرويد ستوديو قام بوضع خط أحمر اسفلها هى الأخري  urlopenconnection

يمكن أن نستقبل نتيجة الاتصال بكلاس URLConnection لكن هناك Class أخرى متخصصة أكثر بموضوع الـ Http  تسمىHttpUrlConnection وتوفر لك مميزات أكتر لذلك سوف نستقبل ناتج الاتصال بداخل HttpUrlConnection ويكون الكود التالى :

srccnshot

الان  ما زال الخط الأحمر تحت السطر الاول وستجده هناك خطا أحمر اخر تحت السطر التانى  لذلك سنقوم بتحديد السطرين داخل اندرويد ستوديو والضغط على ctrl + alt +T ونختار try.. catch  وستجد الكود الناتج كالتالى

Android studio htttpurl connection

ستجد انه تم عمل Catch للـ IOException وهو الـ Exeption الخاص بالإدخال والاخراج ولم يقم الأندرويد ستوديو بعمل كاتش لـ  MalformedURLException لانها ترث من IOException وبالتالى يغنى عنها IOException هنا .

ولكن الخط الأحمر مستمر بالظهور وذلك لأنه يحتاج لعمل Cast لأن الناتج من Method الـ openConnection() هو URLConnection ونريد امساكه بـ HttpURLConnection لذلك سنقوم بعمل Cast اذا كنت لا تدرى لماذا نقوم بعمل Cast پشكل عام يرجى مراجعة التدوينة ليه بنعمل Cast

بعد ما قمنا بعمل Cast الان لدينا الكود كالتالى

screen shot

وكل ما فعلناه أننا عرفنا الجافا أن هناك URL عنوانها http://developerhendy.16mb.com/hello.php وان يتم فتح اتصال سوف يمسكه الـمتغير myfirstconnection وهو عباره عن Httpurlconnection

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

ولا بد أن تعلم تبادل البيانات يتم عن طريق تدفق من Bytes  (الـ Byte عبارة عن 8 bit والـ bit هو عباره عن 0او 1 وبالتالى تأتى البيانات على شكل 110101010…. ) من مكان الى اخر مثل خرطوم الماء الذى ينقل الماء من مكان الى اخر تتتدفق البيانات من مكان لاخر وهنا تتدفق البيانات من السيرفر او الاستضافة الى تطبيق الاندرويد وتأتينا على هيئة Bytes Stream وفى الجافا هناك الكثير من الكلاسات تتعامل مع الـ Streams كل من له غرض مختلف عن الاخر فتجد مثلا  FileOutputStream و DataOurStream و ObjectoutputStream

 

والكثير منها ربما نتحدث عنهم فى مقالة او عدة مقالات عن الـ Java IO لكن الان لدينا تدفق من الـ Bytes قادم من الـurl ويتحكم به myfirstconnetion  لقد ارسلنا الطلب للسيرفر  والسيرفر سيرسل لنا النتيجة على هيئة bytes ومهتما ستكون تحويل هذا التدفق من الـ Bytes  الى نص وفى الحقيقة لا يمكن تحويله الى نص مباشرة فالنصوص تعتبر معالجتها أصعب من معالجة الملفات الاخرى لانها تمر بمرحلة الـ Encoding والـ Decoding بين حالتها وهى Bytes وبين حالتها وهى نص  لذلك سنضطر تحويل الـ Byte Stream القادم من الويب الى Character stream والكلاس التى ستساعدنا فى ذلك هى كلاس الـ InputStreamReader  وهى كلاس تستقبل الـ bytes stream وتحوله الى Character stream  ولمعلومات أكتر عن الـ Character Stream سوف يكون هناك تدوينة عن الجافا IO أو عدة تدوينات قريبا .

سنستخدم الكلاس بعمل new ونعطيها المتغير myfirstconnection.getInputStream();   وهو الاتصال httpurlconnection لكى يبدأ العمل ونقل  الـبيانات لنا   وهى ستتكفل بباقى الامر .

Input Stream

 

عظيم الان لدينا character stream ويتبقى فقط تحويله الى نص مقروء والجافا توفر لنا كلاس اسمها BufferdReader سوف نعطيها الـ character stream وتقوم باعطائنا القدرة على تحويل الى نص  وسنقوم باستخدامها كالتالى : 

bufferd

تأخذ كلاس الـ BufferdReader الـ stream وهو هنا الـ charcter stream  الناتج من inputstream وتتيح لنا استخراج النص والان سنقوم باستخراج النص فى String عادى  عن طريق Method موجودة فى الـ BufferdReader تسمى readline(); ويكون الكود كالاتى :

string

الان لدينا String hello وهو يحتوى على السطر الموجود بملف php  يمكننا طباعته عن طريق الـ Toast أو Log و عمل setText للمتغير السابق لكن اذا فعلت هذا وقمت بالتشغيل ستجد أن هناك Exeption قد حدث وهو NetwordMainThread ومعناه انك لا تسطيع تنفيذ الكود السابق عبر الـ Main ثريد وهذا موضوع متعلق بالـ MultiThreading وليس موضوعنا الان لكن لاحقا سنتحدث عنه بالتفصيل فى مقالات اخرى .

نقوم بعمل ثريد جديد وذلك عن طريق تغليف الكود السابق كـ Runnable وتشغيله فى Thread جديد لعمل Runnable قم بعمل Runnable runnable= new Runnable وقم بوضع الكود السابق بداخل الـ Method run والتى تنشىء تلقائيا عند إنشاء Runnable جديد سيكون الكود كالتالى :

runn

وسنقوم بإنشاء Thread جديد ووضع هذا الـ Runnable بداخله  كالتالى :

thread

اذا قمت بالتشغيل الان ستواجه Exeption اخر 😀 وهو بسبب أن تعديل مكونات الواجهة لا بد أن يتم من خلال الـ MainThread فقط لذلك بداخل الـ runnable بالنسبة لكود الـ text.setText سنقوم بتنفيذه داخل ميثود تسمى runOnUIThread   كالتالى :

runnable

الان اذا لم تقم باضافة صلاحيات الانترنت فى ملف الـ AndroidManifest قم باضافتها الان

android mainefest

ثم قم بعمل Run واضغط على زر الـ Floatbutton والذى سينفذ الكود السابق وسترى النتيجة

final result

مبروك الان قمت بأول تواصل لك مع ملف php موجود أون لاين ^_^

ملحوظة : لم اتطرق للـ Thread والـ Runnable پشرح مفصل لأنهم تابعين لموضوع الـ MultiThreading وسنتحدث عنه مستقبلا ان شاء الله
السابق
ِAndroid Webservices بالعربية – الدرس الأول
التالي
Android Webservices بالعربية – الدرس الثالث

46 تعليق

أضف تعليقا

  1. Mahmoud Abdelwahab قال:

    بسم الله ما شاء الله ربنا يجعل هذا العمل فى ميزان حسناتك
    شرح وافى الصراحه منتظر باقى الدروس بفارغ الصبر ^^

  2. خالد قال:

    يعطيك العافية على هذا الشرح الأكثر من رائع, ربنا يوفقك ومنتظرين بقية الدروس

  3. محمد المهل قال:

    السلام عليكم يا اخي و الله مجهود مقدر
    بس لو تكرمت لو عندي رسالة echo
    اخري اسفل الكود في ملف ال php
    كيف اتحكم في الوصول للرسالة التي اريدها الاولي او الثانية
    جزاك الله خير
    شرحك وافي جدا تابع في ميزان حسناتك و علينا الدعاء لك 🙂
    جزاك الله خير أنت و من معك.

    1. Hendiware قال:

      سوف تقوم بعمل Stringbuilder وتقوم باضافة سطر بسطر كالتالى
      StringBuilder mystring = new StringBuilder();
      String temp= null;
      while((temp=ourstreamreader.readline())!=null){
      mystring.append(temp)
      )

      وفى النهاية
      mystring.toString(); تكون عباره عن السطور كلها .

  4. امير دويكات قال:

    شكرا الك شرح رائع ولكن الاستضاف مو زابطة لانه مابدعم هاض الاشي عنا بفلسطين بالنسبة للموقع ارجو اذا كنت تعملي استضافة او مساعدتي بهالشي وشكرا الك

  5. محبوب الحسين قال:

    شرح جميل بالتوفيق

  6. Ahmed Ali قال:

    شرح ممتاز ربنا يقدرك وتقدر تكمل وتفيدنا ولو في اي مساعدة لدعم الموقع ياريت تقول
    في عندي سؤالين اول واحد هل انا ممكن استبدل ال Thread بال AsyncTask ؟
    تاني واحد هل الشرح في ال Webservices في الدروس القادمة تقدر تشرح ازاي اتكلم مع السيرفر ب GET و POST ؟
    شكرا ليك مرة تانية 🙂

    1. Hendiware قال:

      بالتأكيد ممكن استبداله بـ AsyncTask
      إن شاء الله هنشرح كل حاجه تخص الويب سيرفس POST و GET و Volley و JSON Parsing وأشياء اخرى 😉

      نورت التدوينة يا أحمد .

  7. talib قال:

    بارك الله فيك

  8. باسم نصر قال:

    حقيقي مجهود رائع
    شكرا يا احمد

  9. يا سلام ما كنت ابحث عنه طويلا ميرسي بجد

  10. berkanioussama قال:

    hostinager لا يفر استضافة مجانية في الجزائر

    1. Hendiware قال:

      يمكنك استخدام اى استضافة مجانيه اخرى صديقى

    2. abdelkrim قال:

      berkanioussama شو الاستضافة لي استعملتها فأنا ايضا من الجزائر ووجدت مشكلة مع hostinager

  11. Maha Ghanem قال:

    لو سمحت ازاى ارف ملف الphp على hostinager
    وجزاك الله خير

    1. Hendiware قال:

      يمكنك من خلال ايقونة مدير الملفات 2
      عند الضغط عليها سيقوم بفتح نافذه جديدة
      نقوم بالدخول لمجلد public_html بالضغط عليه
      ثم نضغط على زر upload باللون الاصفر يفتح صفحة جديدة بها زر choose file نختار الملف ثم نضغط علامة صح سيتم رفع المف .

      1. Maha Ghanem قال:

        تمام ربنا يكرمك شكرا جدا

  12. Zualfekar قال:

    رائع رائع .. شكرا 🙂

  13. ناصر قال:

    جزاك الله خيرا
    اسلوبك رائع وخصوصا في شرح الكود

    يوجد مشكله في انشاء استضافة على موقع hostinger
    Unfortunately, we do not provide free hosting services in your country and for this reason, you are able to use our paid plans only
    هل يوجد حل للمشكلة دى؟؟

    1. حسناء قال:

      طيب بعد ما رفعت الفايل عايزة اخد لينك الفايل اللى بقى موجود علشان اضعه ف الكود .. اازى ؟

      1. Hendiware قال:

        @حسناء
        عند التسجيل فى الاستضافة تقومى باختيار دومين او رابط فرعى لموقعك مثال
        hasnaa.16mmm.com
        واذا قمتى برفع الملف ولينك test.php
        سيكون الرابط الخاص بالملف كالتالى
        hasnaa.16mmm.com/test.php

    2. Hendiware قال:

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

  14. على قال:

    سلام عليكم
    عاوز اجلب بيانات عمود بالكامل واخزنها فى مصفوفه واقدر استدعيها كل عنصر ع حدا

    1. Hendiware قال:

      تابع الدروس التاليه ستجد الاجابة

  15. roja قال:

    السلام عليكم

    عندي مشكلة لما اجي احط رابط مرفوع على السيرفر المحلي حقي لازم احط بدل localhost
    ال ip حق الجهاز حقي مو ؟؟؟

    لما احط الرابط و احط الاي بي حق الجهاز مكان localhost ما يقرا الملف ابدا
    لكن لما احط رابط الملف المرفوع على السيرفر فوق يقرا الجملة الي فيه ويعرضها

    اش الحل
    والكود متاكدة انو صح ولا ماكان اشتغل الرابط حقكم اللي فوق
    لكن المشكلة عندي في الرابط

    1. Hendiware قال:

      لو هتستخدمى localhost بيبقى الكمبيوتر قارىء اللوكال هوست بشكل عادى اما الجنى موشن مثلا لا يقرأ local هوست لذلك الرابط بدلا من localhost ضعى 10.0.3.2 وسيعمل الجنى موشن مع الـ localhost بهذا الايىبى

  16. Sweelam قال:

    السلام عليكم
    شرح ممتاز بارك الله فيك
    لكن للاسف يوجد عندي مشكلة في الكود هل ممكن المساعدة !

    1. Hendiware قال:

      بالتأكيد . ما هى المشكلة ؟

  17. ٌروان شداد قال:

    السلام عليكم بالبداية بتشكرك ع الشرح الرائع
    لكن عم تواجهني مشكلة لما طبق الكود في مجال المساعدة ؟؟؟

    1. Hendiware قال:

      وعليكم السلام ،
      بالتأكيد ما هى المشكلة ؟

  18. ميسرة قال:

    ولكو ايش هادا !!!!!!!!!!
    ايش هالروعة هادي
    ايش هالشرح الفخم !!
    مش عارف كيف اشكركم
    موفقين

  19. محمد فتحي قال:

    شرح جميل جداً جزاكم الله عنا كل خيــر

  20. ahmed elsalamony قال:

    سلام عليكم لو سمحت عندى مشكلة
    مش بيظهرلى المحتوى بتاع الفايل بيظهرلى دة بس

    1. ahmed elsalamony قال:

      اللى بيظهر تاج html فقط

  21. soumia قال:

    السلام عليكم

    طبقت الدرس لكن لم ينجح معي وظهر لي هذا الخطأ

    06-30 21:53:17.032 3112-3112/com.lar.myapp E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.lar.myapp, PID: 3112
    android.os.NetworkOnMainThreadException
    at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1147)
    at java.net.InetAddress.lookupHostByName(InetAddress.java:418)
    at java.net.InetAddress.getAllByNameImpl(InetAddress.java:252)
    at java.net.InetAddress.getAllByName(InetAddress.java:215)
    at com.android.okhttp.HostResolver$1.getAllByName(HostResolver.java:29)
    at com.android.okhttp.internal.http.RouteSelector.resetNextInetSocketAddress(RouteSelector.java:232)
    at com.android.okhttp.internal.http.RouteSelector.next(RouteSelector.java:124)
    at com.android.okhttp.internal.http.HttpEngine.connect(HttpEngine.java:272)
    at com.android.okhttp.internal.http.HttpEngine.sendRequest(HttpEngine.java:211)
    at com.android.okhttp.internal.http.HttpURLConnectionImpl.execute(HttpURLConnectionImpl.java:382)
    at com.android.okhttp.internal.http.HttpURLConnectionImpl.getResponse(HttpURLConnectionImpl.java:332)
    at com.android.okhttp.internal.http.HttpURLConnectionImpl.getInputStream(HttpURLConnectionImpl.java:199)
    at com.lar.myapp.MainActivity$1.onClick(MainActivity.java:32)
    at android.view.View.performClick(View.java:4780)
    at android.view.View$PerformClick.run(View.java:19866)
    at android.os.Handler.handleCallback(Handler.java:739)
    at android.os.Handler.dispatchMessage(Handler.java:95)
    at android.os.Looper.loop(Looper.java:135)
    at android.app.ActivityThread.main(ActivityThread.java:5254)
    at java.lang.reflect.Method.invoke(Native Method)
    at java.lang.reflect.Method.invoke(Method.java:372)
    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)

    1. حاتم قال:

      تاكد من انك تعمل الكود ف Run

  22. أحمد السوري قال:

    أكتر من رااائع
    مدونة خارقة جزاكم الله كل خير

  23. naser قال:

    متشكر جدااا

  24. مصطفى شريف قال:

    فيديو شرح التسجيل على hostinager غير متوفر على ال youtube ؟

  25. انا جربت على button عادي بيطلع error لما بدوس عليه ايه التغير اللي اعمله عشان اخليه يوصل للسيرفر بالبوتن العادي ؟

  26. عيسى قال:

    مشكور جدا على هذا الشرح الاكثر من رائع
    الله يجزيك الخير ويكتب ذلك في ميزان حسناتك

  27. ابراهيم قال:

    طبقت الدرس ومش بيشتغل بيظهر <BODY<BODY
    الخ صفحه HTML وكلها اكواد HTML

  28. ayman قال:

    انا طبقت كل حاجه فى الدرس وبيتصل بالانترنت وتمام لكن النتجيه بتكون شكل اكواد مبيكنش نص عادى ممكن مساعده فى حل المشكله دى

  29. فرح قال:

    كللل الشكر

  30. ahmad قال:

    الاسلوب اكتر من رائع …. جزاك الله خيرا بشمهندس هندى 🙂

  31. Ahlam قال:

    السلام عليكم عندي سؤال هل استضافة hostinger جيده لربط قاعدة البيانات مع تطبيقات اندرويد ؟ لانه خفت يكون خاص فقط لربط المواقع مع قاعدة البيانات

اترك تعليقاً

هذا الموقع يستخدم Akismet للحدّ من التعليقات المزعجة والغير مرغوبة. تعرّف على كيفية معالجة بيانات تعليقك.