Gradle Implementation กับการกำหนดค่า API


231

ฉันพยายามคิดออกว่าอะไรคือความแตกต่างระหว่างapiและimplementationการกำหนดค่าในขณะที่สร้างการอ้างอิงของฉัน
ในเอกสารอธิบายว่ามันimplementationมีเวลาในการสร้างที่ดีขึ้น แต่เมื่อเห็นความคิดเห็นนี้ ในคำถามที่คล้ายกันฉันก็สงสัยว่ามันจริงหรือเปล่า
เนื่องจากฉันไม่มีความเชี่ยวชาญในการไล่ระดับฉันหวังว่าจะมีคนช่วย ฉันอ่านเอกสารแล้ว แต่ฉันสงสัยเกี่ยวกับคำอธิบายที่เข้าใจง่าย


1
คุณอ่านที่นี่ ?
MatPag

ตามความเป็นจริงฉันทำ แต่อย่างที่ฉันพูดความคิดเห็นนั้นทำให้สงสัยเกี่ยวกับมัน ดังนั้นตอนนี้ฉันหายไปแล้ว
reinaldomoreira

คุณอาจจะเปลี่ยนการอ้างอิงห้องสมุดของคุณจากไปcompile apiไลบรารีที่คุณใช้ภายในสามารถใช้การใช้งานส่วนตัวบางอย่างที่ไม่ได้ปรากฏในไลบรารีสุดท้ายเพื่อให้โปร่งใสสำหรับคุณ การพึ่งพา "ภายใน - ส่วนตัว" เหล่านั้นสามารถเปลี่ยนไปใช้implementationและเมื่อปลั๊กอินการไล่ระดับสีของ Android จะรวบรวมแอปของคุณมันจะข้ามการรวบรวมการพึ่งพาเหล่านั้นส่งผลให้เวลาสร้างมีขนาดเล็กลง (แต่การพึ่งพาเหล่านั้นจะพร้อมใช้งาน เห็นได้ชัดว่าคุณสามารถทำสิ่งเดียวกันถ้าคุณมีห้องสมุดโมดูลท้องถิ่น
MatPag

1
ต่อไปนี้เป็นคำอธิบายกราฟิกสั้น ๆ ของ 'api' และ 'การใช้งาน': jeroenmols.com/blog/2017/06/14/androidstudio3
albert c braun

1
นั่นเป็นโพสต์ที่ยอดเยี่ยม! ขอบคุณ @albertbraun
reinaldomoreira

คำตอบ:


418

compileคำหลักGradle ถูกคัดค้านapiและimplementationคำหลักเพื่อกำหนดค่าการอ้างอิง

การใช้apiจะเทียบเท่ากับการใช้ที่เลิกใช้แล้วcompileดังนั้นหากคุณแทนที่ทั้งหมดcompileด้วยapiทุกอย่างจะทำงานเหมือนเคย

เพื่อให้เข้าใจถึงimplementationคำสำคัญให้พิจารณาตัวอย่างต่อไปนี้

ตัวอย่าง

สมมติว่าคุณมีไลบรารีที่เรียกMyLibraryว่าภายในใช้ไลบรารีอื่นที่เรียกว่าInternalLibraryภายใน บางสิ่งเช่นนี้

    // 'InternalLibrary' module
    public class InternalLibrary {
        public static String giveMeAString(){
            return "hello";
        }
    }
    // 'MyLibrary' module
    public class MyLibrary {
        public String myString(){
            return InternalLibrary.giveMeAString();
        }
    }

สมมติว่าการกำหนดค่าการMyLibrary build.gradleใช้งานapiในdependencies{}ลักษณะนี้:

dependencies {
    api project(':InternalLibrary')
}

คุณต้องการใช้MyLibraryในรหัสของคุณดังนั้นในแอปของbuild.gradleคุณคุณเพิ่มการพึ่งพานี้:

dependencies {
    implementation project(':MyLibrary')
}

การใช้การapiกำหนดค่า (หรือเลิกใช้compile) คุณสามารถเข้าถึงInternalLibraryในรหัสแอปพลิเคชันของคุณ:

// Access 'MyLibrary' (granted)
MyLibrary myLib = new MyLibrary();
System.out.println(myLib.myString());

// Can ALSO access the internal library too (and you shouldn't)
System.out.println(InternalLibrary.giveMeAString());

ด้วยวิธีนี้โมดูลMyLibraryอาจ "รั่ว" การใช้งานภายในบางสิ่งบางอย่าง คุณไม่ควร (สามารถ) ใช้สิ่งนี้เพราะคุณไม่ได้นำเข้าโดยตรง

การimplementationกำหนดค่าได้รับการแนะนำเพื่อป้องกันสิ่งนี้ ดังนั้นตอนนี้ถ้าคุณใช้implementationแทนapiในMyLibrary:

dependencies {
    implementation project(':InternalLibrary')
}

คุณจะไม่สามารถโทรInternalLibrary.giveMeAString()ในรหัสแอพของคุณอีกต่อไป

การจัดเรียงของกลยุทธ์นี้จะช่วยให้มวย Android Gradle ปลั๊กอินที่จะรู้ว่าถ้าคุณแก้ไขในบางสิ่งบางอย่างInternalLibraryก็ต้องเรียก recompilation ของMyLibraryและไม่ recompilation ของ app InternalLibraryทั้งหมดของคุณเพราะคุณไม่ได้มีการเข้าถึง

เมื่อคุณมีการพึ่งพาที่ซ้อนกันจำนวนมากกลไกนี้สามารถเร่งการสร้างได้มาก (ดูวิดีโอที่เชื่อมโยงในตอนท้ายเพื่อความเข้าใจอย่างสมบูรณ์)

สรุป

  • เมื่อคุณสลับไปใหม่ Android Gradle ปลั๊กอิน 3.XX คุณควรแทนที่ทั้งหมดของคุณcompileกับimplementationคำหลัก(1 *) จากนั้นลองรวบรวมและทดสอบแอปของคุณ หากทุกอย่างไม่เป็นไรปล่อยให้รหัสเป็นอยู่ถ้าคุณมีปัญหาคุณอาจมีบางสิ่งผิดปกติกับการอ้างอิงของคุณหรือคุณใช้สิ่งที่ตอนนี้เป็นส่วนตัวและไม่สามารถเข้าถึงได้มากขึ้น คำแนะนำโดยวิศวกรปลั๊กอินของ Android Gradle Jerome Dochez (1 ) * )

  • หากคุณเป็นผู้ดูแลห้องสมุดคุณควรใช้apiสำหรับการอ้างอิงทุกครั้งที่จำเป็นสำหรับ API สาธารณะของห้องสมุดของคุณในขณะที่ใช้implementationสำหรับการทดสอบอ้างอิงหรือการอ้างอิงที่ผู้ใช้ขั้นสุดท้ายจะต้องไม่ใช้

บทความที่มีประโยชน์แสดงให้เห็นถึงความแตกต่างระหว่างการใช้งานและAPI

ข้อมูลอ้างอิง (วิดีโอนี้แบ่งออกเป็นวิดีโอชุดเดียวกันเพื่อประหยัดเวลา)

Google I / O 2017 - วิธีสร้าง Gradle ให้เร็วขึ้น (วิดีโอเต็ม)

Google I / O 2017 - Gradle สร้างความเร็วได้อย่างไร (ปลั๊กอิน GRADLE ใหม่ 3.0.0 ส่วนเท่านั้น)

Google I / O 2017 - วิธีสร้าง Gradle ให้เร็วขึ้น (อ้างอิง1 * )

เอกสารประกอบ Android


4
ฉันสังเกตเห็นว่า api ทำงานได้ไม่ดีในโมดูลห้องสมุด ถ้าฉันใช้มันฉันยังคงไม่สามารถเข้าถึงการพึ่งพาจากโครงการแอพของฉัน ฉันสามารถเข้าถึงรหัสในไลบรารีนั้นได้เท่านั้น
อัลลัน W

1
สิ่งนี้ใช้ได้และทำงานกับการดีบัก - สร้าง แต่เมื่อใช้ ProGuard (ในรุ่นที่วางจำหน่าย) MyLibrary#myString()จะมีปัญหาเพราะ ProGuard จะInternalLibraryถูกลบ แนวปฏิบัติที่ดีที่สุดสำหรับ Android-libs ที่จะใช้ในแอป ProGuard'ed คืออะไร
hardysim

3
ฉันคิดว่าคำตอบนั้นไม่ถูกต้องแอปพลิเคชันสามารถใช้ขอบเขตใดก็ได้ที่ต้องการสำหรับ MyLibrary มันจะเห็นหรือไม่ InternalLibrary ขึ้นอยู่กับว่า MyLibrary ใช้ api / การใช้งานหรือไม่
Snicolas

2
ขอบคุณชาย คำอธิบายที่ยอดเยี่ยมมากกว่าเอกสารทางการของ Android
Henry

2
นั่นเป็นคำอธิบายที่สวยงาม ทฤษฏีและคอนกรีตผสมกันอย่างยอดเยี่ยม ทำได้ดี. ขอบคุณสำหรับสิ่งนั้น
Peter Kahn

134

ฉันชอบคิดถึงการapiพึ่งพาเป็นสาธารณะ (มองเห็นโดยโมดูลอื่น) ในขณะที่การimplementationพึ่งพาเป็นส่วนตัว (มองเห็นได้เฉพาะโดยโมดูลนี้)

โปรดทราบว่าแตกต่างจากpublic/ privateตัวแปรและวิธีการapi/ การimplementationอ้างอิงไม่ได้บังคับใช้โดยรันไทม์ นี่เป็นเพียงการปรับให้เหมาะสมแบบ build-time ซึ่งอนุญาตให้Gradleรู้ได้ว่าโมดูลใดที่จำเป็นต้องคอมไพล์ใหม่เมื่อการพึ่งพาตัวใดตัวหนึ่งเปลี่ยน API ของมัน


16
รักความเรียบง่ายของคำตอบนี้ขอบคุณมาก
Kevin Gilles

2
ความแตกต่างที่แท้จริง (AFAICT) คือไฟล์ pom ที่สร้างทำให้การapiอ้างอิงในขอบเขต "คอมไพล์" (ซึ่งจะรวมอยู่ในการอ้างอิงในไลบรารีของคุณและสิ่งใดก็ตามที่ขึ้นอยู่กับไลบรารีของคุณ) และการimplementationพึ่งพาในขอบเขต "รันไทม์" classpath เมื่อรหัสของคุณกำลังทำงาน แต่ไม่จำเป็นต้องรวบรวมรหัสอื่นที่ใช้ไลบรารีของคุณ)
Shadow Man

@ShadowMan มันเป็นรายละเอียดการติดตั้งปลั๊กอินที่รับผิดชอบในการสร้างไฟล์ POM มันทำแผนที่ขอบเขตGradleกับขอบเขตMavenได้อย่างไร
dev.bmax

1
คุณควรใช้implementationสำหรับการอ้างอิงใด ๆ ที่จำเป็นในการเรียกใช้ (และสำหรับไลบรารีของคุณเพื่อคอมไพล์) แต่ไม่ควรดึงเข้ามาในโครงการที่ใช้ไลบรารีของคุณโดยอัตโนมัติ ตัวอย่างเช่น jax-rs, ไลบรารีของคุณอาจใช้ RESTeasy แต่ไม่ควรดึง libs เหล่านั้นไปยังโครงการใด ๆ ที่ใช้ไลบรารีของคุณเนื่องจากพวกเขาอาจต้องการใช้ Jersey แทน
Shadow Man

1
นี้เป็นวิธีที่คุณรู้จักใครได้รับสิ่งที่มัน: D ขอบคุณสำหรับความเรียบง่ายและคำตอบที่ชัดเจน
อีเลียส Fazel

12

พิจารณาว่าคุณมีappโมดูลที่ใช้lib1เป็นห้องสมุดและlib1ใช้lib2เป็นห้องสมุด บางสิ่งเช่นนี้: app -> lib1 -> lib2.

ตอนนี้เมื่อใช้งานapi lib2ในlib1นั้นapp จะเห็น lib2รหัสเมื่อใช้: api lib1หรือimplementation lib1ในappโมดูล

แต่เมื่อใช้implementation lib2ในlib1แล้วapp ไม่สามารถดูlib2รหัส


5

คำตอบจาก@matpagและ@ dev-bmaxมีความชัดเจนเพียงพอที่จะทำให้ผู้คนเข้าใจประเพณีที่แตกต่างระหว่างการใช้งานและ API ฉันแค่ต้องการอธิบายเพิ่มเติมจากมุมมองอื่นหวังว่าจะช่วยคนที่มีคำถามเดียวกัน

ฉันสร้างสองโครงการสำหรับการทดสอบ:

  • โปรเจ็กต์ Aในฐานะโปรเจ็กต์ไลบรารี java ชื่อ 'frameworks-web-gradle-plugin' ขึ้นอยู่กับ 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE'
  • โครงการ Bขึ้นอยู่กับโครงการ A โดยการใช้งาน 'com.example.frameworks.gradle: frameworks-web-gradle-plugin: 0.0.1-SNAPSHOT'

ลำดับชั้นการพึ่งพาที่อธิบายข้างต้นดูเหมือนว่า:

[project-b] -> [project-a] -> [spring-boot-gradle-plugin]

จากนั้นฉันทดสอบสถานการณ์ต่อไปนี้:

  1. โครงการทำขึ้นอยู่กับ 'org.springframework.boot: ฤดูใบไม้ผลิบูต gradle ปลั๊กอิน: 1.5.20.RELEASE โดยการดำเนินการ

    เรียกใช้gradle dependenciesคำสั่งในเทอร์มินัลใน poject B root dir ir ที่มีสกรีนช็อตเอาท์พุทดังต่อไปนี้เราจะเห็นว่า 'spring-boot-gradle-plugin' ปรากฏขึ้นใน treeClasspath พึ่งพา runtimeClasspath แต่ไม่ใช่ใน compileClasspath การใช้ไลบรารีที่ประกาศโดยใช้การนำไปใช้จะไม่สามารถรวบรวมได้

    ป้อนคำอธิบายรูปภาพที่นี่

  2. ทำให้โครงการ A ขึ้นอยู่กับ 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' โดยapi

    รันgradle dependenciesคำสั่งในเทอร์มินัลใน poject B root dir อีกครั้ง ตอนนี้ 'spring-boot-gradle-plugin' จะปรากฏขึ้นทั้งในทรี compileClasspath และ runtimeClasspath

    ป้อนคำอธิบายรูปภาพที่นี่

ความแตกต่างที่สำคัญที่ฉันสังเกตเห็นคือการพึ่งพาในโครงการผู้ผลิต / ไลบรารีที่ประกาศในวิธีการใช้งานจะไม่ปรากฏใน compileClasspath ของโครงการผู้บริโภคดังนั้นเราจึงไม่สามารถใช้ lib ที่สอดคล้องกันในโครงการผู้บริโภค


2

จากเอกสาร gradle :

ลองมาดูสคริปต์สร้างที่ง่ายมากสำหรับโครงการที่ใช้ JVM

plugins {
    id 'java-library'
}

repositories {
    mavenCentral()
}

dependencies {
    implementation 'org.hibernate:hibernate-core:3.6.7.Final'
    api 'com.google.guava:guava:23.0'
    testImplementation 'junit:junit:4.+'
}

การดำเนินงาน

การพึ่งพาที่จำเป็นในการรวบรวมแหล่งผลิตของโครงการซึ่งไม่ได้เป็นส่วนหนึ่งของ API ที่เปิดเผยโดยโครงการ ตัวอย่างเช่นโครงการใช้ Hibernate สำหรับการนำเลเยอร์การคงอยู่ภายในมาใช้

API

การพึ่งพาที่จำเป็นในการรวบรวมแหล่งผลิตของโครงการซึ่งเป็นส่วนหนึ่งของ API ที่เปิดเผยโดยโครงการ ตัวอย่างเช่นโครงการใช้ Guava และแสดงอินเตอร์เฟสสาธารณะกับคลาส Guava ในลายเซ็นเมธอด

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.