Sự khác biệt giữa triển khai và biên dịch trong Gradle là gì?


1029

Sau khi cập nhật lên Android Studio 3.0 và tạo một dự án mới, tôi nhận thấy rằng trong build.gradleđó có một cách mới để thêm các phụ thuộc mới thay vì compileimplementationvà thay vào testCompileđó là có testImplementation.

Thí dụ:

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

thay vì

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

Sự khác biệt giữa chúng và tôi nên sử dụng cái gì?

Câu trả lời:


1282

tl; dr

Chỉ cần thay thế:

  • compilevới implementation(nếu bạn không cần tính siêu việt) hoặc api(nếu bạn cần tính siêu việt)
  • testCompile với testImplementation
  • debugCompile với debugImplementation
  • androidTestCompile với androidTestImplementation
  • compileOnlyvẫn còn hợp lệ. Nó đã được thêm vào 3.0 để thay thế được cung cấp và không biên dịch. (được providedgiới thiệu khi Gradle không có tên cấu hình cho trường hợp sử dụng đó và đặt tên theo phạm vi được cung cấp của Maven.)

Đây là một trong những thay đổi đột phá đến với Gradle 3.0 mà Google đã công bố tại IO17 .

Các compilecấu hình được bây giờ bị phản và cần được thay thế bằng implementationhoặcapi

Từ tài liệu Gradle :

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

Các phụ thuộc xuất hiện trong các apicấu hình sẽ được tiếp xúc quá mức với người tiêu dùng của thư viện và như vậy sẽ xuất hiện trên đường dẫn biên dịch của người tiêu dùng.

implementationMặt khác, các phụ thuộc được tìm thấy trong cấu hình sẽ không được tiếp xúc với người tiêu dùng và do đó không bị rò rỉ vào đường dẫn biên dịch của người tiêu dùng. Điều này đi kèm với một số lợi ích:

  • các phụ thuộc không bị rò rỉ vào đường dẫn biên dịch của người tiêu dùng nữa, vì vậy bạn sẽ không bao giờ vô tình phụ thuộc vào một phụ thuộc bắc cầu
  • biên dịch nhanh hơn nhờ giảm kích thước classpath
  • ít biên dịch lại khi phụ thuộc thực hiện thay đổi: người tiêu dùng sẽ không cần phải biên dịch lại
  • xuất bản sạch hơn: khi được sử dụng cùng với plugin xuất bản maven mới, các thư viện Java tạo ra các tệp POM phân biệt chính xác giữa những gì được yêu cầu để biên dịch với thư viện và những gì được yêu cầu sử dụng thư viện khi chạy (nói cách khác, không trộn những gì cần thiết để biên dịch thư viện và những gì cần thiết để biên dịch với thư viện).

Cấu hình biên dịch vẫn tồn tại, nhưng không nên được sử dụng vì nó sẽ không cung cấp các đảm bảo mà cấu hình apiimplementationcung cấp.


Lưu ý: nếu bạn chỉ sử dụng một thư viện trong mô-đun ứng dụng của mình - trường hợp phổ biến - bạn sẽ không nhận thấy bất kỳ sự khác biệt nào.
bạn sẽ chỉ thấy sự khác biệt nếu bạn có một dự án phức tạp với các mô-đun tùy thuộc vào nhau hoặc bạn đang tạo một thư viện.


137
Ai là "người tiêu dùng"?
Suragch

34
người tiêu dùng là mô-đun sử dụng thư viện. trong trường hợp của Android, đó là ứng dụng Android. Tôi nghĩ điều này là rõ ràng và tôi không chắc đây có phải là điều bạn đang yêu cầu không.
thất vọng

21
Đó là những gì nó nghe giống như tôi, quá. Nhưng nếu tôi đang làm một thư viện, tất nhiên tôi muốn API của nó được hiển thị với ứng dụng. Nếu không, nhà phát triển ứng dụng sẽ sử dụng thư viện của tôi như thế nào? Đó là lý do tại sao tôi không có ý nghĩa implementationche giấu sự phụ thuộc. Liệu câu hỏi của tôi có ý nghĩa?
Suragch

235
vâng, điều này có ý nghĩa ngay bây giờ, nếu ứng dụng của bạn phụ thuộc vào thư viện x mà chính nó phụ thuộc vào y, z. nếu bạn implementationchỉ sử dụng x api sẽ bị lộ, nhưng nếu bạn sử dụng apiy, z cũng sẽ bị lộ.
thất vọng

36
Hiểu rồi! Điều đó có ý nghĩa hơn bây giờ. Bạn có thể thêm lời giải thích này vào câu trả lời của bạn. Nó rõ ràng hơn các tài liệu được trích dẫn.
Suragch

380

Câu trả lời này sẽ chứng minh sự khác biệt giữa implementation, apicompiletrên một dự án.


Giả sử tôi có một dự án với ba mô-đun Gradle:

  • ứng dụng (một ứng dụng Android)
  • myandroidl Library (thư viện Android)
  • thư viện myjaval (một thư viện Java)

appmyandroidlibraryphụ thuộc. myandroidlibrarymyjavalibrary phụ thuộc.

Phụ thuộc1

myjavalibrarycó một MySecretlớp học

public class MySecret {

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

myandroidlibraryMyAndroidComponentlớp thao túng giá trị từ MySecretlớp.

public class MyAndroidComponent {

    private static String component = MySecret.getSecret();

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

Cuối cùng, appchỉ quan tâm đến giá trị từmyandroidlibrary

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

Bây giờ, hãy nói về sự phụ thuộc ...

appcần phải tiêu thụ :myandroidlibrary, vì vậy trong appviệc sử dụng build.gradle implementation.

( Lưu ý : Bạn cũng có thể sử dụng api / biên dịch. Nhưng hãy giữ suy nghĩ đó một lát.)

dependencies {
    implementation project(':myandroidlibrary')      
}

Phụ thuộc2

Bạn nghĩ myandroidlibrarybuild.gradle nên như thế nào? Chúng ta nên sử dụng phạm vi nào?

Chúng tôi có ba lựa chọn:

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

Phụ thuộc3

Sự khác biệt giữa chúng và tôi nên sử dụng cái gì?

Biên dịch hoặc Api (tùy chọn # 2 hoặc # 3) Phụ thuộc4

Nếu bạn đang sử dụng compilehoặc api. Ứng dụng Android của chúng tôi hiện có thể truy cập myandroidcomponentphụ thuộc, đó là một MySecretlớp.

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

Thực hiện (tùy chọn số 1)

Phụ thuộc5

Nếu bạn đang sử dụng implementationcấu hình, MySecretkhông được tiếp xúc.

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

Vậy, bạn nên chọn cấu hình nào? Điều đó thực sự phụ thuộc vào yêu cầu của bạn.

Nếu bạn muốn phơi bày phụ thuộc sử dụng apihoặc compile.

Nếu bạn không muốn để lộ phụ thuộc (ẩn mô-đun nội bộ của mình) thì hãy sử dụng implementation.

Ghi chú:

Đây chỉ là ý chính của cấu hình Gradle, tham khảo Bảng 49.1. Plugin Thư viện Java - các cấu hình được sử dụng để khai báo các phụ thuộc để giải thích chi tiết hơn.

Dự án mẫu cho câu trả lời này có sẵn trên https://github.com/aldoKelvianto/ImcellenceationVsCompile


1
Tôi đã thêm phụ thuộc vào một tệp jar bằng cách sử dụng triển khai, nếu nó không hiển thị quyền truy cập vào lý do tại sao tôi vẫn có thể nhận được và mã của tôi vẫn hoạt động tốt?
smkrn110

Việc triển khai @ smkrn110 sẽ hiển thị thư viện jar của bạn, nhưng không phải thư viện phụ thuộc jar của bạn.
aldok

2
@WijaySharma câu trả lời được chấp nhận compilekhông bảo đảm những điều tương tự apiđảm bảo.
Tài nguyên phụ 6

9
Tôi nghĩ rằng đây nên là câu trả lời được chấp nhận. Giải thích tốt!
Shashank Kapsime

9
@ StevenW.Klassen đó là downvote không được quan tâm nhất mà tôi từng nghe nói. Nếu bạn cho rằng thứ tự thông tin không tối ưu, hãy đề xuất chỉnh sửa thay vì phàn nàn về nó
Tim

65

Compilecấu hình không được dùng nữa và nên được thay thế bằng implementationhoặc api.

Bạn có thể đọc các tài liệu tại https://docs.gradle.org/cản/userguide/java_l Library_plugin.html#sec:java_l Library_separation .

Phần ngắn gọn là-

Sự khác biệt chính giữa plugin Java tiêu chuẩn và plugin Thư viện Java là cái sau giới thiệu khái niệm về API tiếp xúc với người tiêu dùng. Một thư viện là một thành phần Java có nghĩa là được tiêu thụ bởi các thành phần khác. Đây là trường hợp sử dụng rất phổ biến trong các bản dựng đa dự án, nhưng cũng ngay khi bạn có các phụ thuộc bên ngoài.

Plugin hiển thị hai cấu hình có thể được sử dụng để khai báo các phụ thuộc: api và thực hiện. Cấu hình api nên được sử dụng để khai báo các phụ thuộc được API thư viện xuất ra, trong khi đó cấu hình triển khai nên được sử dụng để khai báo các phụ thuộc bên trong thành phần.

Để giải thích thêm tham khảo hình ảnh này. Giải thích ngắn gọn


46

Giải pháp ngắn gọn:

Cách tiếp cận tốt hơn là thay thế tất cả các compilephụ thuộc bằng implementationphụ thuộc. Và chỉ khi bạn rò rỉ giao diện của mô-đun, bạn nên sử dụng api. Điều đó sẽ gây ra rất ít biên dịch lại.

 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'
         })
 }

Giải thích thêm:

Trước plugin Android Gradle 3.0 : chúng tôi đã gặp một vấn đề lớn đó là một thay đổi mã khiến tất cả các mô-đun được biên dịch lại. Nguyên nhân sâu xa cho việc này là Gradle không biết liệu bạn có rò rỉ giao diện của một mô-đun thông qua một mô-đun khác hay không.

Sau plugin Android Gradle 3.0 : plugin Android Gradle mới nhất hiện yêu cầu bạn xác định rõ ràng nếu bạn rò rỉ giao diện của mô-đun. Dựa vào đó, nó có thể đưa ra lựa chọn đúng về những gì nó cần biên dịch lại.

Do đó, sự compilephụ thuộc đã bị phản đối và được thay thế bằng hai cái mới:

  • api: bạn rò rỉ giao diện của mô-đun này thông qua giao diện của riêng bạn, nghĩa là giống hệt như compilephụ thuộc cũ

  • implementation: bạn chỉ sử dụng mô-đun này trong nội bộ và không rò rỉ nó qua giao diện của bạn

Vì vậy, bây giờ bạn có thể yêu cầu Gradle biên dịch lại một mô-đun một cách rõ ràng nếu giao diện của mô-đun được sử dụng thay đổi hay không.

Lịch sự của blog Jeroen Mols


2
Giải thích rõ ràng và súc tích. Cảm ơn!
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.  |
+--------------------+----------------------+-------------+--------------+-----------------------------------------+

Không trả lời câu hỏi trực tiếp
skryvets

1
Ngoài ra còn có một sự phát
triểnOnly

Tôi nên sử dụng gì nếu tôi cần cả thời gian chạy và thời gian biên dịch? Hiện tại, tôi đã implementationtheo dõi a runtime.
Maroun

8

Sự khác biệt ngắn gọn trong nhiệm kỳ của giáo dân là:

  • Nếu bạn đang làm việc trên một giao diện hoặc mô-đun cung cấp hỗ trợ cho các mô-đun khác bằng cách hiển thị các thành viên của phụ thuộc đã nêu, bạn nên sử dụng 'api'.
  • Nếu bạn đang tạo một ứng dụng hoặc mô-đun sẽ triển khai hoặc sử dụng phụ thuộc đã nêu trong nội bộ, hãy sử dụng 'triển khai'.
  • 'Biên dịch' hoạt động giống như 'api', tuy nhiên, nếu bạn chỉ thực hiện hoặc sử dụng bất kỳ thư viện nào, 'triển khai' sẽ hoạt động tốt hơn và tiết kiệm tài nguyên của bạn.

đọc câu trả lời của @aldok cho một ví dụ toàn diện.


Nhưng vấn đề là nếu một người cố tình đến đây để tìm câu trả lời cho những câu hỏi này, thì rốt cuộc anh ta không phải là giáo dân.
Rishav

6

Vì phiên bản 5.6.3 Tài liệu lớp cung cấp các quy tắc đơn giản để xác định liệu một compilephụ thuộc cũ (hoặc một phụ thuộc mới) nên được thay thế bằng một implementationhoặc một apiphụ thuộc:

  • Thích implementationcấu hình hơn apikhi có thể

Điều này giữ cho các phụ thuộc tắt của classpath biên dịch của người tiêu dùng. Ngoài ra, người tiêu dùng sẽ ngay lập tức không biên dịch nếu bất kỳ loại triển khai nào vô tình rò rỉ vào API công khai.

Vậy khi nào bạn nên sử dụng apicấu hình? Một phụ thuộc API là một loại chứa ít nhất một loại được hiển thị trong giao diện nhị phân của thư viện, thường được gọi là ABI (Giao diện nhị phân ứng dụng). Điều này bao gồm, nhưng không giới hạn ở:

  • loại được sử dụng trong siêu lớp hoặc giao diện
  • các loại được sử dụng trong các tham số phương thức công khai, bao gồm các loại tham số chung (trong đó công khai là thứ hiển thị cho trình biên dịch. Tức là, các thành viên riêng tư, được bảo vệ và gói trong thế giới Java)
  • loại được sử dụng trong các lĩnh vực công cộng
  • các loại chú thích công cộng

Ngược lại, bất kỳ loại nào được sử dụng trong danh sách sau đây đều không liên quan đến ABI, và do đó nên được khai báo là implementationphụ thuộc:

  • các loại được sử dụng riêng trong các phương thức
  • loại dành riêng cho thành viên tư nhân
  • các loại được tìm thấy độc quyền trong các lớp bên trong (các phiên bản tương lai của Gradle sẽ cho phép bạn khai báo các gói nào thuộc về API công khai)

6

Gradle 3.0 giới thiệu những thay đổi tiếp theo:

  • compile -> api

    api từ khóa giống như không dùng nữa compile

  • compile -> implementation

    Là cách thích hợp hơn vì có một số lợi thế. implementationchỉ hiển thị phụ thuộc cho một cấp lên khi xây dựng (phụ thuộc có sẵn khi chạy). Kết quả là bạn có một bản dựng nhanh hơn (không cần phải biên dịch lại người tiêu dùng cao hơn 1 cấp)

  • provided -> compileOnly

    Sự phụ thuộc này chỉ có sẵn trong thời gian biên dịch (sự phụ thuộc không có sẵn trong thời gian chạy). Sự phụ thuộc này không thể là bắc cầu và được .aar. Nó có thể được sử dụng với bộ xử lý chú thích thời gian biên dịch và cho phép bạn giảm một tệp đầu ra cuối cùng

  • compile -> annotationProcessor

    Rất giống với compileOnlynhưng cũng đảm bảo rằng sự phụ thuộc quá độ không thể nhìn thấy đối với người tiêu dùng

  • apk -> runtimeOnly

    Sự phụ thuộc không có sẵn trong thời gian biên dịch nhưng có sẵn trong thời gian chạy.


Vì vậy, nói cách khác, api = public, implementation = internalcompileOnly = private- Tôi cần phải tạo bí danh như vậy cho các chức năng này khi chúng được siêu khó hiểu.
t3chb0t
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.