Tampak bagi saya bahwa kebanyakan orang mulai berurusan dengan gradle hanya ketika sesuatu perlu ditambahkan ke proyek atau sesuatu tiba-tiba rusak - dan setelah menyelesaikan masalah "diperoleh dengan bekerja terlalu keras" pengalaman itu dilupakan dengan aman. Selain itu, banyak contoh di Internet mirip dengan mantra khusus yang tidak menambah pemahaman tentang apa yang terjadi:
android { compileSdkVersion 28 defaultConfig { applicationId "com.habr.hello" minSdkVersion 20 targetSdkVersion 28 } buildTypes { release { minifyEnabled false } } }
Saya tidak akan menjelaskan secara rinci untuk apa setiap baris di atas - ini adalah detail pribadi dari penerapan plugin android. Ada sesuatu yang lebih berharga - pemahaman tentang bagaimana semuanya diatur. Informasi ini tersebar di berbagai situs / dokumentasi resmi / sumber hujan es dan plugin untuk itu - secara umum, ini adalah pengetahuan yang sedikit lebih universal yang tidak ingin saya lupakan.
Teks lebih lanjut dapat dianggap sebagai lembar contekan bagi mereka yang hanya menguasai gradle atau sudah lupa.
Tautan yang bermanfaat
Konsol
Android studio / IDEA susah payah menyembunyikan perintah gradle dari pengembang, dan bahkan ketika mengubah file build.gradle itu mulai membodohi atau memulai kembali proyek.
Dalam kasus seperti itu, memanggil gradle dari konsol jauh lebih mudah dan lebih cepat. Grapper wrapper biasanya datang dengan proyek dan berfungsi dengan baik di linux / macos / windows, kecuali di yang terakhir Anda perlu memanggil file bat bukan wrapper.
Tugas tantangan
./gradlew tasks
menulis tugas yang tersedia.
./gradlew subprojectName:tasks --all
Anda dapat menampilkan tugas-tugas dari sub proyek yang terpisah, dan bahkan dengan opsi --all
, semua tugas, termasuk yang sekunder, akan ditampilkan.
Anda dapat memanggil tugas apa pun, dan semua tugas yang bergantung padanya akan dipanggil.
./gradlew app:assembleDevelopDebug
Jika Anda terlalu malas untuk menulis seluruh nama, Anda dapat membuang surat-surat kecil:
./gradlew app:assembleDD
Jika hujan es tidak dapat dengan jelas menebak tugas yang mereka pikirkan, itu akan menampilkan daftar opsi yang sesuai.
Penebangan
Jumlah informasi yang ditampilkan di konsol saat memulai tugas sangat bergantung pada tingkat pencatatan.
Selain default, ada -q, -w, -i, -d
, well, atau --quiet, --warn, --info, --debug
dalam meningkatkan jumlah informasi. Pada proyek yang kompleks, output dengan -d dapat memakan waktu lebih dari satu megabyte, dan karena itu lebih baik menyimpannya ke file segera dan mencari di sana dengan mencari kata kunci:
./gradlew app:build -d > myLog.txt
Jika pengecualian dilemparkan ke suatu tempat, opsi -s
untuk stacktrace.
Anda dapat menulis ke log sendiri:
logger.warn('A warning log message.')
logger adalah implementasi dari SLF4J.
Asyik
Apa yang terjadi di file build.gradle
hanyalah kode asyik.
Untuk beberapa alasan, Groovy, sebagai bahasa pemrograman, tidak terlalu populer, meskipun, menurut saya, itu sendiri layak untuk setidaknya dipelajari sedikit. Bahasa lahir kembali pada tahun 2003 dan perlahan berkembang. Fitur menarik:
- Hampir semua kode java adalah kode asyik yang valid. Sangat membantu untuk menulis kode kerja secara intuitif.
- Bersamaan dengan statis, pengetikan dinamis didukung dalam alur, daripada
String a = "a"
Anda dapat dengan aman menulis def a = "a"
atau bahkan def map = ['one':1, 'two':2, 'list' = [1,false]]
- Ada beberapa penutupan di mana Anda dapat secara dinamis menentukan konteks eksekusi. Blok
android {...}
sama ditutup dan dieksekusi untuk beberapa objek. - Ada interpolasi string
"$a, ${b}"
, string multiline """yep, ${c}"""
, dan string java biasa dibingkai oleh tanda kutip tunggal: 'text'
- Ada kemiripan metode penyuluhan. Kumpulan bahasa standar sudah memiliki metode seperti findAll, setiap, masing-masing. Bagi saya pribadi, nama-nama metode itu tampak tidak biasa, tetapi yang terpenting adalah metode itu .
- Gula sintaksis yang lezat, kodenya jauh lebih pendek dan sederhana. Anda tidak harus menulis tanda kurung di sekitar argumen fungsi, untuk deklarasi daftar dan
[a,b,c], [key1: value1, key2: value2]
hash [a,b,c], [key1: value1, key2: value2]
sintaks yang bagus adalah: [a,b,c], [key1: value1, key2: value2]
Secara umum, mengapa bahasa seperti Python / Javascript telah meroket dan Groovy tidak - itu adalah misteri bagi saya. Pada masanya, ketika tidak ada lambdas di Jawa, dan alternatif seperti kotlin / scala baru saja muncul atau belum ada, Groovy harus terlihat seperti bahasa yang sangat menarik.
Itu adalah fleksibilitas dari sintaks groovy dan pengetikan dinamis yang memungkinkan kami untuk membuat DSL ringkas secara bertahap.
Sekarang dalam dokumentasi resmi Gradle, contohnya digandakan di Kotlin, dan sepertinya direncanakan untuk beralih ke sana, tetapi kode itu tidak lagi terlihat begitu sederhana dan menjadi lebih seperti kode normal:
task hello { doLast { println "hello" } }
vs.
tasks.register("hello") { doLast { println("hello") } }
Namun, penggantian nama di Kradle belum direncanakan.
Tahap perakitan
Mereka dibagi menjadi inisialisasi, konfigurasi, dan eksekusi.
Idenya adalah bahwa gradle mengumpulkan grafik ketergantungan asiklik dan hanya memanggil minimum yang diperlukan. Jika saya mengerti benar, tahap inisialisasi terjadi pada saat ketika kode dari build.gradle dieksekusi.
Sebagai contoh, ini:
copy { from source to dest }
Atau seperti ini:
task epicFail { copy{ from source to dest } }
Mungkin ini tidak jelas, tetapi di atas akan memperlambat inisialisasi. Agar tidak terlibat dalam menyalin file di setiap inisialisasi, Anda perlu menggunakan doLast{...}
atau doFirst{...}
dalam tugas - maka kode akan dibungkus dalam penutup dan akan dipanggil ketika tugas selesai.
task properCopy { doLast { copy { from dest to source } } }
atau lebih
task properCopy(type: Copy) { from dest to source }
Dalam contoh lama, doLast
dapat melihat operator <<
bukannya doLast
, tetapi mereka kemudian mengabaikannya karena perilaku yang tidak jelas.
task properCopy << { println("files copied") }
semua tugas
Apa yang lucu, dengan doLast
dan doFirst
Anda dapat menggantung beberapa jenis tindakan pada tugas apa pun:
tasks.all { doFirst { println("task $name started") } }
IDE menyarankan bahwa tasks
memiliki metode whenTaskAdded(Closure ...)
, tetapi metode all(Closure ...)
bekerja jauh lebih menarik - penutup dipanggil untuk semua tugas yang ada, serta untuk tugas baru saat ditambahkan.
Buat tugas yang mencetak dependensi semua tugas:
task printDependencies { doLast { tasks.all { println("$name dependsOn $dependsOn") } } }
atau lebih:
task printDependencies { doLast { tasks.all { Task task -> println("${task.name} dependsOn ${task.dependsOn}") } } }
Jika tasks.all{}
dipanggil saat runtime (di blok doLast
), maka kita akan melihat semua tugas dan dependensi.
Jika Anda melakukan hal yang sama tanpa doLast
(mis., Selama inisialisasi), maka tugas yang dicetak mungkin tidak memiliki dependensi, karena belum ditambahkan.
Oh ya, kecanduan! Jika tugas lain harus bergantung pada hasil implementasi kami, maka ada baiknya menambahkan ketergantungan:
anotherTask.dependsOn properCopy
Atau bahkan seperti ini:
tasks.all{ task -> if (task.name.toLowerCase().contains("debug")) { task.dependsOn properCopy } }
Tugas umum akan dipanggil setiap waktu. Jika Anda menentukan bahwa tugas yang didasarkan pada file A menghasilkan file B, maka gradle akan melewatkan tugas jika file-file ini tidak berubah. Dan gradle tidak memeriksa tanggal modifikasi file, tetapi isinya.
task generateCode(type: Exec) { commandLine "generateCode.sh", "input.txt", "output.java" inputs.file "input.txt" output.file "output.java" }
Demikian pula, Anda dapat menentukan folder, serta beberapa nilai: inputs.property(name, value)
.
deskripsi tugas
Saat memanggil ./gradlew tasks --all
tugas standar memiliki deskripsi yang indah dan entah bagaimana dikelompokkan. Untuk tugas Anda, ini ditambahkan dengan sangat sederhana:
task hello { group "MyCustomGroup" description "Prints 'hello'" doLast{ print 'hello' } }
task.enabled
Anda dapat "mematikan" tugas - maka dependensinya masih akan dipanggil, tetapi itu sendiri tidak akan.
taskName.enabled false
beberapa proyek (modul)
multi-proyek dibangun dalam dokumentasi
Di proyek utama, Anda dapat menempatkan beberapa modul lagi. Sebagai contoh, ini digunakan dalam proyek android - hampir tidak ada dalam proyek root, plugin android termasuk dalam subproyek. Jika Anda ingin menambahkan modul baru, Anda dapat menambahkan yang lain, dan di sana, misalnya, Anda juga dapat menghubungkan plugin android, tetapi gunakan pengaturan lain untuk itu.
Contoh lain: saat menerbitkan proyek menggunakan jitpack, proyek root menjelaskan dengan pengaturan apa untuk menerbitkan modul anak yang bahkan mungkin tidak mencurigai publikasi.
Modul anak ditentukan dalam settings.gradle:
include 'name'
Baca lebih lanjut tentang dependensi antar proyek di sini.
buildSrc
Jika build.gradle
banyak kode dalam build.gradle
atau digandakan, itu bisa dipindahkan ke modul terpisah. Kami membutuhkan folder dengan nama ajaib buildSrc
, di mana Anda dapat menempatkan kode di groovy atau java. (baik, atau lebih tepatnya, di buildSrc/src/main/java/com/smth/
code, tes dapat ditambahkan ke buildSrc/src/test
). Jika Anda menginginkan sesuatu yang lain, misalnya, tulis tugas Anda di scala atau gunakan beberapa dependensi, maka langsung di buildSrc
Anda perlu membuat build.gradle
dan tentukan dependensi yang diperlukan di dalamnya / aktifkan plugin.
Sayangnya, dengan sebuah proyek di buildSrc
IDE dapat buildSrc
dengan petunjuk, di sana Anda harus menulis impor dan kelas / tugas dari sana build.gradle
juga harus mengimpornya ke build.gradle
biasa. import com.smth.Taskname
tidak sulit, Anda hanya perlu mengingat ini dan tidak memikirkan mengapa tugas dari buildSrc
tidak ditemukan).
Untuk alasan ini, mudah untuk terlebih dahulu menulis sesuatu yang bekerja langsung di build.gradle
, dan baru kemudian mentransfer kode ke buildSrc
.
Jenis tugas sendiri
Tugas ini diturunkan dari DefaultTask
, di mana ada banyak, banyak bidang, metode, dan hal lainnya. Kode AbstractTask diwarisi dari DefaultTask.
Poin yang berguna:
- alih-alih menambahkan
inputs
dan outputs
secara manual outputs
Anda dapat menggunakan bidang dan anotasi @Input, @OutputFile
: @Input, @OutputFile
, dll. - metode yang akan dijalankan saat tugas dijalankan:
@TaskAction
. - metode yang mudah digunakan seperti
copy{from ... , into... }
masih dapat dipanggil, tetapi Anda harus memanggilnya secara eksplisit untuk proyek: project.copy{...}
Ketika seseorang di build.gradle
menulis untuk tugas kita
taskName { ...
metode configure(Closure)
dipanggil pada tugas.
Saya tidak yakin apakah ini adalah pendekatan yang tepat, tetapi jika sebuah tugas memiliki beberapa bidang yang kondisi timbal baliknya sulit dikendalikan dengan pengambil-pengambil, maka tampaknya cukup mudah untuk mendefinisikan kembali metode sebagai berikut:
override def configure(Closure closure){ def result = super().configure(closure)
Dan bahkan jika Anda menulis
taskName.fieldName value
maka metode configure
akan tetap dipanggil.
Plugin sendiri
Seperti tugas, Anda dapat menulis plugin sendiri, yang akan mengonfigurasi sesuatu atau membuat tugas. Misalnya, apa yang terjadi di android{...}
sepenuhnya merupakan prestasi ilmu hitam Plug-in Android, yang selain itu menciptakan banyak tugas seperti aplikasi: assembleDevelopDebug untuk semua kemungkinan kombinasi rasa / tipe build / dimenstion. Tidak ada yang rumit dalam menulis plugin Anda, untuk pemahaman yang lebih baik Anda dapat melihat kode plugin lain.
Ada langkah ketiga - Anda dapat menempatkan kode tidak di buildSrc
, tetapi menjadikannya proyek yang terpisah. Kemudian, menggunakan https://jitpack.io atau yang lainnya, publikasikan plugin dan sambungkan dengan yang lain.
Akhirnya
Contoh di atas mungkin termasuk kesalahan ketik dan ketidakakuratan. Tulis dalam catatan pribadi atau tandai dengan ctrl+enter
- Saya akan memperbaikinya. Contoh spesifik diambil dari dokumentasi, dan lihat artikel ini sebagai sedikit daftar "cara melakukannya".