随着公司的并行项目越来越多,Android新项目初始化的变成一个问题,一开始是每个人建立项目不一样,为此设计了清单列表。后来发现项目是一致了,但是每次都要手动执行列表,大部分都是简单地新建文件,很无聊,而且偶尔思想开个小差,还出错,又要找问题、修bug,烦~。
都说懒惰是程序员的优良品质,所以决定研究一下怎么自动化整个过程。
需求
经过分析,发现这里面最核心需求有两个:
- 根据模板java文件生成最终的java文件,主要是替换
类名
、包名
和app包名
; - 根据包名规则生成相应的文件夹。
其中涉及的流程有:
- 读取、解析配置文件;
- 根据名称,生成相应的文件夹;
- 根据模板文件,生成文件夹下相应的文件;
涉及的技术点有:
- 配置文件的格式用什么好?
- 怎么解析配置文件?
技术点1:配置文件的格式用什么好?
经过分析,考虑扩展性和简洁程序,决定json来写配置。
方案一:json和文件(夹)的层级结构保持一致。
- type:这一层级的类型:
-
dir
: 表示文件夹 -
file
: 表示文件
- 如果 type为dir,则需要定义:
-
name
:文件夹的名字 -
list
:子文件(夹)列表 -
package_name
:包名。默认为 父级文件夹名字和自身文件夹的名字的叠加。
- 如果 type为file,则需要定义:
-
name
:文件的名字 -
template
:模板文件的名字
这样一个配置两个文件的json如下:
{
"type":"dir",
"name":"baby",
"list":[
{
"type":"dir",
"name":"view",
"list":[
{
"type":"file",
"name":"BabyFragment",
"template":"BaseFragment.java.tpl"
},
{
"type":"file",
"name":"MorningCheckFragment",
"template":"BaseFragment.java.tpl"
}
]
}
]
}
才两个文件,就有三层嵌套,就算是增加同包文件,都要增加三行,太繁琐了。换一种思路:
方案二:根据模板、包名,把同一个包名下的同一个模板的文件汇总在一起
- 第一层:
-
app_id
: 应用包名 -
list
: 模板新建列表
- 第二层:模板新建列表项
-
package_name
: 对应的包名 -
template
: 采用的模板 -
fileList
: 需要新建的文件列表
这样json变成如下:
{
"app_id":"panda.android.test",
"list":[
{
"package_name":"panda.android.test",
"template":"BaseFragment.java.tpl",
"fileList":[
"BabyFragment",
"MorningCheckFragment"
]
}
]
}
简单直观很多。
怎么解析配置文件?
这里当然选择强大易用的Python了。
Python字符串替换的3种方法:
1. 字符串替换
将需要替换的内容使用格式化符替代,后续补上替换内容;
template = "hello %s , your website is %s " % ("简书","//www.greatytc.com/")
print(template)
也可使用format函数完成:
template = "hello {0} , your website is {1} ".format("简书","//www.greatytc.com/")
print(template)
2. 字符串命名格式化符替换
使用命名格式化符,这样,对于多个相同变量的引用,在后续替换只用申明一次即可;
template = "hello %(name)s ,your name is %(name), your website is %(message)s" %{"name":"简书","message":"//www.greatytc.com/"}
print(template)
使用format函数的语法方式:
template = "hello {name} , your name is {name}, your website is {message} ".format(name="大CC",message="http://blog.me115.com")
print(template)
3. 模版方法替换
使用string中的Template方法;
from string import Template
tempTemplate = string.Template("Hello $name ,your website is $message")
print(tempTemplate.substitute(name='大CC',message='http://blog.me115.com'))
有了模版方法后,就可以将模版保存到文件单独编辑,在生成的地方替换为需要的变量。
样例代码
- BaseFragment.java.tpl如下:
package ${PACKAGE_NAME};
import ${ANDROID_APP_ID}.R;
import panda.android.lib.base.ui.fragment.BaseFragment;
/**
* Created on ${DATA}.
*/
public class ${CLASS_NAME} extends BaseFragment{
@Override
public int getLayoutId() {
return R.layout.fragment_net;
}
}
- 配置文件如下:
{
"app_id":"panda.android.test",
"list":[
{
"package_name":"panda.android.test",
"template":"BaseFragment.java.tpl",
"fileList":[
"BabyFragment",
"MorningCheckFragment"
]
}
]
}
- 脚本如下:
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import os,sys,re,traceback
from datetime import datetime
from string import Template
import json
def generateAndroidJavaCode(app_id, package_name, template, fileName):
className = fileName;
path = "./" + package_name.replace('.','/');
filePath = r'%s/%s.java' % (path,className)
if os.path.exists(path) == False:
os.makedirs(path)
class_file = open(filePath,'w')
lines = []
#模版文件
template_file = open(r'BaseFragment.java.tpl','r')
tmpl = Template(template_file.read())
#模版替换
lines.append(tmpl.substitute(
PACKAGE_NAME = package_name,
ANDROID_APP_ID = app_id,
DATA = datetime.now().strftime('%Y-%m-%d %H:%M:%S'),
CLASS_NAME = className))
# 0.将生成的代码写入文件
class_file.writelines(lines)
class_file.close()
print 'generate %s over. ~ ~' % filePath
## 1. 解析配置文件
configuration = """
{
"app_id":"panda.android.test",
"list":[
{
"package_name":"panda.android.test",
"template":"BaseFragment.java.tpl",
"fileList":[
"BabyFragment",
"MorningCheckFragment"
]
}
]
}
"""
configurationJson = json.loads(configuration);
## 2. 按照模板生成对应的文件
app_id = configurationJson["app_id"]
for filesInfo in configurationJson["list"]:
package_name = filesInfo["package_name"]
template = filesInfo["template"]
for fileName in filesInfo["fileList"]:
generateAndroidJavaCode(app_id, package_name, template, fileName);
-
最后生成的文件如下:
引用
Panda
2016-07-04