基于IntelliJ IDEA/Android Studio插件開發(fā)指南(推薦)
為防止盜鏈,本文首發(fā)于于果的博客園,轉(zhuǎn)載請注明出處!原文鏈接:https://www.cnblogs.com/yuxiuyan/p/14682460.html
前言
目前在為安卓手機(jī)QQ做自動(dòng)化的相關(guān)工作,包括UI自動(dòng)化,邏輯層自動(dòng)化等。使用到的uiautomator
等框架,需要在Android Studio進(jìn)行編碼工作。
其中很多工作如果做到插件化的話,可以有效地節(jié)省時(shí)間成本,提升大家的自動(dòng)化效率。
比如運(yùn)行自動(dòng)化的時(shí)候,需要用到我們自定義的shell命令。我們可以通過插件來實(shí)現(xiàn)一鍵運(yùn)行。
在運(yùn)行adb shell am instrument
命令的時(shí)候,需要編譯出test APK
和target APK
。手Q整體的git倉庫很大,編譯耗時(shí)很久。我們想著通過一些方法來優(yōu)化這個(gè)耗時(shí)。其中一個(gè)步驟就是,把我們代碼目錄下的變更,同步到一個(gè)編譯目錄下。
這個(gè)小功能的最合適的形態(tài),自然就是Android Studio上的一個(gè)插件。點(diǎn)擊一個(gè)按鈕,一鍵同步,那可真是在米奇妙妙屋吃妙脆角——妙到家了!
Android Studio是基于Intellij IDEA開發(fā)的,所以開發(fā)Android Studio的插件,其實(shí)就是開發(fā)IDEA的插件。
根據(jù)官方推薦,使用IDEA IDE來開發(fā)IDEA插件。
插件開發(fā)的基本流程
1. 環(huán)境配置
1.1 安裝PDK
正如Java開發(fā)需要安裝Java DevKit,IDEA插件開發(fā)也需要安裝Plugin DevKit。PDK的作用是為插件提供IDEA內(nèi)建支持以及相關(guān)庫函數(shù)。
打開Intellij IDEA --> Preferences --> Plugins
,如果沒有安裝,可以在marketplace里面搜索,并安裝。
1.2 配置插件開發(fā)SDK
配置開發(fā) IntelliJ 平臺(tái)插件的SDK也就是IntelliJ Platform Plugin SDK,基于 JDK 之上運(yùn)行,類似于開發(fā) Android 應(yīng)用需要 Android SDK。
切換到 File --> Project Structure
,選擇左側(cè)欄 Platform Settings
下的 SDKs
,點(diǎn)擊+
按鈕,先選擇 Add JDK
,指定 JDK 的路徑;再選擇Add IntelliJ Platform Plugin SDK
,指定上面添加的JDK為插件需要的JDK。需要注意的是,從IDEA 2020.3開始,不能再使用Java1.8版本。因?yàn)镮DEA 2020.3版本基于Java11構(gòu)建,所以如果想要開發(fā)2020.3及以后版本的IDEA的插件,需要選擇Java11版本。
2. 新建插件工程
File --> New --> Project
,在彈出的窗口中選擇Gradle,然后選擇Java(這表明我們使用Java語言開發(fā))和Intellij Platform Plugin,點(diǎn)擊Next,然后設(shè)置項(xiàng)目的名稱和位置,點(diǎn)擊Finish完成創(chuàng)建。
3. Action
我們在IntelliJ自定義的插件可以添加到菜單項(xiàng)目(如右鍵菜單中)或者是放在工具欄中。當(dāng)用戶點(diǎn)擊時(shí)觸發(fā)一個(gè)動(dòng)作事件,IntelliJ則會(huì)回調(diào)AnAction
子類的actionPerformed
函數(shù)。因此我們只需重寫actionPerformed
函數(shù)即可。我們可以認(rèn)為Action是插件的觸發(fā)入口。我們可以直接右鍵New --> Plugin DevKit --> Action
新建action,這個(gè)action是AnAction的子類。
在接下來的彈出窗口中,我們可以創(chuàng)建一個(gè)Action。
- Action ID:這個(gè)action的唯一標(biāo)識(shí)
- Class Name:action的類名
- Name:action的名稱
- Description: action的描述信息
- Groups:這個(gè)標(biāo)簽指定我們自定義的插件應(yīng)該放入到哪個(gè)菜單下面。 在IntelliJ IDEA菜單欄中有很多菜單如File、Edit、View、Navigate、Code、……、Help等。他們的ID一般是
菜單名+Menu
的方式。比如,我們想將我們自定義的插件放到Help菜單中,作為Help菜單的子選項(xiàng)。那么在Groups中就指定HelpMenu
。左側(cè)的Anchor屬性用于描述位置,主要有四個(gè)選項(xiàng):first、last、before、after。他們的含義如下:
first:放在最前面
last:放在最后
before:放在relative-to-action屬性指定的ID的前面
after:放在relative-to-action屬性指定的ID的后面
Keyboard Shortcuts:可以為這個(gè)action指定快捷鍵
public class TestAction extends AnAction { @Override public void actionPerformed(AnActionEvent e) { NotificationGroup notificationGroup = new NotificationGroup("testid", NotificationDisplayType.BALLOON, false); /** * desc: 這是一個(gè)IDEA的通知,會(huì)通知到idea右下角的懸浮小窗 * content : 通知內(nèi)容 * type :通知的類型,warning,info,error */ Notification notification = notificationGroup.createNotification("測試通知", MessageType.INFO); Notifications.Bus.notify(notification); } }
創(chuàng)建完之后,我們也可以在src/resources/META-INF/plugin.xml
中,看到我們之前寫入的action信息,如果想要修改,可以在這個(gè)配置文件中直接修改。
<actions> <!-- Add your actions here --> <action id="testId" class="com.example.yuguo.TestAction" text="通知" description="測試通知的功能"> <add-to-group group-id="ToolsMenu" anchor="first"/> </action> </actions>
4. 配置描述
src/resources/META-INF/plugin.xml
是整個(gè)插件的配置文件,里面定義了插件的名稱,描述信息,支持的IDEA版本號(hào),作者信息,action的相關(guān)信息等。
<idea-plugin> <!--插件的id,屬于全局唯一標(biāo)識(shí)--> <id>plugin.test</id> <!--插件的名稱--> <name>PluginTest</name> <vendor email="xxxx@example.com" url="">author_name</vendor> <!--插件的描述信息,支持html--> <description><![CDATA[ Plugin Test<br> <em>v1.0</em> ]]></description> <extensions defaultExtensionNs="com.intellij"> <!-- Add your extensions here --> </extensions> <actions> <!-- 這里是剛剛定義的插件信息 --> <action id="testId" class="com.example.yuguo.TestAction" text="通知" description="測試通知的功能"> <add-to-group group-id="ToolsMenu" anchor="first"/> </action> </actions> </idea-plugin>
5. 調(diào)試、打包
調(diào)試
等到配置完成后,在IDEA右側(cè)的Gradle一欄中,有Intellij的集合。點(diǎn)擊里面的runIde,可以打開一個(gè)沙盒,里面運(yùn)行包含著該插件的IDEA實(shí)例。也可以右鍵選擇debug模式運(yùn)行。
打包
點(diǎn)擊上圖的buildPlugin,就可以在build/distributions/
目錄下面生成插件zip包,這個(gè)包就是我們需要的最終產(chǎn)物。在IDEA設(shè)置Preferences --> Plugins
,點(diǎn)擊installed旁邊的設(shè)置按鈕,選擇Install Plugin from Disk
,然后選擇這個(gè)zip,就可以安裝到IDEA中了。
插件的組件
GUI
ToolWindow
工具視窗(ToolWindow)的功能主要是進(jìn)行信息的顯示,同時(shí)用戶還可以直接在toolwindow中進(jìn)行操作調(diào)用工具,比如IDE下方默認(rèn)的terminal、Git等。作為IDE側(cè)邊欄中較大的一部分,toolwindow與用戶的交互在整個(gè)ui中非常重要。
實(shí)現(xiàn)toolwindow主要分為兩步,第一步創(chuàng)建類實(shí)現(xiàn)ToolWindowFactory接口,編寫需求的toolWindowFactory實(shí)例,第二步在plugin.xml中注冊該ToolWindow。
當(dāng)用戶單擊工具窗口按鈕時(shí),將調(diào)用工廠類的方法createToolWindowContent(),并初始化工具窗口的UI。此過程可確保未使用的工具窗口不會(huì)在啟動(dòng)時(shí)間或內(nèi)存使用上造成任何開銷:如果用戶不與插件的工具窗口進(jìn)行交互,則不會(huì)加載或執(zhí)行任何插件代碼。
public class ToolFactoryCompute implements ToolWindowFactory { private ToolWindow myToolWindow; private JPanel myPanel; private JTextArea textContent; private JScrollPane myScrollPane; /** * @param project 項(xiàng)目 * @param toolWindow 窗口 */ @Override public void createToolWindowContent(@NotNull Project project, @NotNull ToolWindow toolWindow) { myToolWindow = toolWindow; // 將顯示面板添加到顯示區(qū) ContentFactory contentFactory = ContentFactory.SERVICE.getInstance(); Content content = contentFactory.createContent(mPanel, "Control", false); toolWindow.getContentManager().addContent(content); } }
在plugin.xml中注冊toolwindow。
<extensions defaultExtensionNs="com.intellij"> <!-- canCloseContents表示是否可以關(guān)閉這個(gè)toolwindow, anchor表示toolwindow的位置, id是toolwindow的名字, factoryClass表示toolwindow的工廠類 --> <toolWindow canCloseContents="false" anchor="bottom" id="Compute Code Lines" icon="/myToolWindow/test.png" factoryClass="tools.ToolFactoryCompute"> </toolWindow> </extensions>
Dialog
會(huì)話框(Dialog)可以與用戶交互,獲取用戶自定義輸入的內(nèi)容,也可以作為提示彈窗,告訴用戶信息。會(huì)話框的實(shí)現(xiàn)需要定義一個(gè)繼承了IDEA的DialogWrapper
抽象類的子類,這個(gè)子類就是自定義的會(huì)話框?qū)崿F(xiàn),所有的樣式定義、功能觸發(fā)都是放到這個(gè)子類里的,比如以下實(shí)現(xiàn):
public class FormTestDialog extends DialogWrapper { private String projectName; //假如需要獲取到項(xiàng)目名,作為該類的屬性放進(jìn)來 // DialogWrapper沒有默認(rèn)的無參構(gòu)造方法,所以需要重寫構(gòu)造方法,它提供了很多重載構(gòu)造方法, // 這里使用傳project類型參數(shù)的構(gòu)造方法,通過Project對象可以獲取當(dāng)前IDEA內(nèi)打開的項(xiàng)目的一些屬性, // 比如項(xiàng)目名,項(xiàng)目路徑等 public FormTestDialog(@Nullable Project project) { super(project); setTitle("表單測試"); // 設(shè)置會(huì)話框標(biāo)題 this.projectName = project.getName(); } // 重寫下面的方法,返回一個(gè)自定義的swing樣式,該樣式會(huì)展示在會(huì)話框的最上方的位置 @Override protected JComponent createNorthPanel() { return null; } // 重寫下面的方法,返回一個(gè)自定義的swing樣式,該樣式會(huì)展示在會(huì)話框的最下方的位置 @Override protected JComponent createSouthPanel() { return null; } // 重寫下面的方法,返回一個(gè)自定義的swing樣式,該樣式會(huì)展示在會(huì)話框的中央位置 @Override protected JComponent createCenterPanel() { return null; } }
業(yè)務(wù)實(shí)踐
獲取文件差異
方案一:自建Diff工具
為了獲得代碼目錄與編譯目錄的文件差異,必然要使用到Diff工具,這其中涉及到很多自定義的規(guī)則,比如差異文件是否要忽略等。優(yōu)點(diǎn)是可以完全自定義靈活的識(shí)別差異的規(guī)則。缺點(diǎn)是耗時(shí)較久,畢竟要編寫一套Diff系統(tǒng)。時(shí)間比較緊,所以這個(gè)方案pass了。
方案二:使用JGit
JGit是Java編寫的一套Git工具,通過Java代碼就可以調(diào)用到Git的所有指令,可以完美解決獲得文件差異的需求。但是經(jīng)過實(shí)際測試發(fā)現(xiàn),在調(diào)用git.status.call()
方法時(shí) ,由于它需要初始化Git,包括建立diff,filetree等操作,對于大倉庫,一次運(yùn)行就要十幾秒,不能接受,故放棄。
Git git = Git.open(new File("~/source-code.temp-1/git")); Status status = git.status().call(); //返回的值都是相對工作區(qū)的路徑,而不是絕對路徑 status.getAdded().forEach(it -> System.out.println("Add File :" + it)); //git add命令后會(huì)看到變化 status.getRemoved().forEach(it -> System.out.println("Remove File :" + it)); ///git rm命令會(huì)看到變化,從暫存區(qū)刪除的文件列表 status.getModified().forEach(it -> System.out.println("Modified File :" + it)); //修改的文件列表 status.getUntracked().forEach(it -> System.out.println("Untracked File :" + it)); //工作區(qū)新增的文件列表 status.getConflicting().forEach(it -> System.out.println("Conflicting File :" + it)); //沖突的文件列表 status.getMissing().forEach(it -> System.out.println("Missing File :" + it)); //工作區(qū)刪除的文件列表
方案三:利用內(nèi)存Git
經(jīng)過方案二,我們發(fā)現(xiàn)git是符合我們要求的,但是因?yàn)镴Git要初始化,所以耗時(shí)較久。但是我們在運(yùn)行IDEA的時(shí)候,在終端使用git status
非???,是毫秒級(jí),那我們完全可以利用內(nèi)存中的git,直接執(zhí)行git status
命令,在返回結(jié)果中去匹配文件差異。
通過讓Java執(zhí)行g(shù)it命令,可以達(dá)到毫秒級(jí)相應(yīng)。
Java執(zhí)行shell命令并返回執(zhí)行結(jié)果
/** * 執(zhí)行shellCommand命令,獲取命令的返回結(jié)果。在返回結(jié)果中,把符合條件的文件名放置到文件集合中 * * @param cmd shell命令 * @return 命令的輸出結(jié)果 */ public static String executeCommand(String[] cmd) throws IOException { String resultStr = ""; // 利用runtime去執(zhí)行shell命令 Process ps = Runtime.getRuntime().exec(cmd); // 獲取process對象的正常流和異常流 try (BufferedReader brInfo = new BufferedReader(new InputStreamReader(ps.getInputStream())); BufferedReader brError = new BufferedReader(new InputStreamReader(ps.getErrorStream()))) { StringBuilder stringBuffer = new StringBuilder(); String line; // 讀取輸出結(jié)果,按照每行讀取 if (brInfo.readLine() != null) { while ((line = brInfo.readLine()) != null) { stringBuffer.append(line).append("\n"); // 處理文件差異 filterFiles(line); } } else { // 如果正常輸出流為null,那么就獲取異常流 while ((line = brError.readLine()) != null) { stringBuffer.append(line).append("\n"); } } // 等待shell命令執(zhí)行完成 ps.waitFor(); resultStr = stringBuffer.toString(); } catch (Exception e) { e.printStackTrace(); } // shell命令的返回結(jié)果 return resultStr; } // 在main函數(shù)中測試 public static void main(String[] args) { String cmd = "git status"; String resultStr = executeCommand(new String[]{"/bin/sh", "-c", cmd}); System.out.println(resultStr); }
參考
https://blog.csdn.net/huachao1001/article/details/53856916
https://blog.csdn.net/huachao1001/article/details/53883500
https://plugins.jetbrains.com/docs/intellij/welcome.html?from=jetbrains.org
到此這篇關(guān)于基于IntelliJ IDEA/Android Studio插件開發(fā)指南(推薦)的文章就介紹到這了,更多相關(guān)IDEA Android Studio插件開發(fā)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Android打包上傳AAR文件到Maven倉庫的示例
- Android Studio新建工程默認(rèn)在build.gradle中加入maven阿里源的問題
- 解決android studio 3.0 加載項(xiàng)目過慢問題--maven倉庫選擇
- android 上傳aar到私有maven服務(wù)器的示例
- Android?Gradle?插件自定義Plugin實(shí)現(xiàn)注意事項(xiàng)
- Android?Studio?中Gradle配置sonarqube插件(推薦)
- 淺談Android插件化
- Android?使用maven?publish插件發(fā)布產(chǎn)物(aar)流程實(shí)踐
相關(guān)文章
用VSCode實(shí)現(xiàn)內(nèi)網(wǎng)穿透的步驟詳解
這篇文章給大家介紹了如何用VSCode實(shí)現(xiàn)內(nèi)網(wǎng)穿透,文中通過圖文結(jié)合的方式給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作有一定的幫助,需要的朋友可以參考下2023-12-12git中submodule子模塊的添加、使用和刪除的示例代碼
這篇文章主要介紹了git中submodule子模塊的添加、使用和刪除的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-08-08vscode使用markdown無法預(yù)覽網(wǎng)絡(luò)圖片的解決方法
本文主要介紹了vscode使用markdown無法預(yù)覽網(wǎng)絡(luò)圖片的解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-01-01虛擬主機(jī)下實(shí)現(xiàn)多域名綁定不同的子目錄的方法
虛擬主機(jī)域名綁定子目錄asp php html (通用)2010-03-03UTF8和GBK編碼互轉(zhuǎn)實(shí)現(xiàn)解析
這篇文章主要為大家介紹了UTF8和GBK編碼互轉(zhuǎn)實(shí)現(xiàn)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07