前言
Android studio plugin作为辅助开发的一些工具,例如:JSON To Kotlin Class(json转类),Json Viewer(json格式数据格式化)之类的插件,目的都是为了将繁琐重复的工作自动化,提高开发效率。当前也是为了提高开发效率,探索当前项目可以模板化的代码,通过插件实现模板代码一键生成,节省手动去建立文件、拷贝模板代码的时间,同时也避免拷贝出错、拷贝带了错误引用的问题,实现提高效率的目的。
前期准备
了解Android studio 插件编写需要在什么平台下编写,用什么语言编写。
• 需要用IntelliJ IDEA进行开发,不能使用Android studio进行插件开发。
• 可以直接使用kotlin进行插件的功能编写。开发工具的版本及插件支持的最低版本。
• 因为插件的最低支持版本有不同的代码模板,所以需要关注需要支持的最低版本,及IDE使用的JDK版本。当前是参考jetbrains提供的插件模板(tag v1.13.0)开发,所以就有对IntelliJ IDEA版本要求,版本号需要在22.2.5或者之后版,不然那模板上一些方法就找不到。且从插件模板库的记录来看,tag v1.1.0是支持21.1.3的,所以可以切换到对应的tag,查看支持的版本。
• JDK版本(环境变量JAVA_HOME配置的版本):Android studio使用该环境变量,IntelliJ IDEA高版本JDK生成的插件在低版本JDK的Android studio上装不上。
• IDEA版本:根据当前使用的Android studio(最低)使用的版本及JDK
• Android studio版本:Android studio版本也是需要在222或者之后,用android studio的版本号就是:Flamingo 2022.2.1 Patch 2。
• 推荐一个安装包版本管理的软件,由JetBrains提供:Toolbox App。可以方便这些开发软件的下载、安装卸载与升级,很方便,都是一键操作,还支持多版本共存。
开发细节
1. 新建项目
如果是基于上面写的IntelliJ IDEA版本下载安装,那么就是最简单的开始,打开IDEA,新建项目就有一个IDE Plugin的入口,可以直接开始,只要注意一下JDK版本号就好了,跟使用该插件的IDEA运行的JDK环境一致或者更低。
jdk版本高了就会有以下错误1
2
3com.intellij.diagnostic.PluginException: While loading class FirApkAction:
FirApkAction has been compiled by a more recent version of the Java Runtime (class file version 55.0),
this version of the Java Runtime only recognizes class file versions up to 55.0
使用更高版本的IDEA呢?
需要另外安装一个插件开发的插件:Plugin DevKit,这之后才能新建插件开发的工程。关于高版本的就不具体展开,因为Android studio使用的版本是比较低,高版本IDEA推荐使用JDK17,具体可以自行探索,操作流程基本都是一样的。
2. 项目文件结构
插件开发是有一个固定的框架,在新建项目之后,可以参照官方的插件开发模板工程,配置需要的基础逻辑代码。
新的工程就是按照上面的模板进行抄就好了,或者是直接在上面改一下自己需要的标识就好了。改完是可以运行起来的,那下面就开始开始Android studio 代码模版的操作了。
3. Android studio 代码模板插件开发
3.1 引入Android studio依赖库
官方的代码模板的是对应的IDEA通用模板,下面是针对Android studio进行开发,还是对应当前开发的项目的代码模板。
基于Android studio提供的一个代码模板库进行二次开发:wizard-template.jar。
Mac OS 在:/Applications/Android Studio.app/Contents/plugins/android/lib。
windows应该也是在安装的根目录plugins下了。
将该依赖包引入到工程中,只作为编译使用(compileOnly)。
3.2 配置相关的版本
编辑resources/META-INF/plugin.xml的版本信息。默认新建项目已经有一些信息了,稍微进行更改一下。1
2
3
4
5
6
7
8
9
10
11
12<!-- 这里示例只有简单结构,需要修改的地方 ,其他地方与模板一致-->
<idea-plugin>
<depends>org.jetbrains.android</depends><!-- 新增 -->
<depends>org.jetbrains.kotlin</depends><!-- 新增 -->
<depends>com.intellij.modules.java</depends><!-- 新增 -->
<extensions defaultExtensionNs="com.android.tools.idea.wizard.template"><!-- 新增,对应的Android studio模板入口 -->
<wizardTemplateProvider implementation="com.gbits.plugins.generator.PluginGeneratorProvider" />
</extensions>
</idea-plugin>
在build.gradle.kts中需要对支持的IDEA版本版本进行设置,其他的按照新建的项目的默认代码也是可以。配置完成后执行Gradle Sync就会想在相关的平台引用。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19// 省略模板代码
// 新增
dependencies {
compileOnly(files("lib/wizard-template.jar"))
}
intellij {
version.set("2021.1.3")// 支持的Android studio最低版本的IDE版本
type.set("IC") // Target IDE Platform
plugins.set(listOf("java", "com.intellij.java", "org.jetbrains.android", "android", "org.jetbrains.kotlin"))
}
tasks {
patchPluginXml {
sinceBuild.set("211")//支持的最低版本号,与上面的IDE版本是对应的,可以到Intellij IDEA的历史版本页面查看,见下面引用。
untilBuild.set("241.*")
}
}
3.3 关键代码
3.3.1 模板入口
在上面plugin.xml中引用的类就是模板的入口了。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21package com.gbits.plugins.generator
import com.android.tools.idea.wizard.template.Template
import com.android.tools.idea.wizard.template.WizardTemplateProvider
/**
*
* @Author : yangfj
* @Date : 2024/03/13 16:20
* @Description : 模版类入口
*/
class PluginGeneratorProvider : WizardTemplateProvider() {
override fun getTemplates(): List<Template> = mutableListOf<Template>().apply {
add(TemplateGenerator.activityWithTitle()) //标题 简单Activity
add(TemplateGenerator.activityWithRecyclerView()) // 标题 + 下拉刷新 Activity
add(TemplateGenerator.activityWithTabLayout()) // 标题 + ViewPager Activity
add(TemplateGenerator.fragment()) // 简单Fragment
add(TemplateGenerator.fragmentWithRecyclerView()) // 下拉刷新 Fragment
}
}
每个模板返回的类型都是com.android.tools.idea.wizard.template.Template,参考android-code-template(见引用)的配置。
3.3.2 实现流程
下面从使用的角度去说明代码流程。
例如:选中ActivityTabLayout选项,弹出配置弹窗,通过配置信息执行相关逻辑代码,生成代码字符串,最终以对应的格式生成文件。
3.3.2.1 配置输入
下面是输入框的配置,同时还有布尔值类型参数(booleanParameter)实现是否勾选的功能,以及枚举类型(enumParameter)实现选择指定的选项,最终将这些。具体代码在com.gbits.plugins.generator.TemplateGenerator。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
val moduleNameParameter = stringParameter {
name = "moduleName:功能模块 + 类名" // 标题
default = if (isFragment) { // 显示的默认值
"template.TemplateFragment"
} else {
"template.TemplateActivity"
}
help = "类文件名称:生成文件的存放位置,不是APP包名,直接创建,存在则覆盖。" // 说明性文字
constraints = listOf(Constraint.PACKAGE) // 输入的规则要求
}
// 最终通过widgets()方法将所需的控件按顺序设置到配置窗口中,
// booleanParameter、enumParameter也类似上面的方式配置,这个就可以在新建窗口的时候看到了
widgets(TextFieldWidget(moduleNameParameter))
// 最终取值
moduleNameParameter.value // 取得用户输入的值
3.3.2.2 根据配置生成文件
在配置窗口点击Finish后执行Temple的recipe方法,通过该方法的上下文RecipeExecutor、参数ModuleTemplateData就可以直接对文件进行操作。1
2
3
4
5
6
7
8
9
10val srcDir = moduleData.srcDir // 当前选中的目录
// val rootDir = moduleData.rootDir // 当前的app目录
val resDir = moduleData.resDir // 资源文件的目录
val packagePath = moduleData.packageName // 当前选中的包路径
// 生成对应路径的文件,代码通过字符串的形式传入
RecipeExecutor.save(String,File)
// 清单文件的处理(),例如生成Android四大组件的声明
RecipeExecutor.generateManifest()
• 生成kotlin代码
A. 代码完全当成是字符串,直接将字符串写入对应的文件,所以使用的时候有些引用还需要导包。下面是最简单的一个类的字符串形式,Layout亦是如此。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15/**
* 生成data class 作为model,只有一个默认参数
* @param packageName 选中的路径位置,默认都是生成在该目录
* @param className data class 名字
*/
fun createModelTemplate(packageName: String, className: String): String {
return """
package ${packageName}.data.model
data class $className (
val id :String = "",
)
""".trimIndent()
}
B. 如果觉得直接写字符串形式的代码不够优雅,那也是可以直接使用square/kotlinpoet实现kotlin文件的写入。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20// 例如生成一个类及构造函数
// 目标类
class HelloWorld {
private val greeting: String
constructor(greeting: String) {
this.greeting = greeting
}
}
// 生成类的代码
val flux = FunSpec.constructorBuilder()
.addParameter("greeting", String::class)
.addStatement("this.%N = %N", "greeting", "greeting")
.build()
val helloWorld = TypeSpec.classBuilder("HelloWorld")
.addProperty("greeting", String::class, KModifier.PRIVATE)
.addFunction(flux)
.build()
• 生成XML资源文件
A. 那资源文件(xml文件)也是可以通过合并的方式对源文件进行插入,当文件不存在将会创建对应的文件。1
2// 生成对应路径的文件,样式(style,string,dimen,color)通过字符串的形式传入
RecipeExecutor.mergeXml(String,File)
需要注意:样式(style,string,dimen,color)字符串是完整的xml文件内容。
例如:需要插入一个style,样式内容是:1
2
3
4
5
6
7
8
9
10
11
12
13
14 /**
* Activity带有tabLayout的text样式
*/
fun tabLayoutText15Style(): String {
return """
<resources>
<!--15 号 tabLayout-->
<style name="tab_layout_text_15_style" parent="TextAppearance.AppCompat.Button">
<item name="android:textSize">@dimen/b1_big</item>
<item name="android:letterSpacing">@fraction/default_text_spacing_size</item>
</style>
</resources>
""".trimIndent()
}
B. 如果是这个方式还是不够优雅,那么就可以通过XML解析(DOM、PULL、SAX )的方式去读取、生成对应的xml文件。这块内容就不写了,有很多现成的例子,可以直接搜索到。
上面是简单的代码流程,更具体的需要查看文件,这里就不贴代码了。
3.4 成果
了解Android studio插件开发的基础技术,并且使用Android studio的库实现二次开发,可以用到具体的项目上,例如相同代码结构的:雷霆会员,掌上问道。
成果gitlab地址见引用5。
4. 插件发布
4.1 本地发布
在使用上是本地使用,所以只有本地的发布步骤。
- 在IDEA中,右侧找到Gradle任务列表,执行intellij > buildPlugin 就可以将源码打包成插件(zip文件),生成文件在项目目录> build > distributions下。
- 在Android studio中,Setting > Plugin > Install Plugin from Disk.. ,选中上一步生成的zip文件,需要重启一下IDE。
4.2 线上发布
注册账号,并上传,具体可看引用7的链接。提交后一般需要两天的审核期,审核通过后可以在Android studio的插件中查到。
发布的时候,可能会遇到报错不让提交
Plugin has no dependencies. Please check the documentation
解决方案是:
在plugin.xml文件中添加1
<depends>com.intellij.modules.lang</depends>
5. 使用
因为这一次的插件开发是针对当前的Android项目,所以一些包、资源的引用都是对应当前项目的,或者说是有局限性。
在使用上与Android studio默认提供的模板是一样的使用步骤。通File > New> Other> 选择对应的模板。
这一次只有新增5个比较有代表性的模板,当然需要了解目的情况下才大概理解的样式。
简单说明一下代码模板功能及会生成哪些文件:
• LTActivityActionBar,带有标题的Activity,生成Activity、layout文件。
• LTActivityRecyclerView,下拉刷新的Activity(RecyclerView),生成Activity、layout、adapter、model、viewModel文件。
• LTActivityTabLayout,分页的Activity(ViewPager),生成Activity、layout、adapter、model、viewModel、Fragment文件。
• LTFragment,基础Fragment,生成Fragment、layout文件。
• LTFragmentRecyclerView,分页的Fragment(ViewPager),生成Fragment、layout、adapter、model、viewModel文件。
注意:
生成的文件默认会缺少导包。在使用这些模板的时候,虽然是点击一键插入,但是模板没有自动导包,还需要打开一下这些文件,才会自动导包。(这里的自动导包是Android studio中的自动导包,新版本好像都是自动导包。在Setting > Editor > General>勾选相关的add unambiguous imports on the fly)
6. 存在的问题
- 资源文件的复制尚未完善。可以直接使用copy方法处理。
- 不够灵活,还是针对当前项目的结构、代码。
引用
- Intellij IDEA的历史版本。https://www.jetbrains.com/idea/download/other.html
- Android studio的历史版本。 https://developer.android.com/studio/archive
- Intellij IDEA插件开发代码模板。https://github.com/JetBrains/intellij-platform-plugin-template
- JetBrains安装管理Toolbox App。https://www.jetbrains.com/toolbox-app/
- 这次开发的Android studio插件。https://ice.g-bits.com/yangfj/AndroidTemplatePlugins
- Java代码生成。https://github.com/square/javapoet
- 插件通过Gradle发布到Jetbrains MarketPlace 。https://plugins.jetbrains.com/docs/intellij/publishing-plugin.html
- 插件发布到线上。https://www.ideaplugin.com/idea-docs/Part%20I%20%E6%8F%92%E4%BB%B6/%E7%AC%AC%E4%B8%80%E4%B8%AA%E6%8F%92%E4%BB%B6/Using%20Gradle/Publishing%20Plugins%20with%20Gradle.html
- Android studio代码模板的参考库。https://github.com/AlvinScrp/android-code-template