Implementasi Gradle vs konfigurasi API

250

Saya mencoba mencari tahu apa perbedaan antara apidan implementationkonfigurasi saat membangun dependensi saya.
Dalam dokumentasinya, dikatakan bahwa itu implementationmemiliki waktu pembuatan yang lebih baik, tetapi, melihat komentar ini di pertanyaan serupa saya bertanya-tanya apakah itu benar.
Karena saya bukan ahli gradle, saya harap ada yang bisa membantu. Saya sudah membaca dokumentasinya tetapi saya bertanya-tanya tentang penjelasan yang mudah dipahami.

reinaldomoreira
sumber
1
Sudahkah kamu membaca di sini ?
MatPag
sebenarnya, saya melakukannya, tetapi, seperti yang saya katakan, komentar itu membuat heran tentang hal itu. jadi saya agak tersesat sekarang
reinaldomoreira
Anda mungkin akan mengalihkan dependensi perpustakaan Anda dari compilemenjadi api. Pustaka yang Anda gunakan secara internal dapat menggunakan beberapa implementasi pribadi yang tidak diekspos di pustaka akhir sehingga transparan bagi Anda. Dependensi "internal-private" tersebut dapat dialihkan ke implementationdan saat plugin gradle Android akan mengompilasi aplikasi Anda, kompilasi dependensi tersebut akan dilewati sehingga menghasilkan waktu build yang lebih singkat (tetapi dependensi tersebut akan tersedia pada waktu proses). Jelas Anda dapat melakukan hal yang sama jika Anda memiliki perpustakaan modul lokal
MatPag
1
Berikut penjelasan grafis singkat dari 'api' dan 'implementasi': jeroenmols.com/blog/2017/06/14/androidstudio3
albert c braun
1
itu posting yang luar biasa! terima kasih @albertbraun
reinaldomoreira

Jawaban:

450

Kata compilekunci Gradle tidak lagi digunakan karena kata kunci apidan implementationuntuk mengonfigurasi dependensi.

Menggunakan apisama dengan menggunakan deprecated compile, jadi jika Anda mengganti semua compiledengan apisemuanya akan berfungsi seperti biasa.

Untuk memahami implementationkata kunci, pertimbangkan contoh berikut.

CONTOH

Misalkan Anda memiliki perpustakaan yang dipanggil MyLibraryyang secara internal menggunakan perpustakaan lain yang disebut InternalLibrary. Sesuatu seperti ini:

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

Misalkan MyLibrary build.gradlemenggunakan apikonfigurasi dependencies{}seperti ini:

dependencies {
    api project(':InternalLibrary')
}

Anda ingin menggunakan MyLibrarydalam kode Anda sehingga di aplikasi build.gradleAnda , Anda menambahkan ketergantungan ini:

dependencies {
    implementation project(':MyLibrary')
}

Menggunakan apikonfigurasi (atau tidak digunakan lagi compile) yang dapat Anda akses InternalLibrarydi kode aplikasi Anda:

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

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

Dengan cara ini modul MyLibraryberpotensi "membocorkan" implementasi internal sesuatu. Anda tidak boleh (dapat) menggunakannya karena tidak langsung diimpor oleh Anda.

The implementationkonfigurasi diperkenalkan untuk mencegah hal ini. Jadi sekarang jika Anda menggunakan implementationalih-alih apidi MyLibrary:

dependencies {
    implementation project(':InternalLibrary')
}

Anda tidak akan dapat memanggil InternalLibrary.giveMeAString()kode aplikasi Anda lagi.

Strategi tinju semacam ini memungkinkan plugin Android Gradle mengetahui bahwa jika Anda mengedit sesuatu InternalLibrary, plugin tersebut hanya boleh memicu kompilasi ulang MyLibrarydan bukan kompilasi ulang seluruh aplikasi Anda, karena Anda tidak memiliki akses ke InternalLibrary.

Jika Anda memiliki banyak dependensi bersarang, mekanisme ini dapat mempercepat proses build. (Tonton video yang ditautkan di bagian akhir untuk pemahaman penuh tentang ini)

KESIMPULAN

  • Saat Anda beralih ke plugin Android Gradle 3.XX baru, Anda harus mengganti semua Anda compiledengan implementationkata kunci * (1 ) . Kemudian coba kompilasi dan uji aplikasi Anda. Jika semuanya tidak apa-apa biarkan kodenya apa adanya, jika Anda memiliki masalah, Anda mungkin memiliki sesuatu yang salah dengan dependensi Anda atau Anda menggunakan sesuatu yang sekarang bersifat pribadi dan tidak lebih mudah diakses. * Saran oleh insinyur plugin Android Gradle Jerome Dochez (1 ) )

  • Jika Anda adalah mantainer library, Anda harus menggunakan apiuntuk setiap dependensi yang diperlukan untuk API publik library Anda, sementara digunakan implementationuntuk uji dependensi atau dependensi yang tidak boleh digunakan oleh pengguna akhir.

Artikel yang berguna Menampilkan perbedaan antara implementasi dan api

REFERENSI (Ini adalah video yang sama yang dipisahkan untuk menghemat waktu)

Google I / O 2017 - Bagaimana mempercepat pembangunan Gradle (VIDEO LENGKAP)

Google I / O 2017 - Cara mempercepat pembuatan Gradle (NEW GRADLE PLUGIN 3.0.0 PART ONLY)

Google I / O 2017 - Cara mempercepat build Gradle (referensi ke 1 *)

Dokumentasi Android

MatPag
sumber
5
Saya perhatikan bahwa api tampaknya tidak berfungsi dengan baik di modul perpustakaan. Jika saya menggunakannya, saya masih tidak dapat mengakses dependensi dari proyek aplikasi saya. Saya hanya dapat mengakses kode di perpustakaan itu sendiri.
Allan W
2
Ini bagus dan berfungsi pada debug-build tetapi saat menggunakan ProGuard (pada versi rilis) MyLibrary#myString()akan mogok karena ProGuard telah InternalLibrarydihapus. Apa praktik terbaik untuk android-libs yang akan digunakan dalam aplikasi ProGuard?
hardysim
3
Saya rasa jawabannya tidak akurat, aplikasi dapat menggunakan cakupan apa pun yang diinginkan untuk MyLibrary. Ini akan melihat atau tidak InternalLibrary tergantung apakah MyLibrary menggunakan api / implementasi atau tidak.
Snicolas
2
Terima kasih sobat. penjelasan yang luar biasa, jauh lebih baik daripada yang diberikan di dokumen resmi android
Henry
2
itu penjelasan yang bagus. Teori dan beton bercampur dengan sangat baik. Sudah selesai dilakukan dengan baik. Terima kasih untuk itu
Peter Kahn
157

Saya suka memikirkan tentang apiketergantungan sebagai publik (dilihat oleh modul lain) sedangkan implementationketergantungan sebagai pribadi (hanya dilihat oleh modul ini).

Perhatikan, tidak seperti public/ privatevariabel dan metode, api/ implementationdependensi tidak diberlakukan oleh runtime. Ini hanyalah pengoptimalan waktu build, yang memungkinkan Gradleuntuk mengetahui modul mana yang perlu dikompilasi ulang ketika salah satu dependensi mengubah API-nya.

dev.bmax
sumber
22
Saya suka kesederhanaan jawaban ini, terima kasih banyak
Kevin Gilles
2
Perbedaan sebenarnya (AFAICT) adalah bahwa file pom yang dihasilkan menempatkan apidependensi dalam lingkup "kompilasi" (mereka akan dimasukkan sebagai dependensi di pustaka Anda dan apa pun yang bergantung pada pustaka Anda) dan implementationdependensi dalam cakupan "runtime" (sebaiknya di classpath saat kode Anda dijalankan, tetapi tidak diperlukan untuk mengompilasi kode lain yang menggunakan library Anda).
Shadow Man
@ShadowMan Ini adalah detail implementasi plugin, yang bertanggung jawab untuk menghasilkan file POM, cara memetakan cakupan Gradle ke cakupan Maven .
dev.bmax
1
Anda harus menggunakan implementationdependensi apa pun yang diperlukan untuk dijalankan (dan untuk library Anda untuk dikompilasi), tetapi itu tidak boleh ditarik secara otomatis ke dalam project yang menggunakan library Anda. Contohnya adalah jax-rs, perpustakaan Anda mungkin menggunakan RESTeasy, tetapi seharusnya tidak menarik libs tersebut ke dalam proyek apa pun yang menggunakan perpustakaan Anda, karena mereka mungkin ingin menggunakan Jersey sebagai gantinya.
Shadow Man
1
ini adalah bagaimana Anda tahu seseorang mendapatkan barangnya: D terima kasih atas jawaban yang sederhana dan jelas
Elias Fazel
16

Anggap Anda memiliki appmodul yang digunakan lib1sebagai perpustakaan dan lib1digunakan lib2sebagai perpustakaan. Sesuatu seperti ini: app -> lib1 -> lib2.

Sekarang saat menggunakan api lib2in lib1, maka app dapat melihat lib2 kode saat menggunakan: api lib1atau implementation lib1di appmodul.

TAPI ketika menggunakan implementation lib2di lib1, maka app tidak bisa melihat dengan lib2kode.

Ehsan Mashhadi
sumber
2
Jawaban terbaik dan sangat meyakinkan
Gk Mohammad Emon
5

Jawaban dari @matpag dan @ dev-bmax cukup jelas untuk membuat orang memahami perbedaan penggunaan antara implementasi dan api. Saya hanya ingin membuat penjelasan ekstra dari sudut lain, berharap dapat membantu orang-orang yang memiliki pertanyaan yang sama.

Saya membuat dua proyek untuk pengujian:

  • proyek A sebagai proyek pustaka java bernama 'frameworks-web-gradle-plugin' tergantung pada 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE'
  • proyek B bergantung pada proyek A dengan mengimplementasikan 'com.example.frameworks.gradle: frameworks-web-gradle-plugin: 0.0.1-SNAPSHOT'

Hierarki dependensi yang dijelaskan di atas terlihat seperti:

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

Kemudian saya menguji skenario berikut:

  1. Buat proyek A bergantung pada 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' dengan implementasi .

    Jalankan gradle dependenciesperintah di terminal di direktori root B poject, dengan tangkapan layar berikut dari keluaran, kita dapat melihat bahwa 'spring-boot-gradle-plugin' muncul di pohon dependensi runtimeClasspath, tetapi tidak di compileClasspath's, saya pikir itulah mengapa kita tidak bisa membuatnya penggunaan library yang dideklarasikan menggunakan implementasi, hanya saja tidak melalui kompilasi.

    masukkan deskripsi gambar di sini

  2. Buat proyek A bergantung pada 'org.springframework.boot: spring-boot-gradle-plugin: 1.5.20.RELEASE' oleh api

    Jalankan gradle dependenciesperintah di terminal di poject B root dir lagi. Sekarang 'spring-boot-gradle-plugin' muncul di pohon dependensi compileClasspath dan runtimeClasspath.

    masukkan deskripsi gambar di sini

Perbedaan signifikan yang saya perhatikan adalah bahwa ketergantungan dalam proyek produser / perpustakaan yang dideklarasikan dalam cara implementasi tidak akan muncul di compileClasspath proyek konsumen, sehingga kami tidak dapat menggunakan lib yang sesuai dalam proyek konsumen.

Rong.l
sumber
3

Dari dokumentasi gradle :

Mari kita lihat skrip build yang sangat sederhana untuk proyek berbasis 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.+'
}

penerapan

Dependensi yang diperlukan untuk mengompilasi sumber produksi proyek yang bukan bagian dari API yang diekspos oleh proyek. Misalnya proyek menggunakan Hibernate untuk implementasi lapisan persistensi internalnya.

api

Ketergantungan yang diperlukan untuk mengompilasi sumber produksi proyek yang merupakan bagian dari API yang diekspos oleh proyek. Misalnya, proyek menggunakan Guava dan mengekspos antarmuka publik dengan kelas Guava di tanda tangan metode mereka.

Camilo Silva
sumber