前言:之前有篇文件讲的是模块化开发,一个项目整体结构可以这么设计,模块化开发中每个人的习惯都不一样,有的人习惯MVP,有的人习惯MVVM或者有其他的app架构,对整个app相对底层的抽象规范的时候,上层编写的类的总是基本一直的,大家都得遵循某一些行为规范。我个人认为,编写父类的目的一定不是为了实现类可以少写代码,而是去规范子类的实现,最理想的状态就是同行看了你的一个activity的整体编写,就能知道你其他的是怎么一个写法,这样的话阅读成本就会大大降低。因此,我们可以会在写子类实现的时候,在最初始的时候,我们会做很多一样的操作。这些操作又没有特别重要的价值,繁琐又是可以省略的一些操作。工欲善其事必先利其器,我们可以通过编写一个IDEA的插件来帮我们自动生成这些文件。
1.我自己是用MVP模式来进行app开发的。这里我就以mvp来举这个例子。
一个activity的编写
多个activity和多个fragment的编写
可以看到,我的mvp的搭建是需要四个类,在加上每一个activity和fragment都是需要一个界面xml的,实际上我每新建一个界面,我都需要创建一个包,包里面四个文件外加layout文件中一个xml文件的。为了编写一个界面,我的初始化工作如此之多,而且大多数格式基本都是固定的,只是包名,文件名字可能稍许有些不同而已。这时候我就考虑是否可以不要浪费时间在这些无意义的初始化工作上面,不会偷懒的程序员不是一个好的程序员。
2.目标
- 快速准确的生成这些文件。
- 直接可以继承编写好的基类,一些规范性的方法调用顺序直接写好在创建好的文件里面。
- 四个类之间的关系已经自动连接好了。
3.思路
- 生成代码的方式有很多种,最熟悉应该就是Android Studio的自动补全功能,还有我们自定义的快速代码块生成。这里大部分是针对单个文件,布局代码的生成,和我期望一次生成多个文件是没法实现的。
- 还有一种是通过注解的方式,在代码运行时,自动生成我们所需要的文件。例如ButterKnife。使用这种方式,我们需要Build一次才能生成文件,最重要的是,我们还需要完整的学习java运行时注解的一系列api,学习成本比较大。
- 最终思路,五个文件实际上除了名字上有些区别,内容大致是一样的,可以直接通过Java来进行文件的创建,首先我们需要五个文件的模版,然后通过字符替换来是最终文本达到每一个都有所不同。
4.准备工作
- Android Studio的插件是需要IDEA来编写的,所以我们首先需要下载一个IDEA
- 五个文件的文件的文件模版
5.步骤
5.1 新建插件工程
需要注意的选择的SDK,不是我们使用的JDK5.2 创建action
这里我们只需要创建一个action就可以了,下面三种的组件可以去查询IDEA插件开发官方文档。
创建对应的action后,IDEA会自动帮我们把这个注册到插件的配置文件中去
5.3 编写对话视图,这里我们编写一个dialog,这里和Android的dialog没什么区别,只不过一个使用的是Java的swing,一个是Android的组件,界面Android是xml,这里的dialog是form文件。
5.4 编写action逻辑,处理通过dialog和用户的交互
*5.5 打包发布
在当前工程下会出现jar这个就是我们所需要的插件。在不发布的情况下,直接放到Android Studio目录下,重启Android Studio。
6.MvpGenerateAction文件重要部分说明
* 拿到dialog交互中的数据,作者,基础包名,模块名字,文件名字,创建文件类型(activity or fragment)
private void init()
{
MyDialog myDialog = new MyDialog(new MyDialog.DialogCallBack()
{
@Override
public void ok(String author, String moduleName, String appName, String packageName, boolean isAc)
{
if (author == null || moduleName == null || appName == null || moduleName.length() == 0 || author.length() == 0 || appName.length() == 0)
{
Messages.showInfoMessage(project, "something is empty", "title");
return;
}
isActivity = isAc;
mAuthor = author;
mModuleName = moduleName;
mAppName = appName;
if (packageName != null || packageName.length() != 0)
{
mPackageName = packageName;
}
if (isActivity)
{
createActivityClassFiles();
} else
{
createFragmentClassFiles();
}
// createClassFiles();
Messages.showInfoMessage(project, "create mvp code success", "title");
}
});
myDialog.setVisible(true);
}
- 创建文件入口
private void createActivityClassFiles()
{
createClassFile(CodeType.Activity);
createClassFile(CodeType.ActivityContract);
createClassFile(CodeType.ActivityPresenter);
createClassFile(CodeType.ActivityMoudle);
}
private void createFragmentClassFiles()
{
createClassFile(CodeType.Fragment);
createClassFile(CodeType.FragmentContract);
createClassFile(CodeType.FragmentPresenter);
createClassFile(CodeType.FragmentMoudle);
}
- 生成文件的核心方法步骤都差不多,找到模版,读取文件,替换文件中的字符串,最后写入文件
private void createClassFile(CodeType codeType)
{
String fileName = "";
String content = "";
String appPath = getAppPath();
String xmlName = "";
String xmlContent = "";
String xmlPath = getXmlPath();
switch (codeType)
{
case Activity:
fileName = "TemplateActivity.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Activity.java");
xmlName = "TemplateXml.txt";
xmlContent = ReadTemplateFile(xmlName);
writeToFile(xmlContent, xmlPath, mAppName.toLowerCase() + "_activity" + humpToLine(mModuleName) + ".xml");
break;
case ActivityContract:
fileName = "TemplateActivityContract.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Contract.java");
break;
case ActivityMoudle:
fileName = "TemplateActivityMoudle.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Moudle.java");
break;
case ActivityPresenter:
fileName = "TemplateActivityPresenter.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Presenter.java");
break;
case Fragment:
fileName = "TemplateFragment.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Fragment.java");
xmlName = "TemplateXml.txt";
xmlContent = ReadTemplateFile(xmlName);
writeToFile(xmlContent, xmlPath, mAppName.toLowerCase() + "_fragment" + humpToLine(mModuleName) + ".xml");
break;
case FragmentContract:
fileName = "TemplateFragmentContract.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Contract.java");
break;
case FragmentMoudle:
fileName = "TemplateFragmentMoudle.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Moudle.java");
break;
case FragmentPresenter:
fileName = "TemplateFragmentPresenter.txt";
content = ReadTemplateFile(fileName);
content = dealTemplateContent(content);
writeToFile(content, appPath + mModuleName.toLowerCase(), mModuleName + "Presenter.java");
break;
}
}
7.总结
* 总的来说就是先把模版做出来,然后在通过文件的读写,把特定字符替换掉,最后写入到指定的目录,这样我们的文件就直接生成,不需要太多的东西。简单准确的初始化文件。
* 如果在IDEA调试的时候是正常的,放到Android Studio报空指针,可能是你的插件配置文件有问题,其中的版本可能需要换一个。
* IDEA插件开发官方文档
* 插件github地址,可以要对于自己当前的项目进行一些小修改,这个是适合我的项目。
* 不通过jar包的进行插件安装,也可以直接发布出去,发布方式指南
* 我的个人博客