קישור של Gradle לספריית המקורית

כדי לכלול את הפרויקט של ספריית הנייטיב כתלות ב-build של Gradle, צריך כדי לספק ל-Gradle את הנתיב לקובץ הסקריפט CMake או ndk-build. מתי שאתם מפתחים את האפליקציה שלכם, Gradle מפעילה את CMake או ndk-build, וחבילות משותפות ספריות עם האפליקציה שלכם. Gradle משתמשת גם בסקריפט של build כדי לדעת אילו קבצים ניגשים לפרויקט Android Studio, כך שאפשר לגשת אליהם החלון Project. אם אין לכם סקריפט build ל-Native מקורות, צריך ליצור Cצור סקריפט build לפני שתמשיך.

כל מודול בפרויקט Android יכול לקשר ל-CMake או ל-ndk-build אחד בלבד סקריפט. לדוגמה, אם אתם רוצים ליצור ולארוז פלטים מספר פרויקטים של CMake, יש להשתמש בקובץ CMakeLists.txt אחד בתור סקריפט ה-build ברמה העליונה של CMake (שאליו מקשרים את Gradle) הוספת פרויקטים אחרים של CMake בתור יחסי התלות של סקריפט ה-build הזה. באותו האופן, אם משתמשים ב-ndk-build, יכול לכלול קובצי מאכל אחרים ברמה העליונה Android.mk קובץ סקריפט.

אחרי שמקשרים את Gradle לפרויקט מקורי, Android Studio מעדכן את החלונית Project כדי להציג את קובצי המקור והספריות המקוריות בקבוצה cpp, ואת הסקריפטים החיצוניים של build קבוצה קובצי Build חיצוניים.

הערה: כשמשנים את ההגדרות של Gradle, חשוב להקפיד כדי להחיל את השינויים, לוחצים על סנכרון הפרויקט בסרגל הכלים. בנוסף, כשמבצעים שינויים ב-CMake או ב-ndk-build את קובץ הסקריפט אחרי שכבר קישרתם אותו ל-Gradle, עליכם לסנכרן אותו. ב-Android Studio עם השינויים שביצעת באמצעות בחירה באפשרות פיתוח > רענון C++ המקושר פרויקטים בסרגל התפריטים.

אפשר לקשר את Gradle לפרויקט CMake חיצוני או לפרויקט ndk-build באמצעות ממשק המשתמש של Android Studio:

  1. פותחים את החלונית Project (פרויקט) מהצד השמאלי של סביבת הפיתוח המשולבת (IDE), בוחרים בתצוגת Android.
  2. לוחצים לחיצה ימנית על המודול שרוצים לקשר לספריית הנייטיב, כמו המודול app, ובחר קישור C++ פרויקט עם Gradle מהתפריט. אמורה להופיע תיבת דו-שיח שדומה לזו אחת שמוצגת באיור 4.
  3. בתפריט הנפתח, בוחרים באפשרות CMake או ndk-build.
    1. אם בוחרים באפשרות CMake, משתמשים בשדה שלצד נתיב הפרויקט לציון הסקריפט CMakeLists.txt עבור פרויקט CMake החיצוני.
    2. אם בוחרים באפשרות ndk-build, צריך להשתמש בשדה שליד Project Path כדי לציין את קובץ הסקריפט Android.mk עבור את פרויקט ה-ndk-build החיצוני שלכם. Android Studio כולל גם את Application.mk אם הוא נמצא באותה ספרייה שבה נמצא קובץ Android.mk.

    איור 4. קישור של פרויקט C++ חיצוני באמצעות תיבת דו-שיח ל-Android Studio.

  4. לוחצים על אישור.

הגדרה ידנית של Gradle

כדי להגדיר ב-Gradle באופן ידני קישור לספרייה המקורית, צריך להוסיף ה externalNativeBuild חסום ברמת המודול build.gradle ולהגדיר אותו באמצעות cmake או ndkBuild חסימה:

מגניב

android {
  ...
  defaultConfig {...}
  buildTypes {...}

  // Encapsulates your external native build configurations.
  externalNativeBuild {

    // Encapsulates your CMake build configurations.
    cmake {

      // Provides a relative path to your CMake build script.
      path "CMakeLists.txt"
    }
  }
}

Kotlin

android {
  ...
  defaultConfig {...}
  buildTypes {...}

  // Encapsulates your external native build configurations.
  externalNativeBuild {

    // Encapsulates your CMake build configurations.
    cmake {

      // Provides a relative path to your CMake build script.
      path = file("CMakeLists.txt")
    }
  }
}

הערה: אם רוצים לקשר את Gradle לקובץ ndk-build קיים הפרויקט, השתמשו ב חסימת ndkBuild במקום cmake ומספקים נתיב יחסי לקובץ Android.mk. גם Gradle כולל את הקובץ Application.mk, אם נמצא באותה ספרייה כמו הקובץ Android.mk.

ציון הגדרות אופציונליות

ניתן לציין ארגומנטים ודגלים אופציונליים עבור CMake או ndk-build לפי הגדרת נוסף externalNativeBuild בתוך בלוק אחד (defaultConfig) ברמת המודול שלך קובץ build.gradle. בדומה לנכסים אחרים במלון defaultConfig, אפשר לשנות את המאפיינים האלה לכל אחד מהם ולשפר את הטעם של המוצר בתצורת ה-build שלך.

לדוגמה, אם בפרויקט CMake או ndk-build מוגדרים כמה רכיבי Native אפשר להשתמש בפקודה targets כדי ליצור ולקבץ רק קבוצת משנה של הרכיבים האלה של טעם מסוים של מוצר. דוגמת הקוד הבאה מתארת חלק מהמאפיינים שאפשר להגדיר:

מגניב

android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake or ndk-build script.
    externalNativeBuild {

      // For ndk-build, instead use the ndkBuild block.
      cmake {

        // Passes optional arguments to CMake.
        arguments "-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang"

        // Sets a flag to enable format macro constants for the C compiler.
        cFlags "-D__STDC_FORMAT_MACROS"

        // Sets optional flags for the C++ compiler.
        cppFlags "-fexceptions", "-frtti"
      }
    }
  }

  buildTypes {...}

  productFlavors {
    ...
    demo {
      ...
      externalNativeBuild {
        cmake {
          ...
          // Specifies which native libraries or executables to build and package
          // for this product flavor. The following tells Gradle to build only the
          // "native-lib-demo" and "my-executible-demo" outputs from the linked
          // CMake project. If you don't configure this property, Gradle builds all
          // executables and shared object libraries that you define in your CMake
          // (or ndk-build) project. However, by default, Gradle packages only the
          // shared libraries in your app.
          targets "native-lib-demo",
                  // You need to specify this executable and its sources in your CMakeLists.txt
                  // using the add_executable() command. However, building executables from your
                  // native sources is optional, and building native libraries to package into
                  // your app satisfies most project requirements.
                  "my-executible-demo"
        }
      }
    }

    paid {
      ...
      externalNativeBuild {
        cmake {
          ...
          targets "native-lib-paid",
                  "my-executible-paid"
        }
      }
    }
  }

  // Use this block to link Gradle to your CMake or ndk-build script.
  externalNativeBuild {
    cmake {...}
    // or ndkBuild {...}
  }
}

Kotlin

android {
  ...
  defaultConfig {
    ...
    // This block is different from the one you use to link Gradle
    // to your CMake or ndk-build script.
    externalNativeBuild {

      // For ndk-build, instead use the ndkBuild block.
      cmake {

        // Passes optional arguments to CMake.
        arguments += listOf("-DANDROID_ARM_NEON=TRUE", "-DANDROID_TOOLCHAIN=clang")

        // Sets a flag to enable format macro constants for the C compiler.
        cFlags += listOf("-D__STDC_FORMAT_MACROS")

        // Sets optional flags for the C++ compiler.
        cppFlags += listOf("-fexceptions", "-frtti")
      }
    }
  }

  buildTypes {...}

  productFlavors {
    ...
    create("demo") {
      ...
      externalNativeBuild {
        cmake {
          ...
          // Specifies which native libraries or executables to build and package
          // for this product flavor. The following tells Gradle to build only the
          // "native-lib-demo" and "my-executible-demo" outputs from the linked
          // CMake project. If you don't configure this property, Gradle builds all
          // executables and shared object libraries that you define in your CMake
          // (or ndk-build) project. However, by default, Gradle packages only the
          // shared libraries in your app.
          targets += listOf("native-lib-demo",
                  // You need to specify this executable and its sources in your CMakeLists.txt
                  // using the add_executable() command. However, building executables from your
                  // native sources is optional, and building native libraries to package into
                  // your app satisfies most project requirements.
                  "my-executible-demo")
        }
      }
    }

    create("paid") {
      ...
      externalNativeBuild {
        cmake {
          ...
          targets += listOf("native-lib-paid",
                  "my-executible-paid")
        }
      }
    }
  }

  // Use this block to link Gradle to your CMake or ndk-build script.
  externalNativeBuild {
    cmake {...}
    // or ndkBuild {...}
  }
}

לקבלת מידע נוסף על הגדרת טעמים של מוצרים ועל יצירת וריאציות, אפשר לעבור אל הגדרת וריאציות ב-Build עבור רשימה של משתנים שאפשר להגדיר ל-CMake באמצעות arguments, ראו שימוש במשתני CMake.

הכללה של ספריות מותאמות מוכנות מראש

אם רוצים ש-Gradle לארוז מראש ספריות מקוריות מוכנות מראש שלא נעשה בהן שימוש build מקורי חיצוני, צריך להוסיף אותם אל src/main/jniLibs/ABI של המודול.

נדרשות גרסאות של הפלאגין Android Gradle לפני 4.0, כולל CMake IMPORTED יעדים בספרייה שלך ב-jniLibs כדי שהם ייכללו אפליקציה. אם אתה עובר מגרסה קודמת של הפלאגין, ייתכן תופיע שגיאה כמו:

* What went wrong:
Execution failed for task ':app:mergeDebugNativeLibs'.
> A failure occurred while executing com.android.build.gradle.internal.tasks.Workers$ActionFacade
   > More than one file was found with OS independent path 'lib/x86/libprebuilt.so'

אם משתמשים ב-Android Gradle Plugin 4.0, צריך להעביר את כל הספריות שנמצאות בשימוש על ידי IMPORTED כדי להימנע מהשגיאה הזו, יש ליצור יעדים מתוך הספרייה jniLibs.

ציון ממשקי ABI

כברירת מחדל, Gradle יוצרת את ספריית הנייטיב שלך לתוך .so נפרדת עבור Application Binary Interface (ABI) ש-NDK תומך ומכיל את כולן באפליקציה שלך. אם רוצים Gradle ליצירה ולאריזה של תצורות ABI מסוימות בלבד אפשר לציין אותן באמצעות ndk.abiFilters בקובץ build.gradle ברמת המודול, כפי שמוצג בהמשך:

מגניב

android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      cmake {...}
      // or ndkBuild {...}
    }

    // Similar to other properties in the defaultConfig block,
    // you can configure the ndk block for each product flavor
    // in your build configuration.
    ndk {
      // Specifies the ABI configurations of your native
      // libraries Gradle should build and package with your app.
      abiFilters 'x86', 'x86_64', 'armeabi', 'armeabi-v7a',
                   'arm64-v8a'
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

Kotlin

android {
  ...
  defaultConfig {
    ...
    externalNativeBuild {
      cmake {...}
      // or ndkBuild {...}
    }

    // Similar to other properties in the defaultConfig block,
    // you can configure the ndk block for each product flavor
    // in your build configuration.
    ndk {
      // Specifies the ABI configurations of your native
      // libraries Gradle should build and package with your app.
      abiFilters += listOf("x86", "x86_64", "armeabi", "armeabi-v7a",
                   "arm64-v8a")
    }
  }
  buildTypes {...}
  externalNativeBuild {...}
}

ברוב המקרים, צריך לציין רק abiFilters בלוק ndk, כפי שמוצג למעלה, כי הוא מורה ל-Gradle ואריזתם את הגרסאות האלה של הספריות המקוריות שלכם. אבל אם רוצים שליטה על מה ש-Gradle צריך ליצור, בלי קשר למה שרוצים שהוא מוסיפים את החבילה לאפליקציה, צריך להגדיר דגל abiFilters נוסף חסימת defaultConfig.externalNativeBuild.cmake (או defaultConfig.externalNativeBuild.ndkBuild). גרדל יוצר את הגדרות ה-ABI האלה אלא רק את החבילות שקבעתם defaultConfig.ndk.

מומלץ לפרסם באמצעות קובצי Android App Bundle כדי לצמצם עוד יותר את גודל האפליקציה שלך, כלומר רק ספריות מקוריות שתואמות לממשק ה-ABI של המשתמש המכשיר יסופק עם ההורדה.

לגבי אפליקציות מדור קודם שמפרסמים באמצעות חבילות APK (שנוצרו לפני אוגוסט 2021), כדאי לשקול את האפשרות הגדרה כמה חבילות APK על סמך ממשק ABI – במקום ליצור APK גדול אחד עם כל חבילות ה-APK גרסאות של ספריות ה-ABI שלך, Gradle יוצרת APK נפרד לכל ABI שרוצים לתמוך בהם ואריזתם רק את הקבצים שדרושים לכל ממשק ABI. אם להגדיר כמה חבילות APK לכל ממשק ABI בלי לציין את הקוד סימון abiFilters כמו בדוגמת הקוד שלמעלה, Gradle את כל גרסאות ה-ABI הנתמכות של הספריות המקוריות, אבל רק חבילות אותן שציינת בתצורה המרובת של ה-APK. כדי להימנע מבניית גרסאות של את הספריות המקוריות שלא רוצים ליצור, לספק את אותה רשימה של ממשקי ABI גם הדגל abiFilters וגם ה-APK המרוב לכל ABI הגדרה אישית.