أساسيات أندرويد بجافا

هيكل مشروع أندرويد

18 دقيقة الدرس 3 من 12

هيكل مشروع أندرويد

عندما يُنشئ Android Studio مشروعًا جديدًا، يُوجِد تخطيطًا محددًا للمجلدات يجب على كل مطوّر أندرويد فهمه. هذا التخطيط ليس عشوائيًا — فكل مجلد يرتبط بمفهوم في خط أنابيب البناء. معرفة مكان الأشياء والسبب يُخبرك بمكان إجراء التغييرات وما سيحدث حين تفعل ذلك.

المنظور العام للمشروع

في جذر المشروع ستجد طبقتين متمايزتين: مستوى المشروع ومستوى وحدة التطبيق. مشاريع أندرويد متعددة الوحدات افتراضيًا (حتى لو كان تطبيقك يحتوي على وحدة واحدة فقط). ملفا build.gradle وsettings.gradle الخارجيان ينتميان إلى مستوى المشروع، أما مجلد app/ فهو وحدة التطبيق الفعلية.

MyAndroidApp/ ├── app/ │ ├── src/ │ │ ├── main/ │ │ │ ├── java/com/example/myapp/ ← ملفات Java المصدرية │ │ │ ├── res/ ← الموارد (تخطيطات، صور، نصوص …) │ │ │ └── AndroidManifest.xml ← إعلان التطبيق │ │ └── test/ ← اختبارات الوحدة (JVM) │ │ └── androidTest/ ← الاختبارات المزوّدة (جهاز/محاكي) │ └── build.gradle ← سكريبت Gradle لمستوى الوحدة ├── gradle/ │ └── wrapper/ │ └── gradle-wrapper.properties ← تثبيت إصدار Gradle ├── build.gradle ← سكريبت Gradle لمستوى المشروع └── settings.gradle ← قائمة الوحدات وإدارة الإضافات

شجرة مصادر Java

يقع كل كود التطبيق تحت app/src/main/java/ في هرمية حزم Java المعتادة. يُنشئ Android Studio حزمة افتراضية بناءً على معرّف التطبيق (Application ID) الذي أدخلته عند إنشاء المشروع (مثلًا com.example.myapp). كل Activity وFragment وService وBroadcastReceiver وفئة مساعدة تكتبها تذهب إلى هنا.

اسم الحزمة = معرّف التطبيق (عادةً). الحزمة المُعلَنة في ملفات Java وapplicationId في build.gradle مستقلّان، لكن إبقاءهما متطابقَين يتجنّب الالتباس. معرّف التطبيق هو ما يُميّز تطبيقك على متجر Play وعلى الجهاز؛ لا يمكنك تغييره بعد النشر.

مجلد res/

يفصل أندرويد الكود عن الموارد. كل شيء بصري أو قابل للإعداد — تخطيطات وصور وألوان ونصوص — يقع ضمن res/ في مجلدات فرعية مُسمّاة حسب نوعها. يُجمّع نظام البناء كل مورد إلى معرّف رقمي ويُولّد فئة R حتى يتمكّن كود Java من الإشارة إلى الموارد بالاسم في وقت التجميع.

  • res/layout/ — ملفات XML تصف التسلسل الهرمي للواجهة لكل شاشة أو fragment.
  • res/values/ — ملفات XML للقيم المسمّاة: strings.xml وcolors.xml وdimens.xml وstyles.xml.
  • res/drawable/ — الرسوميات المتجهة وغيرها من الصور (PNG وملفات XML للأشكال).
  • res/mipmap-*/ — أيقونات مشغّل التطبيق بكثافات مختلفة (hdpi، xhdpi، xxhdpi …).
  • res/menu/ — تعريفات قوائم XML لقوائم الخيارات والقوائم السياقية.
  • res/anim/ — ملفات XML لحركات Tween.

الوصول إلى مورد نصي من Java يبدو كالتالي:

// داخل Activity: String appName = getString(R.string.app_name); // في ملف XML للتخطيط (res/layout/activity_main.xml): // android:text="@string/app_name"
لا تُضمّن نصوصًا مرئية للمستخدم في الكود مباشرةً أبدًا. أعلنها دائمًا في res/values/strings.xml وأشر إليها عبر R.string.*. هذا هو أساس التوطين — إضافة res/values-ar/strings.xml للعربية كل ما يلزم لدعم لغة جديدة.

AndroidManifest.xml

يُعدّ AndroidManifest.xml في المسار app/src/main/AndroidManifest.xml الملف الإعدادي الأهم في مشروعك. قبل أن ينفّذ نظام أندرويد أي مكوّن من مكوّنات تطبيقك، يجب تصريح ذلك المكوّن هنا. يُعلن الملف أيضًا عن:

  • اسم حزمة التطبيق (يُستخدم لحلّ أسماء الفئات النسبية).
  • كل <activity> و<service> و<receiver> و<provider> في التطبيق.
  • نشاط المشغّل (Launcher Activity) — الذي يبدأ حين يضغط المستخدم على الأيقونة — عبر <intent-filter> يحمل الإجراء MAIN والفئة LAUNCHER.
  • الأذونات التي يحتاج التطبيق من المستخدم (<uses-permission>).
  • الحد الأدنى وإصدار SDK المستهدف (عبر <uses-sdk>، وإن كانت هذه الأيام تُحدَّد في Gradle عادةً).
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android"> <uses-permission android:name="android.permission.INTERNET" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:theme="@style/Theme.MyApp"> <activity android:name=".MainActivity" android:exported="true"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
يجب تعيين android:exported صراحةً في API 31+. بدءًا من Android 12 (مستوى API 31)، يجب على الأنشطة التي تُعلن عن <intent-filter> تعيين android:exported="true" أو "false". إغفال هذا يتسبّب في فشل البناء. نشاط المشغّل يكون دائمًا exported="true"؛ أما الأنشطة الداخلية التي يبدأها تطبيقك فقط فينبغي أن تكون exported="false".

نظام بناء Gradle

يستخدم أندرويد Gradle أداةً للبناء. ثمة سكريبتا بناء يجب عليك فهمهما: سكريبت مستوى المشروع وسكريبت مستوى الوحدة.

build.gradle على مستوى المشروع — يُعلن عن إضافات Gradle المتاحة لجميع الوحدات. نادرًا ما تُعدّل هذا الملف فيما عدا إضافة إضافة جديدة إلى classpath.

// build.gradle على مستوى المشروع plugins { id 'com.android.application' version '8.3.0' apply false id 'org.jetbrains.kotlin.android' version '1.9.23' apply false }

app/build.gradle على مستوى الوحدة — هنا تقضي معظم وقتك. يتحكّم في إصدارات Android SDK وإعدادات التوقيع وكل تبعيات المكتبات التي يستخدمها تطبيقك.

// app/build.gradle plugins { id 'com.android.application' } android { namespace 'com.example.myapp' compileSdk 34 defaultConfig { applicationId "com.example.myapp" minSdk 24 // الحد الأدنى لإصدار أندرويد المدعوم (Android 7.0) targetSdk 34 // SDK المُستهدَف والمُختبَر عليه versionCode 1 versionName "1.0" } buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' } } compileOptions { sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } } dependencies { implementation 'androidx.appcompat:appcompat:1.7.0' implementation 'com.google.android.material:material:1.12.0' implementation 'androidx.constraintlayout:constraintlayout:2.1.4' testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' }

الإعدادات الأساسية لفهمها:

  • compileSdk — إصدار SDK الذي يستخدمه المُجمِّع. اضبطه على أحدث إصدار مستقر حتى تتمكن من استخدام واجهات API الجديدة في وقت التجميع (مع فحوصات وقت التشغيل للأجهزة الأقدم).
  • minSdk — أقدم إصدار أندرويد سيعمل عليه تطبيقك. تخفيضه يوسّع جمهورك المحتمل لكنه يُقيّد واجهات API التي يمكنك استدعاؤها دون قيود.
  • targetSdk — يُخبر أندرويد بنموذج السلوك الذي يتوقعه تطبيقك. كلّما رفعته، اخترت سلوكيات النظام الجديدة (مثل التخزين المحدود النطاق وأذونات الإشعارات). تُلزم Google Play التطبيقات باستهداف SDK حديث.
  • applicationId — المعرّف الفريد لتطبيقك على متجر Play. لا يمكن تغييره بعد النشر.
نطاقات التبعيات: implementation تعني أن المكتبة متاحة لوحدتك لكنها لا تُعرَّض للوحدات الأخرى التي تعتمد على وحدتك — وهو الخيار الصحيح لكل تبعية تقريبًا. testImplementation تُضيف مكتبة لاختبارات الوحدة على JVM فقط؛ أما androidTestImplementation فللاختبارات المزوّدة (على الجهاز) فقط. أبقِ مكتبات الاختبار بعيدةً عن classpath الرئيسية.

settings.gradle

يُخبر ملف settings.gradle في جذر المشروع Gradle بالوحدات الموجودة. في التطبيقات أحادية الوحدة يحتوي ببساطة على:

pluginManagement { repositories { google() mavenCentral() gradlePluginPortal() } } dependencyResolutionManagement { repositories { google() mavenCentral() } } rootProject.name = "MyAndroidApp" include ':app'

حين تُضيف وحدة مكتبة أو وحدة ميّزة إلى مشروعك، تُضيف سطر include ':moduleName' آخر هنا وتُنشئ المجلد المقابل.

الخلاصة

هيكل مشروع أندرويد هو فصل متعمّد للاهتمامات: تُجمّع مصادر Java منطقك، ويُخرج res/ كل ما يتغيّر بحسب اللغة أو كثافة الشاشة، ويُسجّل AndroidManifest.xml مكوّناتك مع نظام التشغيل، وتربط سكريبتات Gradle كل ذلك معًا في APK أو AAB. في الدرس القادم ستُنشئ Activity وتربطها بتخطيط، مستفيدًا مباشرةً من هذه المجلدات.