Apa perbedaan antara implementasi dan kompilasi di Gradle?


1030

Setelah memperbarui ke Android Studio 3.0 dan membuat proyek baru, saya perhatikan bahwa build.gradleada cara baru untuk menambahkan dependensi baru alih-alih compileada implementationdan bukannya testCompileada testImplementation.

Contoh:

 implementation 'com.android.support:appcompat-v7:25.0.0'
 testImplementation 'junit:junit:4.12'

dari pada

 compile 'com.android.support:appcompat-v7:25.0.0'
 testCompile 'junit:junit:4.12'

Apa perbedaan antara mereka dan apa yang harus saya gunakan?

Jawaban:


1282

tl; dr

Cukup ganti:

  • compiledengan implementation(jika Anda tidak membutuhkan transitivitas) atau api(jika Anda memerlukan transitivitas)
  • testCompile dengan testImplementation
  • debugCompile dengan debugImplementation
  • androidTestCompile dengan androidTestImplementation
  • compileOnlymasih valid Itu ditambahkan di 3.0 untuk menggantikan disediakan dan tidak dikompilasi. ( provideddiperkenalkan ketika Gradle tidak memiliki nama konfigurasi untuk use-case itu dan menamainya setelah lingkup yang disediakan Maven.)

Ini adalah salah satu perubahan besar yang datang dengan Gradle 3.0 yang diumumkan Google di IO17 .

The compilekonfigurasi sekarang usang dan harus diganti dengan implementationatauapi

Dari dokumentasi Gradle :

dependencies {
    api 'commons-httpclient:commons-httpclient:3.1'
    implementation 'org.apache.commons:commons-lang3:3.5'
}

Ketergantungan yang muncul dalam apikonfigurasi akan terpapar secara transparan kepada konsumen perpustakaan, dan dengan demikian akan muncul pada kompilasi classpath konsumen.

Ketergantungan yang ditemukan dalam implementationkonfigurasi, di sisi lain, tidak akan terpapar ke konsumen, dan karenanya tidak bocor ke kompilasi classpath konsumen. Ini dilengkapi dengan beberapa manfaat:

  • dependensi tidak bocor ke kompilasi classpath konsumen lagi, sehingga Anda tidak akan pernah secara tidak sengaja bergantung pada dependensi transitif
  • kompilasi yang lebih cepat berkat ukuran classpath yang berkurang
  • lebih sedikit kompilasi ketika dependensi implementasi berubah: konsumen tidak perlu dikompilasi ulang
  • penerbitan bersih: ketika digunakan bersama dengan plugin maven-publish baru, perpustakaan Java menghasilkan file POM yang membedakan secara tepat antara apa yang diperlukan untuk dikompilasi terhadap perpustakaan dan apa yang diperlukan untuk menggunakan perpustakaan saat runtime (dengan kata lain, jangan campur apa yang dibutuhkan untuk mengkompilasi perpustakaan itu sendiri dan apa yang dibutuhkan untuk dikompilasi terhadap perpustakaan).

Konfigurasi kompilasi masih ada, tetapi tidak boleh digunakan karena tidak akan menawarkan jaminan yang disediakan oleh apidan implementationkonfigurasi.


Catatan: jika Anda hanya menggunakan perpustakaan di modul aplikasi Anda - kasing umum - Anda tidak akan melihat perbedaan.
Anda hanya akan melihat perbedaannya jika Anda memiliki proyek yang rumit dengan modul tergantung satu sama lain, atau Anda membuat perpustakaan.


137
Siapa "konsumen"?
Suragch

34
konsumen adalah modul menggunakan perpustakaan. dalam kasus Android, ini adalah aplikasi Android. Saya pikir ini jelas dan saya tidak yakin apakah ini yang Anda minta.
humazed

21
Bagi saya itu juga terdengar seperti itu. Tetapi jika saya membuat perpustakaan, tentu saja saya ingin APInya terbuka ke aplikasi. Jika tidak, bagaimana pengembang aplikasi akan menggunakan perpustakaan saya? Itu sebabnya saya tidak mendapatkan arti implementationmenyembunyikan ketergantungan. Apakah pertanyaan saya masuk akal?
Suragch

235
ya, masuk akal sekarang, jika aplikasi Anda tergantung pada perpustakaan x yang itu sendiri tergantung pada y, z. jika Anda implementationhanya menggunakan x api akan diekspos, tetapi jika Anda menggunakan apiy, z juga akan terpapar.
humazed

36
Oke! Itu lebih masuk akal sekarang. Anda dapat menambahkan penjelasan ini ke dalam jawaban Anda. Ini lebih jelas daripada dokumentasi yang dikutip.
Suragch

380

Jawaban ini akan menunjukkan perbedaan antara implementation, apidancompile pada sebuah proyek.


Katakanlah saya punya proyek dengan tiga modul Gradle:

  • aplikasi (aplikasi Android)
  • myandroidlibrary (perpustakaan Android)
  • myjavalibrary (perpustakaan Java)

appmemiliki myandroidlibrarydependensi. myandroidlibrarymemiliki myjavalibrary dependensi.

Ketergantungan1

myjavalibrarypunya MySecretkelas

public class MySecret {

    public static String getSecret() {
        return "Money";
    }
}

myandroidlibrarymemiliki MyAndroidComponentkelas yang memanipulasi nilai dari MySecretkelas.

public class MyAndroidComponent {

    private static String component = MySecret.getSecret();

    public static String getComponent() {
        return "My component: " + component;
    }    
}

Terakhir, apphanya tertarik pada nilai darimyandroidlibrary

TextView tvHelloWorld = findViewById(R.id.tv_hello_world);
tvHelloWorld.setText(MyAndroidComponent.getComponent());

Sekarang, mari kita bicara tentang ketergantungan ...

appperlu dikonsumsi :myandroidlibrary, jadi dalam apppenggunaan build.gradleimplementation .

( Catatan : Anda dapat menggunakan api / kompilasi juga. Tapi tahan pikiran itu sejenak.)

dependencies {
    implementation project(':myandroidlibrary')      
}

Ketergantungan2

Bagaimana menurut anda myandroidlibrary seharusnya build.gradle? Lingkup mana yang harus kita gunakan?

Kami memiliki tiga opsi:

dependencies {
    // Option #1
    implementation project(':myjavalibrary') 
    // Option #2
    compile project(':myjavalibrary')      
    // Option #3
    api project(':myjavalibrary')           
}

Ketergantungan3

Apa perbedaan antara mereka dan apa yang harus saya gunakan?

Kompilasi atau Api (opsi # 2 atau # 3) Ketergantungan4

Jika Anda menggunakan compileatau api. Aplikasi Android kami sekarang dapat mengakses myandroidcomponentketergantungan, yang merupakan MySecretkelas.

TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can access MySecret
textView.setText(MySecret.getSecret());

Implementasi (opsi # 1)

Ketergantungan5

Jika Anda menggunakan implementationkonfigurasi, MySecrettidak terbuka.

TextView textView = findViewById(R.id.text_view);
textView.setText(MyAndroidComponent.getComponent());
// You can NOT access MySecret
textView.setText(MySecret.getSecret()); // Won't even compile

Jadi, konfigurasi mana yang harus Anda pilih? Itu sangat tergantung pada kebutuhan Anda.

Jika Anda ingin mengekspos dependensi, gunakan apiatau compile.

Jika Anda tidak ingin mengekspos dependensi (menyembunyikan modul internal Anda) kemudian gunakanimplementation .

catatan:

Ini hanya inti dari konfigurasi Gradle, lihat Tabel 49.1. Plugin Java Library - konfigurasi yang digunakan untuk mendeklarasikan dependensi untuk penjelasan lebih rinci.

Proyek sampel untuk jawaban ini tersedia di https://github.com/aldoKelvianto/ImplementationVsCompile


1
Saya telah menambahkan ketergantungan pada satu file jar menggunakan implementasi, jika tidak membuka akses ke sana mengapa saya masih bisa mendapatkan dan kode saya berfungsi dengan baik?
smkrn110

@ smkrn110 implementasi akan mengekspos perpustakaan jar Anda, tetapi tidak perpustakaan dependensi jar Anda.
aldok

2
@WijaySharma, jawaban yang diterima menyatakan bahwa compiletidak menjamin hal yang sama yang apimenjamin.
Sub 6 Sumber Daya

9
Saya pikir ini harus menjadi jawaban yang diterima. Diterangkan dengan baik!
Shashank Kapsime

9
@ StevenW.Klassen itu adalah downvote paling tidak pantas yang pernah saya dengar. Jika Anda berpikir urutan informasi tidak optimal, sarankan edit daripada mengeluh tentang hal itu
Tim

65

Compilekonfigurasi sudah usang dan harus diganti oleh implementationatau api.

Anda dapat membaca dokumen di https://docs.gradle.org/current/userguide/java_library_plugin.html#sec:java_library_separation .

Bagian singkat menjadi-

Perbedaan utama antara plugin Java standar dan plugin Java Library adalah plugin Java memperkenalkan konsep API yang dipaparkan kepada konsumen. Perpustakaan adalah komponen Java yang dimaksudkan untuk dikonsumsi oleh komponen lain. Ini adalah kasus penggunaan yang sangat umum dalam pembangunan multi-proyek, tetapi juga segera setelah Anda memiliki dependensi eksternal.

Plugin memperlihatkan dua konfigurasi yang dapat digunakan untuk mendeklarasikan dependensi: api dan implementasi. Konfigurasi api harus digunakan untuk mendeklarasikan dependensi yang diekspor oleh pustaka API, sedangkan konfigurasi implementasi harus digunakan untuk mendeklarasikan dependensi yang internal ke komponen.

Untuk penjelasan lebih lanjut lihat gambar ini. Penjelasan singkat


46

Solusi Singkat:

Pendekatan yang lebih baik adalah mengganti semua compiledependensi dengan implementationdependensi. Dan hanya di mana Anda membocorkan antarmuka modul, Anda harus menggunakannya api. Itu seharusnya menyebabkan kompilasi yang jauh lebih sedikit.

 dependencies {
         implementation fileTree(dir: 'libs', include: ['*.jar'])

         implementation 'com.android.support:appcompat-v7:25.4.0'
         implementation 'com.android.support.constraint:constraint-layout:1.0.2'
         // …

         testImplementation 'junit:junit:4.12'
         androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
             exclude group: 'com.android.support', module: 'support-annotations'
         })
 }

Jelaskan lebih:

Sebelum Android Gradle plugin 3.0 : kami memiliki masalah besar yaitu satu perubahan kode menyebabkan semua modul dikompilasi ulang. Akar penyebabnya adalah Gradle tidak tahu apakah Anda membocorkan antarmuka modul melalui modul lain atau tidak.

Setelah Android Gradle plugin 3.0 : plugin Android Gradle terbaru sekarang mengharuskan Anda untuk secara eksplisit menentukan apakah Anda membocorkan antarmuka modul. Berdasarkan hal itu, ia dapat membuat pilihan yang tepat tentang apa yang harus dikompilasi ulang.

Dengan demikian compileketergantungan telah ditinggalkan dan digantikan oleh dua yang baru:

  • api: Anda membocorkan antarmuka modul ini melalui antarmuka Anda sendiri, artinya sama persis dengan compileketergantungan lama

  • implementation: Anda hanya menggunakan modul ini secara internal dan tidak membocorkannya melalui antarmuka Anda

Jadi sekarang Anda dapat secara eksplisit memberi tahu Gradle untuk mengkompilasi ulang modul jika antarmuka modul yang digunakan berubah atau tidak.

Atas perkenan blog Jeroen Mols


2
Penjelasan yang bersih dan ringkas. Terima kasih!
LeOn - Han Li

20
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| Name               | Role                 | Consumable? | Resolveable? | Description                             |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| api                | Declaring            |      no     |      no      | This is where you should declare        |
|                    | API                  |             |              | dependencies which are transitively     |
|                    | dependencies         |             |              | exported to consumers, for compile.     |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| implementation     | Declaring            |      no     |      no      | This is where you should                |
|                    | implementation       |             |              | declare dependencies which are          |
|                    | dependencies         |             |              | purely internal and not                 |
|                    |                      |             |              | meant to be exposed to consumers.       |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| compileOnly        | Declaring compile    |     yes     |      yes     | This is where you should                |
|                    | only                 |             |              | declare dependencies                    |
|                    | dependencies         |             |              | which are only required                 |
|                    |                      |             |              | at compile time, but should             |
|                    |                      |             |              | not leak into the runtime.              |
|                    |                      |             |              | This typically includes dependencies    |
|                    |                      |             |              | which are shaded when found at runtime. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| runtimeOnly        | Declaring            |      no     |      no      | This is where you should                |
|                    | runtime              |             |              | declare dependencies which              |
|                    | dependencies         |             |              | are only required at runtime,           |
|                    |                      |             |              | and not at compile time.                |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| testImplementation | Test dependencies    |      no     |      no      | This is where you                       |
|                    |                      |             |              | should declare dependencies             |
|                    |                      |             |              | which are used to compile tests.        |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| testCompileOnly    | Declaring test       |     yes     |      yes     | This is where you should                |
|                    | compile only         |             |              | declare dependencies                    |
|                    | dependencies         |             |              | which are only required                 |
|                    |                      |             |              | at test compile time,                   |
|                    |                      |             |              | but should not leak into the runtime.   |
|                    |                      |             |              | This typically includes dependencies    |
|                    |                      |             |              | which are shaded when found at runtime. |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+
| testRuntimeOnly    | Declaring test       |      no     |      no      | This is where you should                |
|                    | runtime dependencies |             |              | declare dependencies which              |
|                    |                      |             |              | are only required at test               |
|                    |                      |             |              | runtime, and not at test compile time.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+

Tidak menjawab pertanyaan secara langsung
skryvets

1
Ada juga developmentOnly
Hohenheimsenberg

Apa yang harus saya gunakan jika saya membutuhkan waktu runtime dan waktu kompilasi? Saat ini, saya telah implementationdiikuti oleh a runtime.
Maroun

8

Perbedaan singkat dalam istilah awam adalah:

  • Jika Anda bekerja pada antarmuka atau modul yang menyediakan dukungan untuk modul lain dengan mengekspos anggota dependensi yang disebutkan, Anda harus menggunakan 'api'.
  • Jika Anda membuat aplikasi atau modul yang akan mengimplementasikan atau menggunakan dependensi yang disebutkan secara internal, gunakan 'implementasi'.
  • 'kompilasi' bekerja sama dengan 'api', namun, jika Anda hanya mengimplementasikan atau menggunakan pustaka apa pun, 'implementasi' akan bekerja lebih baik dan menghemat sumber daya Anda.

baca jawabannya oleh @aldok untuk contoh yang komprehensif.


Tetapi masalahnya adalah jika seseorang dengan sengaja datang ke sini untuk mencari jawaban atas pertanyaan-pertanyaan ini, maka ia bukanlah orang awam.
Rishav

6

Karena versi 5.6.3 Dokumentasi gradle memberikan aturan praktis sederhana untuk mengidentifikasi apakah compileketergantungan lama (atau yang baru) harus diganti dengan ketergantungan implementationatau api:

  • Lebih suka implementationkonfigurasi selesai apibila memungkinkan

Ini menjaga dependensi dari classpath kompilasi konsumen. Selain itu, konsumen akan segera gagal untuk mengkompilasi jika ada jenis implementasi yang tidak sengaja bocor ke API publik.

Jadi kapan sebaiknya Anda menggunakan apikonfigurasi? Ketergantungan API adalah salah satu yang mengandung setidaknya satu jenis yang diekspos di antarmuka biner perpustakaan, sering disebut sebagai ABI (Application Binary Interface). Ini termasuk, tetapi tidak terbatas pada:

  • tipe yang digunakan di kelas atau antarmuka super
  • tipe yang digunakan dalam parameter metode publik, termasuk tipe parameter umum (di mana publik adalah sesuatu yang dapat dilihat oleh kompiler. Yaitu, publik, dilindungi, dan mengemas anggota pribadi di dunia Java)
  • jenis yang digunakan di bidang publik
  • jenis penjelasan publik

Sebaliknya, tipe apa pun yang digunakan dalam daftar berikut ini tidak relevan dengan ABI, dan karenanya harus dinyatakan sebagai implementationketergantungan:

  • jenis yang secara eksklusif digunakan dalam badan metode
  • jenis khusus digunakan dalam anggota pribadi
  • jenis-jenis yang ditemukan secara eksklusif di kelas internal (versi Gradle yang akan datang memungkinkan Anda menyatakan paket mana yang milik API publik)

6

Gradle 3.0 memperkenalkan perubahan selanjutnya:

  • compile -> api

    api kata kunci sama dengan usang compile

  • compile -> implementation

    Lebih disukai karena memiliki beberapa kelebihan. implementationmengekspos dependensi hanya untuk satu level di atas pada waktu build (dependensi tersedia saat runtime). Akibatnya Anda memiliki build yang lebih cepat (tidak perlu mengkompilasi ulang konsumen yang lebih tinggi dari 1 level ke atas)

  • provided -> compileOnly

    Ketergantungan ini hanya tersedia dalam waktu kompilasi (ketergantungan tidak tersedia saat runtime). Ketergantungan ini tidak bisa transitif dan menjadi .aar. Ini dapat digunakan dengan kompilasi prosesor anotasi waktu dan memungkinkan Anda untuk mengurangi file keluaran akhir

  • compile -> annotationProcessor

    Sangat mirip dengan compileOnlytetapi juga menjamin bahwa ketergantungan transitif tidak terlihat bagi konsumen

  • apk -> runtimeOnly

    Ketergantungan tidak tersedia dalam waktu kompilasi tetapi tersedia saat runtime.


Jadi dengan kata lain api = public,, implementation = internaldan compileOnly = private- Saya perlu membuat alias untuk fungsi-fungsi ini karena sangat membingungkan.
t3chb0t
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.