آموزش فرایند سیستم بیلد پروژه اندروید با گردل در اندروید استودیو

در این مقاله از سری آموزش های برنامه نویسی اندروید می خواهیم به این سوالات جواب دهیم : سیستم بیلد اندروید (Android build) چیست؟ فرایند ساخت APK برای نسخه دیباگ و نسخه اصلی اندروید چگونه است؟ مراحل Build گرفتن از پروژه اندروید در اندروید استودیو چگونه است؟ تنظیمات سفارشی کردن سیستم بیلد اندروید چگونه است؟ همگام سازی پروژه با فایل های Gradle چگونه است؟ پس با مرجع آموزش برنامه نویسی اندروید الکامکو همراه باشید.

سیستم بیلد اندروید چیست؟ 

سیستم بیلد اندروید (Android build system) منابع اپلیکیشن و کد منبع را کامپایل می کند و آنها را در APK ها بسته بندی (packages) می کند که می توانید آنها را تست کنید، پیاده سازی کنید، امضا و توزیع کنید. اندروید استودیو (Android Studio) از گردل (Android Gradle) که یک ابزار پیشرفته برای مدیریت سیستم بیلد خودکار  است استفاده می کند، در حالی که به شما نیز این امکان را می دهد که تنظیمات بیلد سفارشی انعطاف پذیری را تعریف کنید.

هر پیکربندی Build اندروید می تواند ضمن استفاده مجدد از قسمت های مشترک برای همه نسخه های برنامه شما، مجموعه ای از کد و منابع خاص خود را تعریف کند. اندروید برای Gradle کردن از مجموعه ابزارها Build برای ارائه فرآیندها و تنظیمات قابل تنظیم خاص برای ساخت و آزمایش برنامه های اندرویدی استفاده می کند.

Gradle و افزونه اندرویدی (Android Plugin) مستقل از اندروید استودیو هستند. این بدان معناست که می توانید اپلیکیشن های اندرویدی خود را از طریق Android Studio و با خط فرمان در دستگاه خود یا دستگاه هایی که اندروید استودیو در آن نصب نشده است (مانند continuous integration servers) بسازید.

انعطاف پذیری سیستم بیلد اندروید شما را قادر می سازد تا تنظیمات ساخت سفارشی را بدون تغییر در فایل های منبع اصلی برنامه خود انجام دهید. این بخش به شما کمک می کند تا نحوه عملکرد سیستم بیلد اندرویدی را درک کنید و اینکه می تواند به شما در تنظیم خودکار چندین تنظیمات ساخت کمک کند.

فرایند ساخت APK برای نسخه دیباگ و نسخه اصلی اندروید : 

در کل برای برنامه نویس اندروید دو نوع خروجی وجود دارد. نسخه ی دیباگ (Debug) و نسخه ی اصلی یا نسخه ریلیز شده (Release).

فرایند بیلد گرفتن از پروژه اندروید شامل ابزارها و فرآیندهای بسیاری است که پروژه شما را به یک برنامه کاربردی اندروید (APK) تبدیل می کند. روند Build گرفتن پروژه خیلی انعطاف پذیر است، بنابراین اینکه درک خوبی از اتفاقاتی که در این فرایند می افتد داشته باشید به شما کمک می کند. عکس زیر بازخورد همین درک می باشد.

آموزش فرایند سیستم بیلد پروژه اندروید با گردل در اندروید استودیوشکل 1. فرایند بیلد یک ماژول تحت سیستم عامل اندروید

 مراحل بیلد گرفتن از پروژه برای یک ماژول معمولی برنامه اندروید : 

همانطور که در شکل 1 نشان داده شده است، این مراحل کلی را دنبال می کند:

1- کامپایلرها کد منبع شما را به فایل های (DEX (Dalvik Executable تبدیل می کنند، که شامل bytecode است و در دستگاه های اندروید اجرا می شود و سایر موارد به منابع کامپایل شده تبدیل می شوند.

2- APK Packager فایل های DEX و منابع گردآوری شده را در یک APK ترکیبی قرار می دهد. قبل از نصب و راه اندازی اپلیکیشن بر روی دستگاه اندروید توسط شما، APK باید امضا شود.

3- APK ،Packager APK شما را با استفاده از دکمه debug یا keystore امضا می کند:

  • اگر در حال ساخت نسخه دیباگ (debug version) از اپلیکیشن خود هستید، پکیجر اپلیکیشن شما را با debug keystore امضا می کند. اندروید استودیو به طور خودکار پروژه های جدید را با یک debug keystore پیکربندی می کند.
  • اگر در حال ساخت نسخه رسمی (release version) از اپلیکیشن خود هستید که قصد انتشار آن را در مارکت ها دارید، پکیجره، اپلیکیشن را با debug keystore امضا می کند.

4-قبل از تولید APK نهایی شما، پکیجر از ابزار zipalign برای بهینه سازی اپلیکیشن شما استفاده می کند تا از حافظه کمتری در هنگام اجرای دستگاه استفاده کند.
در پایان مراحل ساخت، شما یک نسخه دیباگ (debug version) یا نسخه ریلیز (release version) از اپلیکیشن خود را منتشر می کنید که می توانید از آن برای استقرار ، آزمایش یا انتشار در مارکت ها برای کاربران استفاده کنید.

تنظیمات سفارشی کردن سیستم بیلد اندروید :

گردل (Gradle) و افزونه Android به شما کمک می کند تا فرایند ساخت APK اندروید خود را سفارشی سازی کنید:

  • Build types

Build types ویژگی های خاصی را که Gradle هنگام بیلد و پکیجینگ اپلیکیشن شما استفاده می کند، تعریف می کند و به طور معمول برای مراحل مختلف چرخه توسعه شما پیکربندی می شود. به عنوان مثال، debug build type گزینه هایی را برای دیباگ تعریف می کند و APK را با debug key امضا می کند. برای ساختن اپلیکیشن حداقل باید یک نوع Build را تعریف کنید ( اندروید استودیو بطور پیش فرض انواع دیباگ را منتشر کرده و تولید می کند.)

  • Product flavors

Product flavors نسخه های مختلف اپلیکیشن شما را نشان می دهد یعنی ممکن است شما بخواهید نسخه های رایگان و پولی از اپلیکیشن خود را برای کاربران منتشر کنید. می توانید با استفاده از کد ها و منابع مختلف، ضمن استفاده از بخش هایی که برای همه نسخه های برنامه شما مشترک هستند ،Product flavors را سفارشی کنید. Product flavors اختیاری است و شما باید آنها را بصورت دستی ایجاد کنید.

  • Build variants

یک build variant ترکیبی از  build type و product flavor است و پیکربندی گردل برای ساخت برنامه شماست. با استفاده از  build type ، می توانید نسخه دیباگ product flavor خود را در حین توسعه یا نسخه های امضا شده از product flavor خود برای توزیع بسازید. اگرچه build type را به طور مستقیم پیکربندی نمی کنید ، اما  build type و product flavor را که تشکیل می دهد پیکربندی می کنید.

  • Manifest entries

می توانید مقادیر برخی از خواص فایل مانیفست را در پیکربندی build type تعیین کنید. این مقادیر ساختگی مقادیر موجود در فایل مانیفست را رد می کنند. در صورتیکه می خواهید برای ماژول های خود Multiple APK تولید کنید این کار مفید است. در این بخش هر یک از فایل های APK دارای یک نام مختلف، حداقل نسخه SDK یا نسخه SDK هدف هستند. هنگامی که مانیفست های متعدد موجود هستند، Gradle تنظیمات مانیفست را ادغام می کند.

  • Dependencies

سیستم بیلد، Dependencies پروژه را از سیستم فایل محلی شما و مخازن از راه دور مدیریت می کند. این امر باعث می شود که پکیج های باینری Dependencies های خود را در فهرست پروژه خود به صورت دستی جستجو ، بارگیری و کپی کنید.

  • Signing

سیستم بیلد اندروید به شما امکان می دهد تنظیمات امضای را در پیکربندی ساخت مشخص کنید و می تواند بطور خودکار APK های شما را در طی مراحل ساخت امضا کند. سیستم بیلد اندروید نسخه دیباگ را با یک کلید پیش فرض و گواهی نامه با استفاده از اعتبارنامه های شناخته شده امضا می کند تا از ورود سریع رمز عبور در زمان ساخت جلوگیری کند. سیستم بیلد نسخه ریلیز را امضا نمی کند مگر اینکه صریحاً پیکربندی امضای این Build را تعیین کنید. اگر  release key ندارید، می توانید یکی از مواردی را که در Sign in برنامه خود توضیح داده شده ، تولید کنید.

  • Code and resource shrinking

سیستم بیلد اندروید شما را قادر می سازد برای هر نوع ساخت یک فایل، قوانین ProGuard را تعیین کنید. هنگام ساخت اپلیکیشن، سیستم بیلد مجموعه ای از قوانین مناسب را برای کم کردن کد و منابع شما با استفاده از ابزارهای کوچک سازی داخلی آن، مانند R8 اعمال می کند.

  • Multiple APK support

سیستم بیلد اندروید شما را قادر می سازد تا بطور خودکار APK های متفاوتی بسازید که هرکدام فقط کد و منابع مورد نیاز برای تراکم صفحه نمایش خاص یا رابط باینری برنامه (ABI) را دارند.

فایل Build Configuration

ایجاد تنظیمات Build سفارشی، شما را ملزم به ایجاد تغییر در یک یا چند فایل build configuration یا فایل های build.gradle می کند. این فایل های متنی ساده از Domain Specific Language) DSL) برای توصیف و دستکاری منطق ساخت با استفاده از Groovy استفاده می کنند که یک زبان پویا برای ماشین مجازی جاوا (JVM) است. برای شروع پیکربندی Build خود نیازی به دانستن Groovy ندارید زیرا افزونه Android برای Gradle بیشتر عناصر DSL مورد نیاز شما را معرفی می کند.

هنگام شروع یک پروژه جدید، اندروید استودیو ، به طور خودکار برخی از این فایل ها را ، همانطور که در شکل 2 نشان داده شده است ، برای شما ایجاد می کند و بر اساس پیش فرض معقول آنها را جمع می کند.

فایل Build Configuration - گریدل در اندروید استودیو - آموزش برنامه نویسی اندرویدچند فایل پیکربندی Gradle build وجود دارد که جزئی از ساختار پروژه استاندارد برای یک برنامه اندرویدی است. قبل از شروع به کانفیگ Build خود ، مهم است که دامنه و هدف هر یک از این فایل ها و عناصر اصلی DSL آنها را تعریف کنید.

فایل Gradle settings

فابل settings.gradle ، که در دایرکتوری پروژه root ، به Gradle وجود دارد می گوید که ماژول های آن را باید هنگام ساخت برنامه include شوند. برای اکثر پروژه ها ، فایل ساده است و فقط موارد زیر را شامل می شود:

include ‘:app’

با این حال ، پروژه های چند ماژوله نیاز دارند تا ماژول هایی که به ساخت نهایی کمک می کند تعیین کنند.

فایل top-level build

فایل build.gradle سطح بالا که در فهرست پروژه ریشه واقع شده است، تنظیمات Build را تعریف می کند که برای همه ماژول های پروژه شما اعمال می شود. به طور پیش فرض ، فایل  top-level build  از بلوک buildscript برای تعریف مخازن و وابستگی های Gradle که برای همه ماژول های پروژه مشترک است استفاده می کند. نمونه کد زیر تنظیمات پیش فرض و عناصر DSL را می تواند بعد از ایجاد یک پروژه جدید، در build.gradl top-level بیابید.

/**
 * The buildscript block is where you configure the repositories and
 * dependencies for Gradle itself—meaning, you should not include dependencies
 * for your modules here. For example, this block includes the Android plugin for
 * Gradle as a dependency because it provides the additional instructions Gradle
 * needs to build Android app modules.
 */

buildscript {

    /**
     * The repositories block configures the repositories Gradle uses to
     * search or download the dependencies. Gradle pre-configures support for remote
     * repositories such as JCenter, Maven Central, and Ivy. You can also use local
     * repositories or define your own remote repositories. The code below defines
     * JCenter as the repository Gradle should use to look for its dependencies.
     *
     * New projects created using Android Studio 3.0 and higher also include
     * Google's Maven repository.
     */

    repositories {
        google()
        jcenter()
    }

    /**
     * The dependencies block configures the dependencies Gradle needs to use
     * to build your project. The following line adds Android plugin for Gradle
     * version 3.6.0 as a classpath dependency.
     */

    dependencies {
        classpath 'com.android.tools.build:gradle:3.6.0'
    }
}

/**
 * The allprojects block is where you configure the repositories and
 * dependencies used by all modules in your project, such as third-party plugins
 * or libraries. However, you should configure module-specific dependencies in
 * each module-level build.gradle file. For new projects, Android Studio
 * includes JCenter and Google's Maven repository by default, but it does not
 * configure any dependencies (unless you select a template that requires some).
 */

allprojects {
   repositories {
       google()
       jcenter()
   }
}

پیکربندی مشخصات project-wide

برای پروژه های اندرویدی که شامل چندین ماژول هستند، ممکن است تعریف برخی از ویژگی ها در سطح پروژه و اشتراک آنها در تمام ماژول ها مفید باشد. شما می توانید این کار را با اضافه کردن خواص اضافی به بلوک ext در فایل build.gradle top-level انجام دهید.

buildscript {...}

allprojects {...}

// This block encapsulates custom properties and makes them available to all
// modules in the project.
ext {
    // The following are only a few examples of the types of properties you can define.
    compileSdkVersion = 28
    // You can also create properties to specify versions for dependencies.
    // Having consistent versions between modules can avoid conflicts with behavior.
    supportLibVersion = "28.0.0"
    ...
}
...

برای دسترسی به این خصوصیات از یک ماژول در همان پروژه ، از syntax زیر در فایل build.gradle ماژول استفاده کنید.

android {
  // Use the following syntax to access properties you defined at the project level:
  // rootProject.ext.property_name
  compileSdkVersion rootProject.ext.compileSdkVersion
  ...
}
...
dependencies {
    implementation "com.android.support:appcompat-v7:${rootProject.ext.supportLibVersion}"
    ...
}

فایل module-level build

فایل build.gradle در سطح ماژول ، که در هر project/module/ directory قرار دارد ، به شما امکان می دهد تنظیمات Build را برای ماژول خاصی که در آن قرار دارد پیکربندی کنید. پیکربندی این تنظیمات به شما امکان می دهد گزینه های پکیجینگ سفارشی ، مانند  build types اضافی را ارائه دهید. و product flavors ، و تنظیمات در فایلbuild.gradle  top-level یا main/ app manifest نادیده گرفته می شوند.

این نمونه ماژول برنامه Android build build.gradle برخی از عناصر و تنظیمات اصلی DSL را مشخص می کند که باید بدانید.

/**
 * The first line in the build configuration applies the Android plugin for
 * Gradle to this build and makes the android block available to specify
 * Android-specific build options.
 */

apply plugin: 'com.android.application'

/**
 * The android block is where you configure all your Android-specific
 * build options.
 */

android {

  /**
   * compileSdkVersion specifies the Android API level Gradle should use to
   * compile your app. This means your app can use the API features included in
   * this API level and lower.
   */

  compileSdkVersion 28

  /**
   * buildToolsVersion specifies the version of the SDK build tools, command-line
   * utilities, and compiler that Gradle should use to build your app. You need to
   * download the build tools using the SDK Manager.
   *
   * This property is optional because the plugin uses a recommended version of
   * the build tools by default.
   */

  buildToolsVersion "29.0.2"

  /**
   * The defaultConfig block encapsulates default settings and entries for all
   * build variants, and can override some attributes in main/AndroidManifest.xml
   * dynamically from the build system. You can configure product flavors to override
   * these values for different versions of your app.
   */

  defaultConfig {

    /**
     * applicationId uniquely identifies the package for publishing.
     * However, your source code should still reference the package name
     * defined by the package attribute in the main/AndroidManifest.xml file.
     */

    applicationId 'com.example.myapp'

    // Defines the minimum API level required to run the app.
    minSdkVersion 15

    // Specifies the API level used to test the app.
    targetSdkVersion 28

    // Defines the version number of your app.
    versionCode 1

    // Defines a user-friendly version name for your app.
    versionName "1.0"
  }

  /**
   * The buildTypes block is where you can configure multiple build types.
   * By default, the build system defines two build types: debug and release. The
   * debug build type is not explicitly shown in the default build configuration,
   * but it includes debugging tools and is signed with the debug key. The release
   * build type applies Proguard settings and is not signed by default.
   */

  buildTypes {

    /**
     * By default, Android Studio configures the release build type to enable code
     * shrinking, using minifyEnabled, and specifies the default Proguard rules file.
     */

    release {
        minifyEnabled true // Enables code shrinking for the release build type.
        proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
    }
  }

  /**
   * The productFlavors block is where you can configure multiple product flavors.
   * This allows you to create different versions of your app that can
   * override the defaultConfig block with their own settings. Product flavors
   * are optional, and the build system does not create them by default.
   *
   * This example creates a free and paid product flavor. Each product flavor
   * then specifies its own application ID, so that they can exist on the Google
   * Play Store, or an Android device, simultaneously.
   *
   * If you declare product flavors, you must also declare flavor dimensions
   * and assign each flavor to a flavor dimension.
   */

  flavorDimensions "tier"
  productFlavors {
    free {
      dimension "tier"
      applicationId 'com.example.myapp.free'
    }

    paid {
      dimension "tier"
      applicationId 'com.example.myapp.paid'
    }
  }

  /**
   * The splits block is where you can configure different APK builds that
   * each contain only code and resources for a supported screen density or
   * ABI. You'll also need to configure your build so that each APK has a
   * different versionCode.
   */

  splits {
    // Settings to build multiple APKs based on screen density.
    density {

      // Enable or disable building multiple APKs.
      enable false

      // Exclude these densities when building multiple APKs.
      exclude "ldpi", "tvdpi", "xxxhdpi", "400dpi", "560dpi"
    }
  }
}

/**
 * The dependencies block in the module-level build configuration file
 * specifies dependencies required to build only the module itself.
 * To learn more, go to Add build dependencies.
 */

dependencies {
    implementation project(":lib")
    implementation 'com.android.support:appcompat-v7:28.0.0'
    implementation fileTree(dir: 'libs', include: ['*.jar'])
}

فایل Gradle properties :

Gradle همچنین شامل دو فایل خاص در دایرکتوری پروژه root شما می باشد که می توانید از آنها برای تنظیم تنظیمات مربوط به مجموعه ابزار Gradle build خود استفاده کنید:

gradle.properties
اینجاست که می توانید تنظیمات project-wide Gradle را مانند حداکثر اندازه Gradle daemon’s تنظیم کنید.
local.properties
خصوصیات local environment را برای سیستم ساخت را تنظیم می کند ، از جمله موارد زیر:
  • ndk.dir – مسیر NDK. این خاصیت از بین رفته است. هر نسخه بارگیری شده از NDK در فهرست ndk در داخل فهرست SDK Android نصب خواهد شد
  • sdk.dir – مسیر SDK
  • cmake.dir – مسیر CMake
  • ndk.symlinkdir – در  +Android Studio 3.5 ، یک راه ارتباطی با NDK ایجاد می کند که می تواند از مسیر نصب شده NDK کوتاه تر باشد

NDK را به مسیری کوتاه تر (فقط Windows) انتقال دهید

رایج ترین مسئله در مورد مسیرهای طولانی ویندوز این است که ابزارهای (ld.exe)در پوشه نصب شده NDK با مسیری بسیار عمیق به پایان می رسند ، اما ابزارها از مسیرهای طولانی به خوبی پشتیبانی نمی کنند.

در local.properties ، می توانید ویژگی ndk.symlinkdir را تنظیم کنید تا درخواست کنید افزونه Gradle یک سیملینک (symlink) را برای NDK ایجاد کند.

مسیر symlink کوتاهتر از پوشه NDK موجود است. به عنوان مثال ، \:ndk.symlinkdir = C منجر به پیوند زیر می شود: C:\ndk\19.0.5232133

همگام سازی پروژه با فایل های Gradle

هنگامی که در پروژه خود تغییراتی در فایل های پیکربندی بیلد ایجاد می کنید ، اندروید استودیو نیاز دارد که فایل های پروژه تان را همگام سازی کنید تا بتواند تغییرات پیکربندی بیلد شما را وارد کند و برخی از بروزرسانی ها را اجرا کند تا مطمئن شوید که پیکربندی شما باعث ایجاد خطاهای ساخت نمی شود.

برای همگام سازی فایل های پروژه خود ، مطابق شکل 3 روی Sync Now در نوتیفیکیشن بار کلیک کنید. یا از نوار منو روی Sync Project کلیک کنید. اگر اندروید استودیو در پیکربندی شما متوجه هرگونه خطایی شود، پنجره Messages ظاهر می شود تا اطلاعاتی دراین باره به شما دهد.

فایل Source sets : 

اندروید استودیو به طور منطقی کد منبع و منابع را برای هر ماژول در فایل source sets قرار می دهد. ماژول /main شامل کد و منابعی است که توسط همه build variants آن استفاده می شود. به علاوه دایرکتوری های source set اختیاری هستند و هنگامی که build variants جدید را پیکربندی می کنید ، اندروید استودیو به طور خودکار آنها را برای شما ایجاد نمی کند. با این حال ، ایجاد source set، شبیه به /main ، به سازماندهی فایل ها و منابعی که Gradle فقط هنگام ساخت نسخه های خاص برنامه شما کمک می کند ، کمک می کند:

src / main /

این مجموعه منبع شامل کد و منابع مشترک برای همه  build variants است.

src / buildType /

این منبع را ایجاد کنید تا کد و منابع را فقط برای یک build type خاص شامل کنید.

src / productFlavor /

این منبع را ایجاد کنید تا کد و منابع را فقط برای یک product flavor بسازد.

src / productFlavorBuildType /

این منبع را ایجاد کنید تا کدها و منابع را فقط برای یک build variant خاص شامل کنید.

به عنوان مثال ، برای تولید نسخه “fullDebug” برنامه شما ، سیستم Build کد ، تنظیمات و منابع را با source set زیر ادغام می کند:

  • src/fullDebug/ (the build variant source set)
  • src/debug/ (the build type source set)
  • src/full/ (the product flavor source set)
  • src/main/ (the main source set)

توجه: وقتی یک فایل یا فهرست جدید را در اندروید استودیو ایجاد می کنید، با استفاده از منو File> New ، می توانید آن را برای یک source set تهیه کنید. source set انتخابی شما بر اساس تنظیمات Build شما است و اندروید استودیو در صورت وجود آنها به طور خودکار فهرستهای مورد نیاز را ایجاد می کند.

اگر source sets مختلف دارای نسخه های مختلف با همان فایل باشد ، Gradle هنگام تصمیم گیری که از کدام فایل استفاده کند، از دستور اولویت زیر استفاده می کند (فایل های Source sets در سمت چپ و تنظیمات Source sets در سمت راست):

build variant > build type > product flavor > main source set > library dependencies

این امر به Gradle اجازه می دهد تا هنگام استفاده مجدد از فعالیت ها ، منطق برنامه و منابعی که برای سایر نسخه های برنامه شما مشترک است از فایل هایی استفاده کند که مختص نسخه ساختاری است که می خواهید در آنها بسازید. هنگام ادغام مانیفست های متعدد ، Gradle از همان ترتیب اولویت استفاده می کند، بنابراین هر build variant می تواند مؤلفه ها یا مجوزهای مختلف را در مانیفست نهایی تعریف کند.

امیدواریم از این مقاله خوشتان آمده باشد.

جدیدترین دوره ها
آموزش ساخت اپلیکیشن فروشگاهی اندروید دیجی کالا Digikala - سورس دیجی کالا php - الکامکو
دوره آموزش ساخت اپلیکیشن اندروید فیلیمو - خرید اشتراک فیلیمو - خرید اشتراک فیلم - برنامه فیلیمو برای اندروید - ساخت اپلیکیشن فیلم و سریال - ساخت برنامه فیلیمو | مرجع آموزش برنامه نویسی اندروید الکامکو
آموزش طراحی رابط کاربری (طراحی UI اندروید) و آموزش طراحی تجربه کاربری (طراحی UX اندروید) - آموزش برنامه نویسی اندروید الکامکو
مشاهده همه دوره های آموزش برنامه نویسی اندروید الکامکو
{ آموزشگاه آنلاین الکامکو }

مرجع آموزش برنامه نویسی اندروید
طراحی اپلیکیشن موبایل
طراحی سایت

این قسمت از آموزش برنامه نویسی اندروید الکامکو که درباره Build پروژه در اندروید استودیو است چطور بود؟ در صورتی که سوال و  یا نظری در این رابطه دارید خوشحال می شویم زیر همین پست با ما درمیان بگذارید.

اشتراک گذاری این صفحه در شبکه های اجتماعی:

سوالات و نظرات خود را در این بخش مطرح کنید

avatar
فهرست