
لو اشتغلت قبل كده علي بروجيكت كبير بتلاقي ملف ال build.gradle فيه سطرين:
namespace 'com.company.projectname'
applicationId "com.company.appname"
أول رد فعل هيجي في ذهنك هو: “طيب ليه مش نفس الاسم؟” الاسم الأول(projectname)، والتاني اسم تاني خالص (appname).
في Android، فيه فرق كبير بين اسم ال project و اسم الـ app في نظر الموبايل ومتجر التطبيقات. المشكلة إن معظمنا لما بيبدأ مشروع جديد، الاتنين بيكونوا نفس القيمة تلقائيًا، فمش بتحس إن فيه فرق أصلًا. المشكلة بتظهر بس لما المشروع “يكبر” ويجي “يتغيّر اسمه” بعد فترة كبيرة من الشغل عليه. وبالأخص لو حصل كده في مشروع فيه آلاف الملفات، هتلاقي نفسك قدام سؤال صعب: هل أعمل rename لكل حاجة؟ ولا فيه حل تاني؟
الفرق الحقيقي بين namespace و applicationId:
الاتنين بيبانوا متشابهين لأن الاتنين شكلهم package name، بس دورهم مختلف تمامًا:
– ال namespace – هوية الكود جوه المشروع:
- بيتستخدم وقت الـ compile فقط.
- بيحدد الـ package بتاع الـ R class والـ BuildConfig اللي الكود بتاعك بيعتمد عليهم.
- هو الاسم اللي بتستخدمه جوه الكود نفسه، زي com.company.projectname.R.string.something.
- ثابت مش بيتغير حسب الـ flavor.
– ال applicationId – هوية ال app في نظر النظام والمتجر:
- ده اللي بيتحدد جوه productFlavors ومختلف لكل flavor.
- هو اللي بيستخدمه نظام Android وGoogle Play عشان يعرف التطبيق ده هو مين بالظبط.
- بيسمحلك تنزل أكتر من نسخة من نفس الـ app على نفس الموبايل باستخدام ال productFlavors.
يعني ببساطة: namespace بيجاوب على سؤال “الكود بينادي نفسه إيه؟“، وapplicationId بيجاوب على سؤال “البلاي ستور والنظام بينادوا التطبيق إيه؟“
مثال حقيقي من مشروع فيه flavors كتير:
في المشاريع الكبيرة، غالبًا بيكون عندك أكتر من بيئة: dev، uat، audit، prod. كل بيئة محتاجة applicationId مختلف عشان تقدر تركّبهم كلهم مع بعض على جهازك وقت الاختبار:
val appId = "com.company.projectname"
android {
productFlavors {
named("dev") {
applicationId = "${appId}dev"
}
named("test") {
applicationId = "${appId}test"
}
named("prod") {
applicationId = "com.company.appname"
}
}
}
لاحظ إن الـ prod flavor هنا واخد اسم مختلف تمامًا عن باقي الـ flavors.
ليه بيحصل الاختلاف ده أصلًا؟
القصة غالبًا بتبدأ بـ rebrand. المشروع بيتبني الأول باسم داخلي (هوية الكود)، زي اللي اتحول لـ package اسمه: com.company.projectname
وبعد كده، فريق التسويق بيقرر إن اسم الـ app اللي هيبان للناس مختلف خالص. المشروع بقى اسمه حاجة تانية بالكامل في نظر المستخدم، لكن جوه الكود لسه شغال بنفس البنية القديمة. الحل العملي هنا مش تغيير آلاف الملفات، لكن تغيير الـ applicationId بس:
applicationId "com.company.appname"
وبكده الـ app اللي المستخدم شايفه في Play Store بقي باسم جديد، لكن الكود جوّه لسه بيتكلم بلغته القديمة، وده تمامًا سبب وجود namespace منفصل عن applicationId.
ليه معملناش الاتنين نفس القيمة الجديدة؟
namespace = applicationId
الإجابة:
إنك فعلًا تقدر تعمل كده، ومشاريع كتير جديدة بتبدأ بالشكل ده من الأول. لكن المشكلة إن تغيير namespace في مشروع “ناضج” (mature project) مش مجرد rename بسيط. لأنه بيأثر على أشياء كتير مبنية على بنية الـ package نفسها:
• نقل الـ packages من مكان لمكان.
• تعديل كل الـ imports في المشروع.
• تعديل الـ classes اللي بتتولد تلقائيًا (generated classes).
• تعديل الـ annotation processors زي Hilt وDagger، اللي أحيانًا بيبنوا افتراضات على مكان الـ package.
• تعديل الاختبارات (tests).
• مراجعة أي كود بيستخدم reflection ويعتمد على اسم الـ package بشكل نصّي.
• مراجعة قواعد ProGuard اللي بتشير لمسارات معينة.
– في مشروع كبير فيه مئات الـ modules، الكلام ده مش “شوية شغل بسيط”، ده مجهود ضخم فعلًا، ومخاطرة كمان، لأن أي خطأ في تعديل reflection code أو annotation processing ممكن يظهر في الـ runtime بدل ما الـ compiler يكشفهولك بدري.
لما تحط الاتنين جنب بعض، القرار بيبقى واضح:
• تغيير applicationId رخيص التكلفة، لأنه مصمم أصلًا من AGP عشان يتغير بسهولة حسب كل flavor.
• تغيير namespace غالي التكلفة، لأنه بيمس جذور بنية الكود نفسها.
فلما مشروع بيتعرض لموقف زي rebrand، أو محتاج نفس الكود يطلع بأكتر من اسم (white-label app)، القرار العملي إنك تسيب namespace زي ما هو، وتخلي applicationId هو اللي بيتغير حسب الحاجة، لكل flavor على حدة.