Android Studio插件開發3之Extensions And Extension Points(擴展與擴展點)

插件開發系列
Android Studio插件開發1之插件介紹與環境搭建
Android Studio插件開發2之Action System
Android Studio插件開發3之Extensions And Extension Points(擴展與擴展點)

如果說自己定義Action并實現相應的功能邏輯是造輪子的話,那么實現Extensions就是使用別人的輪子了,簡單快速實現復雜的功能!

定義

Extensions 和Extension Points是Intellij平臺提供的一套供插件之間或者是插件與平臺核心功能之間通信的接口。

Extension points

一個插件提供給其它插件擴展自己功能的入口點,通過定義接口來約定溝通的方法,通俗點說就是造好輪子等別人來用。

Extensions

一個插件聲明一個Extension,并通過對應的Extension Point實現相應的功能,通俗的說就是復用別人造好的輪子。

關系圖

如果文字表達得不夠明白,看一下以下的說明圖應該能加深理解,畢竟一圖勝千言

說明圖.png

聲明Extension和Extension Point

Extension和Extension Point都需要在plugin.xml聲明,聲明的語法如下

Extension Point

<extensionPoints>
  <extensionPoint name="ConcreteExtensionPoint1" beanClass="com.example.BeanClass">
  <extensionPoint name="ConcreteExtensionPoint2" interface="com.example.Interface">
</extensionPoints>
  • name 此Extension Point的名字
  • beanClass/interface 提供的接口,定義與Extension的交互方式

Extension Point聲明的方式有兩種,第一是通過interface的方式,此方式定義一個java接口供擴展方實現;第二是beanClass的方式,其定義一個java bean,并在擴展方聲明Extension的時候,由擴展方提供java bean的屬性,這里不明白的話先看下面Extension的聲明。

Extension

聲明通過interface方式定義的Extension

<extensions defaultExtensionNs="com.intellij">
    <appStarter implementation="MyTestPackage.MyTestExtension1" />
</extensions>

Extesion的聲明應該被包裹在<extensions>標簽下

  • <extensions>標簽的defaultExtensionNs屬性的值是對應Extension Point的插件id,但如果這個Extension Point由Intellij提供,則是“com.intellij”,插件的id可以在plugin.xml文件查看
  • 其中的標簽的名字是Extension Point的name,例如要用到上面聲明的Extension Point,則是<ConcreteExtensionPoint1>

這里內容很簡單,就是實現接口,通過implementation屬性提供接口

聲明通過beanClass方式定義的Extension

<extensions defaultExtensionNs="com.intellij">
    <codeInsight.template.postfixTemplateProvider language="JAVA" implementationClass="com.kogitune.intellij.codeinsight.postfix.AndroidPostfixTemplateProvider"/>
</extensions>

這里language和implementationClass實際上是java bean的屬性,下面是這個Extension Point的beanClass的內容

public class LanguageExtensionPoint<T> extends CustomLoadingExtensionPointBean implements KeyedLazyInstance<T> {

  // these must be public for scrambling compatibility
  @Attribute("language")
  public String language;

  @Attribute("implementationClass")
  public String implementationClass;

  private final NotNullLazyValue<T> myHandler = new NotNullLazyValue<T>() {
    @Override
    @NotNull
    protected T compute() {
      try {
        return (T)instantiateExtension(implementationClass, ApplicationManager.getApplication().getPicoContainer());
      }
      catch (ClassNotFoundException e) {
        throw new RuntimeException(e);
      }
    }
  };

  @NotNull
  @Override
  public T getInstance() {
    return myHandler.getValue();
  }

  @Override
  public String getKey() {
    return language;
  }
}

其中language和implementationClass上加了@Attribute注解,加了這個注解的屬性需要由Extension在聲明時提供

<codeInsight.template.postfixTemplateProvider language="JAVA" implementationClass="com.kogitune.intellij.codeinsight.postfix.AndroidPostfixTemplateProvider"/>

實例

一般情況下都是我們寫的都是功能比較簡單的小插件,除非是專門寫插件的軟件公司,否則很少情況下會在插件里定義Extension Point供別人使用,所以重點是怎樣使用別人的Extension Point,尤其是系統提供的Extension Point,那可是系統的核心功能。(好吧是其實我不會寫Extension Point(-_-))

通過Extension可以做什么?

先看三張圖

annotate.png
livetemplate.gif
postfixcompletion.gif

第一張圖是代碼編輯器的錯誤提示,第二張圖是live template,第三張圖是系統提供的postfix completion,相信這些功能我們平時都不少用,其實這些功能我們都可以通過Extension來自定義的!

這里提供一個livetemplate的實例和推薦一個postfix completion的項目

live template

想當年Eclipse用得飛起的時候“syso”打印字符串到控制臺的代碼補全用得非常順手,剛轉到Intellij平臺的時候還有點不適應呢,現在借助live template我們也能實現一個Intellij版本的“syso”補全

首先,找到Extension Point,這里的Extension Point是系統提供的defaultLiveTemplatesProvider,這樣我們可以在plugin.xml中聲明我們的Extension

<extensions defaultExtensionNs="com.intellij">
        <!-- Add your extensions here -->
        <defaultLiveTemplatesProvider implementation="MyTemplateProvider"/>
    </extensions>

這個Extension Point是通過interface聲明的,所以提供實現類MyTemplateProvider如下

import com.intellij.codeInsight.template.impl.DefaultLiveTemplatesProvider;
import org.jetbrains.annotations.Nullable;

public class MyTemplateProvider implements DefaultLiveTemplatesProvider {

    @Override
    public String[] getDefaultLiveTemplateFiles() {
        return new String[]{"templates/mytemplatefile"};
    }

    @Nullable
    @Override
    public String[] getHiddenLiveTemplateFiles() {
        return new String[0];
    }
}

內容很簡單,就是返回一個文件的路徑,是相對于resources文件夾的相對路徑,不要后綴名,絕對路徑是 projectpath/resources/templates/mytemplatefile.xml

也就是說我們只要在這個文件里按語法定義好live template就好了,重點也是在定義live template上,mytemplatefile.xml的內容如下

<templateSet group="other">
    <template name="syso" value="System.out.println($END$);"
              description="Prints a string to System.out like Eclipse"
              toReformat="false" toShortenFQNames="true">
        <context>
            <option name="JAVA_STATEMENT" value="true"/>
        </context>
    </template>
</templateSet>

一個<template>標簽定義一個live template,name屬性和value屬性就是livetemplate的縮寫和縮寫展開對應的內容,這里只展示實現Extension的邏輯,具體怎樣自定義live template可以參考相關文檔
https://www.jetbrains.com/help/idea/live-templates.html

下面是這個小demo的運行效果

customlivetemplate.gif

postfix completion

Android Studio和IDEA提供了一部分postfix completion,可惜的是并沒有提過我們在IDE里自定義postfix completion的入口,但是可以通過插件增加自己的postfix completion,其原理也是利用系統的Extension Point

有興趣的朋友可以看看這個我參與的插件項目

android-postfix-plugin

其中用到的Extension Point是codeInsight.template.postfixTemplateProvider

問答

  • 系統有那么多的功能,我怎么知道它有沒有Extension Point或者它的Extension Point是什么?

    不用擔心,Intellij的文檔提供了下面的Extension Point列表

  • 有Extension Point,但是不會用怎么辦?

    查看其他插件的源碼,例如那些提供智能補全的插件,都是通過系統的Extension Point完成的,所以可以推測一下自己常用的插件里有沒有自己需要使用的Extension Point來學習怎樣使用

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容