目录

Android Studio Plugin 开发

背景

最近,在搞 kmp 工程化的一些东西,因为官方不支持 ohos,所以其对应的 android studio plugin 也不支持 ohos

比如:创建一个 module 等等,都不会自动携带 ohos 目录

看了 kuikly 提供了一个 project 模板,但是没有提供 new module 的模板,所以,开始研究有关 new module 的插件开发

Android Studio Plugin 开发

创建工程

参考: https://plugins.jetbrains.com/docs/intellij/creating-plugin-project.html

需要先安装 plugin devkit

然后按照文档创建即可

注意
JDK 版本 因为 platform version 不同,要求不同

添加本地依赖

因为我们要开发的是 android studio 插件,所以 dependency 需要选择 android studio 平台,还有对应的版本

android studio 对应的版本在这里查看:https://plugins.jetbrains.com/docs/intellij/android-studio-releases-list.html

如果依赖添加的是对应版本,则会自动下载对应版本的 android studio,如果我们本地存在一个版本的 android studio,则可以从本地依赖,完全没必要下载

配置如下:

dependencies {

    intellijPlatform {
        // 本地路径
        local("/Applications/Android Studio.app/Contents")

        // 添加一些打在 andorid studio 内部的插件
        // 不然有些代码,我们无法引用
        bundledPlugins(listOf(
            "com.intellij.java",
            "org.jetbrains.kotlin",
            "org.jetbrains.android",
        ))
    }
}

接下来就是进入开发的步骤了

IDEA 的 New Module 开发

查了很多网上的教程,都是对于 action 的开发

这块我自己验证了下没什么问题

但是,当我想开发 New Module 的界面,增加下图中红色部分的一个 type 的时候,遇到了困难 /img/in-post/as_module_type.jpg

官方文档如下:文档链接

简单介绍一下就是:

  1. 添加一个 module type
  2. 实现 module type 和 module builder

然后就可以了

按照文档介绍以后,我如果将上面的 dependency 的配置改成 IDEA,确实可以看到,new module 多了一个 type

但是,android studio 纹丝不动

Android Studio 的 New Module 开发

尝试了半天,无功而返,只好去查看 andorid studio 的源码,提供一个可以预览源码的网址 链接

经过不懈努力,最终,发现了如下配置

<extensions defaultExtensionNs="com.android">
    <moduleDescriptionProvider implementation="com.XXXXX"/>
</extensions>

通过 copy 相关代码到我自己的工程中,最终实现了 andorid studio 中增加一个 module type 的能力

部分核心代码

需要继承自 ModuleDescriptionProvider,并且实现其 ModuleGalleryEntry,然后就进入到了正常的 create step 步骤

create step 也需要继承自 ConfigureModuleStep 重写其 createMainPanel,也就是我们的 UI 界面

同时,需要绑定一个 Model,model 又有一些 render 方法,负责渲染对应模板

其实,我们没必要这么复杂,可以简化一些操作

部分 copy 过来的核心代码如下:

// plugin.xml 中注册 moduleDescriptionProvider
<extensions defaultExtensionNs="com.android">
    <moduleDescriptionProvider implementation="com.XXX.TestModuleDescriptionProvider"/>
</extensions>

class TestModuleDescriptionProvider : ModuleDescriptionProvider {
    override fun getDescriptions(project: Project): Collection<ModuleGalleryEntry> = listOfNotNull(
        if (StudioFlags.NPW_NEW_KOTLIN_MULTIPLATFORM_MODULE.get()) KotlinMultiplatformLibraryModuleTemplateGalleryEntry() else null,
    )

    // copy from kmp plugin
    private class KotlinMultiplatformLibraryModuleTemplateGalleryEntry : ModuleGalleryEntry {
        override val name: String = "Your Library Name"
        override val description: String = "your library escription"
        override val icon: Icon = IconLoader.getIcon("/icons/your_icon.png", this::class.java.classLoader)

        // 重写 createStep
        override fun createStep(project: Project, moduleParent: String, projectSyncInvoker: ProjectSyncInvoker): SkippableWizardStep<*> {
            // 这里我是想创建一个类似 kmp plugin 的 module type
            return ConfigureKmpLibraryModuleStep(
                KmpModel(project, moduleParent, projectSyncInvoker), name)
        }
    }
}


class ConfigureKmpLibraryModuleStep(
    model: KmpModel,
    title: String,
) : ConfigureModuleStep<KmpModel>(  model,
        FormFactor.MOBILE,
        SdkVersionInfo.LOWEST_ACTIVE_API,
        title = title) {

    // UI 界面
    override fun createMainPanel(): DialogPanel =
        panel {
            row(contextLabel("Module name", AndroidBundle.message("android.wizard.module.help.name"))) {
                cell(moduleName).align(AlignX.FILL)
            }
            row("Package name") { cell(packageName).align(AlignX.FILL) }
        }.withBorder(empty(6))

    override fun onProceeding() {
        super.onProceeding()
        model.template.set(
            // 创建自己的 template
            // 需要继承 
        )
    }

    override fun getPreferredFocusComponent() = moduleName

}


class KmpModel(
    project: Project,
    moduleParent: String,
    projectSyncInvoker: ProjectSyncInvoker,
    name: String = "shared",
) :
    ModuleModel(
        name = name,
        commandName = "New Kotlin Multiplatform Library Module",
        isLibrary = true,
        _template = GradleAndroidModuleTemplate.createMultiplatformModuleTemplate(project, name),
        projectModelData = ExistingProjectModelData(project, projectSyncInvoker),
        moduleParent = moduleParent,
        wizardContext = AndroidStudioEvent.TemplatesUsage.TemplateComponent.WizardUiContext.NEW_MODULE,
    ) {

        override val renderer: MultiTemplateRenderer.TemplateRenderer =
        object : ModuleTemplateRenderer() {

            @WorkerThread
            override fun init() {
                super.init()
                moduleTemplateDataBuilder.apply {
                    commonSrcDir =
                        (template.get().paths as KotlinMultiplatformModulePathsImpl).getCommonSrcDirectory(
                            this@KmpModel.packageName.get()
                        )
                    iosSrcDir =
                        (template.get().paths as KotlinMultiplatformModulePathsImpl).getIosSrcDirectory(
                            this@KmpModel.packageName.get()
                        )
                }
            }

            // 这部分的调用没有详细去看,强行塞了个 ohos dir 进去
            override val recipe: Recipe
                get() = { td: TemplateData ->
                    val ohosSrcDir = (template.get().paths as KotlinMultiplatformModulePathsImpl).getOHOSSrcDirectory(
                        this@KmpModel.packageName.get()
                    )
                    generateCTModule(this, data = td as ModuleTemplateData, ohosSrcDir, useKts = true, generateManifest())
                }
        }

    }

总结

想给 Android Studio 添加 Module Type,按照 Intellij SDK 添加 Module Type 是不行的

对于新版本的 Android Studio,需要注册 moduleDescriptionProvider,并且参照 kmp plugin 来实现 moduleDescriptionProvider 对应逻辑

如果需要额外定制目录等等,则需要自己深入修改