项目源码

系列教程

插件介绍

本篇实战撸个自动生成安卓Sqlite数据库代码的插件,先演示下最终效果

db文件夹下的都是插件自动生成的,而MainActivity里面的代码是我提前写好的,用于实验插件生成的代码效果

DatabaseGenerator.gif

简单解释下插件功能

给定一个数据类,比如User。希望插件能根据数据类自动生成对应的表结构,存在一个Column类里。然后再生成对应的Dao类其中包含CRUD方法。

和网上常见的一些数据库框架类似,只不过这里是用插件直接生成Android Sqlite原生代码

优点:

无需额外依赖

无学习成本

便于自定义

缺点:

原生代码量较多

需要对安卓Sqlite原生代码有一定了解

开撸~

需要处理这么几个模块

SqliteOpenHelper类,其中包含create table的sql语句;

Columns字段类,统一存在一个DataContract类中;

数据Dao类,包含CRUD的sql语句

几个模块的处理步骤和逻辑都类似,这里拿Columns类生成举例。

其他可以下载源码参考 ,源码地址:

https://github.com/boredream/AndroidDatabaseGeneratorPlugin

欢迎star和follow~

下载源码后参考教程一先搭建环境,然后导入项目

处理步骤如下:

一、定位到需要创建文件的目录

这里希望把生成的类都存在包名目录下的db包中(com.packagename.db)

首先要获取到包名目录路径...app/src/main/java/包名,然后才能在它下面获取或新建db文件夹。而获取包名目录又要先获取Android项目的包名,想获取这个又得先找到AndroidManifest文件~

AndroidManifest.png

因为AndroidManifest文件路径是固定的,所以可以用上一篇教程中的LocalFileSystem.getInstance().findFileByPath(path);方法获取文件

public static PsiFile getManifestFile(Project project) {

String path = project.getBasePath() + File.separator +

"app" + File.separator +

"src" + File.separator +

"main" + File.separator +

"AndroidManifest.xml";

VirtualFile virtualFile = LocalFileSystem.getInstance().findFileByPath(path);

if(virtualFile == null) return null;

return PsiManager.getInstance(project).findFile(virtualFile);

}

project可以在Action中通过event.getData()获取,参考上一篇教程

获取到VirtualFile后再转换成PsiFile,大部分操作都是针对Psi体系的

然后解析AndroidManifest文件,获取package属性里的包名

因为是Xml文件,所以和Dom啥的解析差不多,获取代码如下

public static String getAppPackageName(Project project) {

PsiFile manifestFile = getManifestFile(project);

XmlDocument xml = (XmlDocument) manifestFile.getFirstChild();

return xml.getRootTag().getAttribute("package").getValue();

}

然后就可以根据包名获取到包名目录了

public static VirtualFile getAppPackageBaseDir(Project project) {

String path = project.getBasePath() + File.separator +

"app" + File.separator +

"src" + File.separator +

"main" + File.separator +

"java" + File.separator +

getAppPackageName(project).replace(".", File.separator);

return LocalFileSystem.getInstance().findFileByPath(path);

}

project.getBasePath()是项目的根目录,在其基础上拼接后续路径

然后,包名目录下断有没有db文件夹,没有就创建一个

// app包名根目录 ...\app\src\main\java\PACKAGE_NAME\

VirtualFile baseDir = AndroidUtils.getAppPackageBaseDir(project);

// 判断根目录下是否有db文件夹

VirtualFile dbDir = baseDir.findChild("db");

if(dbDir == null) {

// 没有就创建一个

try {

dbDir = baseDir.createChildDirectory(null, "db");

} catch (IOException e) {

e.printStackTrace();

}

}

这次我们用了 VirtualFile.FindChild(filename) 方法,获取文件子一级路径中寻找文件或文件夹

(LocalFileSystem.getInstance().findFileByPath(path); 只能获取文件不能获取目录,所以不用)

没有db文件夹的话,就 VirtualFile.createChildDirectory(requestor, name) 创建一个

这个方法第一个参数是指定谁调用了它,一般传null不做特殊处理

db目录定位到了,然后就是在里面创建DataContract类了,再在其中存放Columns类。

DataContact.java文件其实也可以通过类似上面的 VirtualFile.createChildData 直接创建文件,

但创建的是空的文件,而我们需要的是有代码内容的java文件,所以下面我们介绍另一个方法~

二、创建一个包含代码的文件

按照我们的插件需求,要创建一个DataContract类,然后把Columns类都存进去。

首先就是要生成这个作为壳子的类~ 我们先拼接出来类文件的字符串,代码如下

public static String genDataContractInitCode(VirtualFile dir) {

return StringUtils.formatSingleLine(0, "package " + AndroidUtils.getFilePackagePath(dir) + ";") +

"\n" +

StringUtils.formatSingleLine(0, "import android.provider.BaseColumns;") +

"\n" +

StringUtils.formatSingleLine(0, "public final class DataContract {") +

"\n" +

StringUtils.formatSingleLine(1, "private DataContract() {") +

StringUtils.formatSingleLine(2, "// private") +

StringUtils.formatSingleLine(1, "}") +

"\n" +

"}";

}

其中getFilePackagePath是获取当前文件/文件夹对应包名 com.xxx.xxx 的,

逻辑是把当前文件路径的 / 替换成 . 然后截取com.xxx.xxx以后的部分即可

public static String getFilePackageName(VirtualFile dir) {

if(!dir.isDirectory()) {

// 非目录的取所在文件夹路径

dir = dir.getParent();

}

String path = dir.getPath().replace("/", ".");

String preText = "src.main.java";

int preIndex = path.indexOf(preText) + preText.length() + 1;

path = path.substring(preIndex);

return path;

}

获取到代码字符串以后,可以用createFileFromText创建有内容的文件,如下

String name = "DataContract.java";

VirtualFile virtualFile = dbDir.findChild(name);

if(virtualFile == null) {

// 没有就创建一个,第一次使用代码字符串创建个类

PsiFile initFile = PsiFileFactory.getInstance(project).createFileFromText(

name, JavaFileType.INSTANCE, CodeFactory.genDataContractInitCode(dbDir));

// 加到db目录下

PsiManager.getInstance(project).findDirectory(dbDir).add(initFile);

virtualFile = dbDir.findChild(name);

}

dbDir是步骤一中获取到的db文件夹

genDataContractInitCode是上面拼接代码的方法,返回代码字符串

注意,createFileFromText创建的文件是一个无目录的文件,需要手动add到需要位置

这个add操作就会把文件加到指定目录下,新建一个文件~

三、解析数据生成对应Columns类

上一篇介绍过,我们可以用action中的event获取当前正在编辑的文件,然后在file中获取到PsiClass元素,最后遍历Class获取全部成员变量Field。PsiClass和Java中的Class相似,有一点反射姿势的可以很快上手

下面就是根据数据类信息,拼接代码字符串的方法

public static String genBeanColumnsCode(PsiClass clazz) {

StringBuilder sb = new StringBuilder();

sb.append(StringUtils.formatSingleLine(0, "public interface " + clazz.getName() + " extends BaseColumns {"));

sb.append(StringUtils.formatSingleLine(1, "String TABLE_NAME = \"" + StringUtils.camel2underline(clazz.getName()) + "\";"));

for (PsiField field : clazz.getFields()) {

String name = StringUtils.camel2underline(field.getName()).toUpperCase();

String value = name.toLowerCase();

sb.append(StringUtils.formatSingleLine(1, "String " + name + " = \"" + value + "\";"));

}

sb.append("}");

return sb.toString().trim();

}

clazz.getField获取到类的所有成员变量,然后拼接成需要的代码

其中的StringUtils是自己封装的工具类

camel2underline是将驼峰命名转换成下划线风格的字符串

formatStringLine是在前面加缩进符,后面加换行符

拼好代码后,就可以用它去生成类、文件、方法等等

和之前生成文件类似,也是 createXXXFromText一类的方法,

可以用代码生成类、方法、语句、变量等等。

这里我们就要根据代码去生成Columns类,也就是PsiClass对象

上一步我们已经获取到了DataContract类了,新建的Columns要保存在它里面

PsiFile psiFile = PsiManager.getInstance(project).findFile(virtualFile);

// 用拼接的代码生成Columns Class

String beanColumnsCode = CodeFactory.genBeanColumnsCode(clazz);

PsiElementFactory factory = JavaPsiFacade.getInstance(project).getElementFactory();

PsiClass beanColumnsClass = factory.createClassFromText(beanColumnsCode, psiFile);

// 将创建的class添加到DataContract Class中

PsiClass fileClass = PluginUtils.getFileClass(psiFile);

fileClass.add(beanColumnsClass.getInnerClasses()[0]);

这次用到了 PsiElementFactory 类,然后用它去 **createClassFromText **创建类

方法的第二个参数是Context,传入所在File或所在Class都可以

然后将这个生成的类添加到DataContract文件类中

注意不能是添加到DataContract文件上,而是添加到文件里的类上

获取方法如下(应该有更好的方法吧,暂时没找到)

public static PsiClass getFileClass(PsiFile file) {

for (PsiElement psiElement : file.getChildren()) {

if (psiElement instanceof PsiClass) {

return (PsiClass) psiElement;

}

}

return null;

}

class.getInnerClasses()[0] 这里要单独说明下

createFileFromText的时候我们拼接的字符串是完整的代码,

但是在createClassFromText的时候比较特殊,codeText是作为类主体部分的

String classCode = "public class MyClass {\n" +

"\tprivate String a;\n" +

"}";

PsiClass newClass = factory.createClassFromText(classCode, null);

如果你这样去生成,那么最终代码会是

class _Dummy_ {

public class MyClass {

private String a;

}

}

这不是我们想要的!!!

所以一般做法是只用类 { } 里面的代码去生成,比如"private String a;"

而类的public等信息需要额外设置,如下

newClass.setName("User"); // 设置类名,默认名为_Dummy_

newClass.getModifierList().add(factory.createKeyword(PsiKeyword.PUBLIC)); // 定义列表里添加关键字public

newClass.getImplementsList().add(factory.createReferenceElementByFQClassName(

"android.provider.BaseColumns", clazz.getResolveScope())); // 实现接口列表里添加BaseColumns类

这就比较麻烦了,所以介绍个良心小技巧!

还是用全部代码生成,然后再获取这个类的innerClasses内部类里面的第一个就行了!

所以才有了上面的 class.getInnerClasses()[0] 的处理

四、整合代码,运行

将之前的代码封装到DatabaseGenerator类中的genCode方法中,然后在action里调用

action的相关介绍参考教程一

public class DatabaseGenerateAction extends AnAction {

@Override

public void actionPerformed(AnActionEvent e) {

Project project = e.getData(PlatformDataKeys.PROJECT);

PsiFile file = e.getData(PlatformDataKeys.PSI_FILE);

PsiClass clazz = PluginUtils.getFileClass(file);

WriteCommandAction.runWriteCommandAction(project, () -> {

DatabaseGenerator.genCode(file, clazz);

});

}

}

注意,这里有个特殊的处理 WriteCommandAction.runWriteCommandAction

在插件中,如果是新建File等操作是可以直接进行的。

但在DataContract文件类中添加个内部类,这种写入文件内容的操作是需要特殊处理的,需要放在 WriteCommandAction.runWriteCommandAction 第二个参数的runnable中运行

搞定,效果图见文章开始动态图。

源码部分见

https://github.com/boredream/AndroidDatabaseGeneratorPlugin

欢迎star和follow~

下载源码后参考教程一先搭建环境,然后导入项目

android studio 自动生成sql语句,Android Studio Plugin 插件开发教程(三) —— 制作一个自动生成数据库代码的插件...相关推荐

  1. 解放程序员双手!GPT-3自动生成SQL语句 | 代码开源

    金磊 发自 凹非寺 量子位 报道 | 公众号 QbitAI "无所不能"的GPT-3,现在又来解放程序员们的双手了. 像这样,只需用简单的英文问下GPT-3"上个月注册了 ...

  2. Bootstrap4+MySQL前后端综合实训-Day04-PM【PowerDesigner 图形化数据库设计软件(设置依赖关系、自动增长主键、生成sql语句)、SQLyog软件(备份数据库)】

    [Bootstrap4前端框架+MySQL数据库]前后端综合实训[10天课程 博客汇总表 详细笔记] 目   录 PowerDesigner软件 5张数据表之间的依赖关系图 设置数据表的自动增长主键 ...

  3. 用excel自动生成sql语句

    在空白列插入函数 =CONCATENATE("insert into user(code, name) values('",A1, "','", B1, &qu ...

  4. NO2:自动生成sql语句

    SQL语句自动生成工具 大哉乾元 2016/2/26   作者原创转载请注明出处 前言 这个程序是几年前做成的,现在整理成文档和大家分享,当时参与的项目中大量使用的sql语句,所以SqL语句的代码输入 ...

  5. java自动生成sql语句

    java自动生成sql语句 艳学网强势来袭http://47.98.237.162/index,首次发布艳辉工具,第一个工具我们发布sql语句自动生成. 以前首次接触sql是在大学期间,刚开始是手写s ...

  6. tp5循环查询语句_如何用Excel快速生成SQL语句,用过的人都说好

    Excel的公式自动生成想必大家都知道了,就是写好一个公式后直接往下拖,就可以将后面数据的公式自动生成. 今天我们就用这个功能来快速生成SQL语句. 导入Excel数据 Excel的数据有多种方式,这 ...

  7. EntityFramework Core自动返回SQL语句

    [导读]给各位拜年了,开年第一篇,后续我们介绍EF Core 5.0相关新特性 自动返回SQL语句 当执行LINQ查询时,EF Core 5.0提供了ToQueryString扩展方法返回生成的SQL ...

  8. 如何用Excel快速生成SQL语句,用过的人都说好

    导读:Excel的公式自动生成想必大家都知道了,就是写好一个公式后直接往下拖,就可以将后面数据的公式自动生成. 今天我们就用这个功能来快速生成SQL语句. 作者:丶平凡世界 来源:SQL数据库开发(I ...

  9. Excel表格生成sql语句

    假如excel表格中有A.B.C三列数据,希望导入到数据库users表中,对应的字段分别是name,sex,age ,在你的excel表格中增加一列,利用excel的公式自动生成sql语句,方法如下: ...

最新文章

  1. 可逼近信道容量编码技术之霍夫曼编码的实现
  2. TensorFlow1.8.0正式发布,Bug修复和改进内容都在这里了
  3. linux文件统计命令,linux文件统计命令和目录统计命令
  4. 类型重定义 头文件预编译设置
  5. 5月20日发!索尼Xperia 1国行版官宣:骁龙855+4K“带鱼”屏
  6. 【es】es 分布式一致性原理剖析 节点篇
  7. 千氪|比特币十周年大事记
  8. 【JAVA基础知识总结】JAVA对象转型之上转型对象与下转型对象
  9. 核心频率个加速频率_AMD 32核心频率飞起!Intel
  10. html 打印 a4 像素,精确打印A4纸张排版HTML
  11. 常用媒体查询以及手机横竖屏监听
  12. 什么是股票量化交易软件?
  13. run `npm fund` for details
  14. 开源 Python IDE PyScripter设置中文界面
  15. linux服务器防御ddos,linux如何防御ddos
  16. 小程序 function(res)与(res) =的区别
  17. 银河麒麟加完全自主的龙芯指令集,组合渡劫能否成功
  18. dfs python
  19. Linux用户、用户组的管理
  20. 数据库手工注入中的闭合

热门文章

  1. 架构模式: 服务器端页面碎片化元素构建
  2. 前端如何展示商品属性:SKU多维属性状态判断算法的应用-Vue 实现
  3. 【全网最全的博客美化系列教程】08.自定义地址栏Logo
  4. Java标识符和关键字(static,final,abstract,interface)
  5. hdu-1088 Write a simple HTML Browser
  6. .NET 内存管理与垃圾回收:实现IDisposable接口和析构函数
  7. 演示FilterConfig接口的getInitParameter(String name)方法
  8. java面向对象编程基础实验报告_20155313 实验三《Java面向对象程序设计》实验报告...
  9. linux mysql清除缓存_转载-清除Linux中MySQL的使用痕迹~/.mysql_history
  10. 创建程序集时元数据失败 -- 拒绝访问_Veeam 云原生数据管理解决方案 Kasten K10 介绍...