Hidup dengan proyek multi-modul tidak begitu sederhana. Untuk menghindari rutin membuat modul baru, kami membuat plug-in kami sendiri untuk Android Studio. Dalam proses implementasi, kami menemukan kurangnya dokumentasi praktis, mencoba beberapa pendekatan dan menggali banyak jebakan. Ternyata dua artikel: "Teori" dan "Praktek" . Temui aku!

Apa yang akan saya bicarakan?
- Mengapa plugin? Mengapa plugin?
- Menyusun daftar periksa
- Opsi Otomasi daftar periksa
- Dasar-Dasar Pengembangan Plugin
- Tindakan
- Pengembangan UI dalam plugin
- Kesimpulan
- Internal IDEA: komponen, PSI
- Unit Internal IDEA
- PSI
- Kesimpulan
Mengapa plugin? Mengapa plugin?
Jika Anda sedang mengembangkan proyek multi-modul Android, maka Anda tahu rutinitas macam apa untuk membuat modul baru setiap kali. Anda perlu membuat modul, mengkonfigurasi Gradle di dalamnya, menambahkan dependensi, menunggu sinkronisasi, jangan lupa untuk memperbaiki sesuatu di modul aplikasi - semuanya membutuhkan banyak waktu. Kami ingin mengotomatiskan rutinitas, dan kami mulai dengan menyusun daftar periksa tentang apa yang kami lakukan setiap kali kami membuat modul baru.
1. Pertama, kita membuat modul itu sendiri melalui menu File -> New -> New module -> Android library.

2. Kami menulis lintasan ke modul di file settings.gradle, karena kami memiliki beberapa jenis modul - modul inti dan modul fitur, yang ada di folder yang berbeda.
Kami menulis jalur ke modul 3. Kami mengubah konstanta compileSdk , minSdk , targetSdk di build.gradle yang dihasilkan: kami menggantinya dengan konstanta yang didefinisikan dalam root build.gradle .
Mengubah konstanta di build.gradle dari modul baru Catatan: kami baru-baru ini menghapus bagian dari pekerjaan kami ini ke plugin Gradle kami, yang membantu mengonfigurasi semua parameter yang diperlukan dari file build.gradle dalam beberapa baris.
4. Karena kita menulis semua kode baru di Kotlin, sebagai standar kita menghubungkan dua plugin: kotlin-android dan kotlin-kapt . Jika modul entah bagaimana terhubung dengan UI, kami juga menghubungkan modul kotlin-android-extensions .
5. Kami menghubungkan perpustakaan umum dan modul inti. Modul inti, misalnya, logger, analitik, beberapa utilitas umum, dan perpustakaan - RxJava, Moxy, dan banyak lainnya.
Kami menghubungkan perpustakaan dan modul yang dibagikan 6. Siapkan kapt untuk Tusuk Gigi. Tusuk gigi adalah kerangka DI utama kami. Kemungkinan besar Anda tahu: untuk menggunakan pembuatan kode daripada refleksi dalam rilis rilis, Anda perlu mengkonfigurasi prosesor anotasi sehingga mengerti di mana mendapatkan pabrik untuk objek yang sedang dibuat:
Mengkonfigurasi prosesor anotasi untuk Tusuk Gigi Catatan: di hh.ru kami menggunakan versi pertama dari Tusuk Gigi, di yang kedua kami menghapus kemampuan untuk menggunakan pembuatan kode .
7. Kami mengkonfigurasi kapt untuk Moxy di dalam modul yang dibuat. Moxy adalah kerangka kerja utama kami untuk membuat MVP dalam suatu aplikasi, dan Anda perlu sedikit memutarnya agar dapat bekerja dalam proyek multi-modul. Secara khusus, daftarkan paket modul yang dibuat dalam argumen kapt:
Mengkonfigurasi kapt untuk Moxy Catatan: kami telah beralih ke versi baru Moxy , dan bagian pembuatan kode ini telah kehilangan relevansinya.
8. Kami menghasilkan banyak file baru. Maksud saya bukan file-file yang dibuat secara otomatis (AndroidManifest.xml, build.gradle, .gitignore), tetapi kerangka umum modul baru: interaksor, repositori, modul DI, presenter, fragmen. Ada banyak file-file ini, mereka memiliki struktur yang sama di awal, dan membuatnya secara rutin.

9. Kami menghubungkan modul yang kami buat ke modul aplikasi. Pada langkah ini Anda harus ingat untuk mengonfigurasi Tusuk Gigi di file build.gradle pada modul aplikasi. Untuk melakukan ini, kami menambahkan paket modul yang dibuat ke prosesor penjelasan argumen khusus - toothpick_registry_children_package_names .
Setelah itu, kami mengkonfigurasi Moxy di modul aplikasi. Kami memiliki kelas yang ditandai dengan penjelasan @RegisterMoxyReflectorPackages - di sana kami menambahkan nama paket dari modul yang dibuat:
Konfigurasikan MoxyReflectorStub Dan, pada akhirnya, jangan lupa menghubungkan modul yang dibuat ke blok dependensi dari modul aplikasi:
Kami mendapat daftar periksa sembilan poin.
Karena ada banyak poin, kemungkinan besar akan melupakan sesuatu. Dan kemudian menghabiskan berjam-jam bertanya-tanya apa yang terjadi dan mengapa proyek ini tidak terjadi.
Kami memutuskan bahwa Anda tidak bisa hidup seperti itu dan Anda perlu mengubah sesuatu.
Opsi Otomasi daftar periksa
Setelah menyusun daftar periksa, kami mulai mencari opsi untuk mengotomatisasi item-itemnya.
Opsi pertama adalah upaya untuk melakukan "Ctrl + C, Ctrl + V" . Kami mencoba menemukan implementasi untuk membuat modul Perpustakaan Android, yang tersedia bagi kami “out of the box”. Dalam folder dengan Android Studio (untuk MacOs: / Aplikasi / Android \ Studio.app/Contents/plugins/android/lib/templates/gradle-projects/ ) Anda dapat menemukan folder khusus dengan templat proyek-proyek yang Anda lihat ketika Anda memilih File - > Baru -> Modul Baru. Kami mencoba menyalin template NewAndroidModule dengan mengubah id di dalam file template.xml.ftl . Kemudian mereka meluncurkan IDE, mulai membuat modul baru, dan ... Android Studio macet karena daftar modul yang Anda lihat di menu untuk membuat modul baru adalah kode-keras, Anda tidak dapat mengubahnya dengan copy-paste primitif. Saat Anda mencoba untuk mengambil dan menambahkan, menghapus atau mengubah suatu elemen, Android Studio mogok.

Opsi kedua untuk mengotomatisasi daftar periksa adalah mesin template FreeMarker . Setelah upaya salin-tempel yang gagal, kami memutuskan untuk melihat lebih dekat pada templat modul dan menemukan templat FreeMarker di bawah tenda.
Saya tidak akan memberi tahu Anda detail FreeMarker - ada artikel bagus dari RedMadRobot dan video dari MosDroid dari Lesha Bykov . Namun singkatnya - ini adalah mesin untuk menghasilkan file menggunakan templat dan Map-ki java-objek khusus. Anda memberi makan template, objek, dan FreeMarker menghasilkan kode pada output.
Tetapi lihat kembali daftar periksa:

Jika Anda melihat lebih dekat, Anda dapat melihat bahwa itu dibagi menjadi dua kelompok besar tugas:
- Tugas menghasilkan kode baru (1, 3, 4, 5, 6, 7, 8) dan
- Tugas untuk memodifikasi kode yang ada (2, 7, 8, 9)
Dan jika FreeMarker berupaya dengan tugas-tugas dari kelompok pertama dengan keras, maka itu tidak mengatasi yang kedua sama sekali. Sebagai contoh kecil: dalam implementasi integrasi FreeMarker saat ini di Android Studio, ketika Anda mencoba untuk memasukkan baris ke file settings.gradle yang tidak dimulai dengan kata 'termasuk', studio akan macet . Di sini kami menangkap yang sedih, dan memutuskan untuk meninggalkan penggunaan FreeMarker.
Setelah kegagalan dengan FreeMarker, muncul ide untuk menulis utilitas konsol saya sendiri untuk melakukan daftar periksa. Di dalam Intellij IDEA dimungkinkan untuk menggunakan terminal, jadi mengapa tidak? Mari kita menulis skrip di bash, bisnis total:

Tetapi karena kita ingin dapat mengkonfigurasi modul yang dibuat secara fleksibel, kita harus memasukkan banyak flag yang berbeda, yang tidak nyaman untuk dicetak di konsol.
Setelah itu, kami mundur selangkah dan ingat bahwa kami bekerja di dalam Intellij IDEA. Dan bagaimana cara mengaturnya? Ada inti kelas tertentu, sebuah mesin yang banyak plug-in terpasang, yang menambahkan fungsi yang kita butuhkan.
Berapa banyak dari Anda yang melihat dalam tangkapan layar lebih dari dua plugin yang terhubung?

Di sini mereka terhubung setidaknya tiga. Jika Anda bekerja dengan Kotlin, maka Anda mengaktifkan plugin Kotlin. Jika Anda bekerja dalam proyek dengan Gradle, maka plugin Gradle juga disertakan. Jika Anda bekerja dalam proyek dengan sistem kontrol versi - Git, SVN atau yang lainnya - Anda memiliki plug-in yang sesuai untuk mengintegrasikan VCS ini.
Kami melihat ke repositori plugin JetBrains resmi , dan ternyata sudah ada lebih dari 4000 plugin yang terdaftar secara resmi! Hampir seluruh dunia menulis plugin, dan plugin ini dapat melakukan apa saja: mulai dari mengintegrasikan bahasa pemrograman ke IDEA dan berakhir dengan alat khusus yang dapat dijalankan dari dalam IDEA.
Singkatnya, kami memutuskan untuk menulis plugin kami sendiri.
Dasar-Dasar Pengembangan Plugin
Kami beralih ke dasar-dasar pengembangan plugin. Untuk memulai, Anda hanya perlu tiga hal:
- IntelliJ IDEA , Edisi Komunitas minimum (Anda dapat bekerja dalam versi Ultimate, tetapi itu tidak akan memberikan keuntungan khusus ketika mengembangkan plugin);
- Plugin DevKit yang terhubung dengannya adalah plugin khusus yang menambahkan kemampuan untuk menulis plugin lain;
- Dan bahasa JVM apa pun di mana Anda ingin menulis sebuah plugin. Bisa jadi Kotlin, Jawa, Groovy - apa pun.
Kami mulai dengan membuat proyek plugin. Kami memilih proyek baru , titik Gradle , kami mencentang Plugin Platform IntelliJ dan kami membuat proyek.

Catatan: Jika Anda tidak melihat kotak centang IntelliJ Platform Plugin, itu berarti Anda tidak menginstal DevKit Plugin.
Setelah mengisi kolom yang diperlukan, kita akan melihat struktur plugin kosong.

Mari kita lihat lebih dekat. Terdiri dari:
- Folder tempat Anda akan menulis kode untuk proyek mendatang; ( main / java , main / kotlin , dll);
- file build.gradle , di mana Anda akan mendeklarasikan dependensi plugin Anda pada beberapa perpustakaan, dan juga akan mengonfigurasi hal tersebut sebagai gradle-intellij-plugin .
gradle-intellij-plugin - Plugin Gradle yang memungkinkan Anda menggunakan Gradle sebagai sistem pembuat plugin. Ini nyaman karena hampir setiap pengembang Android terbiasa dengan Gradle dan tahu cara bekerja dengannya. Selain itu, gradle-intellij-plugin menambahkan tugas gradle yang berguna untuk proyek Anda, khususnya:
- runIde - tugas ini meluncurkan instance IDEA terpisah dengan sebuah plugin yang sedang Anda kembangkan sehingga Anda dapat men-debug-nya;
- buildPlugin - mengumpulkan arsip zip plugin Anda sehingga Anda dapat mendistribusikannya secara lokal atau melalui repositori IDEA resmi;
- memverifikasiPlugin - tugas ini memeriksa plugin Anda untuk kesalahan kotor yang mungkin tidak memungkinkan untuk diintegrasikan ke dalam Android Studio atau IDEA lainnya.
Apa lagi pemberian gradle-intellij-plugin ? Dengan bantuannya, menjadi lebih mudah untuk menambahkan dependensi pada plugin lain, tetapi kita akan membicarakannya nanti, tetapi untuk sekarang saya dapat mengatakan bahwa gradle-intellij-plugin adalah bro Anda, gunakanlah.
Kembali ke struktur plugin. File terpenting dari setiap plugin adalah plugin.xml .
plugin.xml <idea-plugin> <id>com.experiment.simple.plugin</id> <name>Hello, world</name> <vendor email="myemail@yourcompany.com" url="http://www.mycompany.com"> My company </vendor> <description><![CDATA[ My first ever plugin - try to open Hello world dialog<br> ]]></description> <depends>com.intellij.modules.lang</depends> <depends>org.jetbrains.kotlin</depends> <depends>org.intellij.groovy</depends> <idea-version since-build="163"/> <actions> <group description="My actions" id="MyActionGroup" text="My actions"> <separator/> <action id="com.experiment.actions.OpenHelloWorldAction" class="com.experiment.actions.OpenHelloWorldAction" text="Show Hello world" description="Open dialog"> <add-to-group group-id="NewGroup" anchor="last"/> </action> </group> </actions> <idea-plugin>
Ini adalah file yang berisi:
- Metadata plugin Anda: pengidentifikasi, nama, deskripsi, informasi vendor, log perubahan
- Deskripsi dependensi pada plugin lain;
- Di sini Anda juga dapat menentukan versi IDEA yang dengannya plugin Anda akan berfungsi dengan benar
- Tindakan juga dijelaskan di sini.
Tindakan
Apa itu Tindakan ? Misalkan Anda membuka menu untuk membuat file baru. Faktanya, setiap elemen menu ini telah ditambahkan oleh beberapa jenis plugin:

Tindakan adalah titik masuk ke plugin Anda untuk pengguna. Setiap kali pengguna mengklik item menu, Anda mendapatkan kendali di dalam plugin, Anda dapat merespons klik ini dan melakukan apa yang diperlukan.
Bagaimana Tindakan dibuat? Mari kita menulis Tindakan sederhana yang akan menampilkan dialog dengan pesan "Halo, Dunia".
OpenHelloWorldAction class OpenHelloWorldAction : AnAction() { override fun actionPerformed(actionEvent: AnActionEvent) { val project = actionEvent.project Messages.showMessageDialog( project, "Hello world!", "Greeting", Messages.getInformationIcon() ) } override fun update(e: AnActionEvent) { super.update(e)
Untuk membuat Action, pertama-tama kita membuat kelas yang mewarisi dari kelas AnAction . Kedua, kita harus mendefinisikan kembali metode actionPerformed, di mana parameter khusus dari kelas AnActionEvent datang . Parameter ini berisi informasi tentang konteks pelaksanaan Aksi Anda. Konteksnya merujuk pada proyek di mana Anda bekerja, file yang sekarang terbuka untuk pengguna dalam editor kode, elemen yang dipilih dalam pohon proyek dan data lain yang dapat membantu dalam memproses tugas Anda.
Untuk memperlihatkan dialog "Halo, dunia", pertama-tama kita mendapatkan proyek (hanya dari parameter AnActionEvent ), dan kemudian menggunakan Pesan kelas utilitas untuk menampilkan kotak dialog.
Fitur tambahan apa yang kita miliki di dalam Aksi? Kami dapat mengesampingkan dua metode: perbarui dan beforeActionPerformedUpdate .
Metode pembaruan dipanggil setiap kali konteks eksekusi dari Action Anda berubah. Mengapa ini mungkin berguna bagi Anda: misalnya, untuk memperbarui item menu yang ditambahkan oleh plugin Anda. Misalkan Anda menulis Tindakan yang hanya dapat berfungsi dengan file Kotlin, dan pengguna kini telah membuka file Groovy. Kemudian dalam metode pembaruan Anda dapat membuat tindakan Anda tidak dapat diakses.
Metode beforeActionPerformedUpdate mirip dengan metode pembaruan, tetapi disebut tepat sebelum actionPerformed . Ini adalah kesempatan terakhir untuk memengaruhi Aksi Anda. Dokumentasi merekomendasikan agar Anda tidak melakukan apa pun "berat" dalam metode ini sehingga berjalan sesegera mungkin.
Anda juga dapat mengikat Tindakan ke elemen tertentu dari antarmuka IDEA dan mengaturnya kombinasi tombol default untuk panggilan - Saya sarankan membaca lebih lanjut tentang ini di sini .
Pengembangan UI dalam plugin
Jika Anda membutuhkan desain dialog Anda sendiri, Anda harus bekerja keras. Kami mengembangkan UI kami karena kami ingin memiliki antarmuka grafis yang nyaman yang memungkinkan untuk menandai beberapa kutu, memiliki pemilih untuk nilai enum, dan sebagainya.
Plugin DevKit untuk pengembangan UI menambahkan beberapa tindakan, seperti formulir GUI dan Dialog . Yang pertama membuat formulir kosong untuk kami, yang kedua - formulir dengan dua tombol: Oke dan Batal .

Oke, ada perancang formulir , tapi dia ... begitu-begitu. Sebagai perbandingan, bahkan desainer Layout di Android Studio terlihat nyaman dan baik. Seluruh UI dikembangkan di perpustakaan seperti Java Swing. Perancang formulir ini menghasilkan file XML yang dapat dibaca manusia. Jika Anda tidak dapat melakukan sesuatu di perancang formulir (contoh: menyisipkan beberapa kontrol dalam sel kotak yang sama dan menyembunyikan semua kecuali satu dari kontrol), Anda perlu membuka file ini dan mengubahnya - IDEA akan mengambil perubahan ini.
Hampir setiap bentuk terdiri dari dua file: yang pertama memiliki ekstensi .form , ini hanya file XML, yang kedua adalah yang disebut kelas Bound , yang dapat ditulis dalam Java, Kotlin, tetapi apa pun yang Anda inginkan. Karena berfungsi sebagai pengontrol bentuk. Tanpa diduga, tetapi menulis di Jawa jauh lebih mudah daripada dalam bahasa lain. Karena, misalnya, penyetelan untuk Kotlin belum begitu sempurna. Ketika Anda menambahkan komponen baru dalam bekerja dengan kelas Java, komponen ini secara otomatis ditambahkan ke kelas, dan ketika Anda mengubah nama komponen di perancang, itu akan ditarik secara otomatis. Tetapi dalam kasus Kotlin, komponen tidak ditambahkan - tidak ada integrasi terjadi, Anda dapat melupakan sesuatu dan tidak mengerti mengapa tidak ada yang berhasil.
Kami meringkas dasar-dasarnya
- Untuk membuat plugin, Anda perlu: IDEA Community Edition, Plugin DevKit dan Java yang terhubung dengannya.
- gradle-intellij-plugin adalah bro Anda, itu akan sangat menyederhanakan hidup Anda, saya sarankan menggunakannya.
- Jangan menulis UI Anda sendiri kecuali diperlukan. Ada banyak kelas utilitas di IDEA yang memungkinkan Anda membuat UI sendiri di luar kotak. Jika Anda membutuhkan sesuatu yang rumit - bersiaplah untuk bekerja keras.
- Plugin dapat memiliki sejumlah Tindakan. Plugin yang sama dapat menambahkan banyak fungsi ke IDEA Anda.
Internal IDEA: komponen, PSI
Mari kita bicara tentang usus IDEA, tentang bagaimana itu diatur di dalam. Saya memberi tahu Anda agar tidak ada yang hancur di kepala Anda ketika saya menjelaskan bagian praktisnya, dan agar Anda mengerti dari mana asalnya.
Bagaimana cara mengatur IDEA? Pada tingkat pertama hierarki adalah kelas seperti Aplikasi . Ini adalah contoh IDEA yang terpisah. Untuk setiap instance IDEA, satu objek kelas Aplikasi dibuat. Misalnya, jika Anda menjalankan AppCode, Intellij IDEA, Android Studio secara bersamaan, Anda akan mendapatkan tiga instance terpisah dari kelas Aplikasi. Kelas ini dirancang untuk menangani aliran input / output.
Tempatkan Aplikasi dalam hierarki Level selanjutnya adalah kelas Project . Ini adalah konsep terdekat dengan apa yang Anda lihat saat membuka proyek baru di IDEA. Proyek biasanya diperlukan untuk mendapatkan komponen lain di dalam IDEA: kelas utilitas, manajer, dan banyak lagi.
Level detail selanjutnya adalah kelas Module . Secara umum, modul adalah hierarki kelas yang dikelompokkan ke dalam satu folder. Tapi di sini yang dimaksud dengan modul adalah modul Maven, modul Gradle. Kelas ini diperlukan, pertama, untuk menentukan dependensi antar modul, dan kedua, untuk mencari kelas di dalam modul ini.
Tempatkan Modul dalam Hirarki Tingkat detail selanjutnya adalah kelas VirtualFile . Ini adalah abstraksi dari file asli yang ada di disk Anda. Beberapa instance VirtualFile dapat sesuai dengan setiap file nyata, tetapi semuanya sama. Pada saat yang sama, jika file asli dihapus, maka VirtualFile tidak akan dihapus sendiri, tetapi hanya menjadi tidak valid.
Tempat Hierarki VirtualFile Entitas seperti Dokumen dikaitkan dengan setiap VirtualFile . Ini adalah abstraksi atas teks file Anda. Diperlukan dokumen agar Anda dapat melacak peristiwa yang terkait dengan perubahan dalam teks file : pengguna menyisipkan baris, menghapus baris, dll., Dll.
Tempatkan Dokumen dalam Hirarki Sedikit ke sisi hierarki ini adalah kelas Editor - itu adalah editor kode. Setiap proyek dapat memiliki satu Editor . Diperlukan agar Anda dapat melacak peristiwa yang terkait dengan editor kode: pengguna telah menyoroti garis di mana tanda sisipan berada, dan sebagainya.
Tempat editor dalam hierarki Hal terakhir yang ingin saya bicarakan adalah PsiFile . Ini juga merupakan abstraksi atas file nyata, tetapi dari sudut pandang mewakili elemen kode . PSI adalah singkatan dari Program Structure Interface.
Tempat PsiFile di Hirarki Dan apa yang terdiri dari setiap program? Pertimbangkan kelas Java reguler.
Kelas java polos package com.experiment; import javax.inject.Inject; class SomeClass { @Inject String injectedString; public void someMethod() { System.out.println(injectedString); } }
Ini terdiri dari menentukan paket, impor, kelas, bidang, metode, anotasi, kata kunci, tipe data, pengubah, pengidentifikasi, referensi metode, ekspresi dan token. Dan untuk setiap elemen ada abstraksi PsiElement . Artinya, masing-masing program Anda terdiri dari PsiElements .

PsiFile , pada gilirannya, adalah struktur pohon di mana setiap elemen dapat memiliki orang tua dan banyak keturunan.

Saya ingin menyebutkan bahwa PSI tidak sama dengan pohon sintaksis abstrak . Pohon sintaksis abstrak adalah pohon representasi dari program Anda setelah parser lulus program Anda, dan itu terlepas dari bahasa pemrograman apa pun. PSI, sebaliknya, terkait dengan bahasa pemrograman tertentu. Saat Anda bekerja dengan kelas Java, Anda berhadapan dengan Java PsiElements. Ketika bekerja dengan kelas Groovy - dengan PsiElements Groovy, dan sebagainya. , PSI- - , , , – .
PSI – PSI- IDEA. , , , . .
IDEA
- PSI IDEA;
- PSI- IDEA, ;
- PsiElement-.
, . .
- ,
- Tool window
- IntelliJ IDEA
- IDEA
- IntelliJ IDEA
- , , . , .
- Droidcon Italy 2017 , , , , FreeMarker- . , .
- KotlinConf , Square SQLDelight, Java, Kotlin.