ButterKnife虽然帮助我们省去了繁琐的findViewById(),使用起来非常的快捷方便,但是有利必然也有弊,ButterKnife在编译期间会额外生成class文件,对App的方法数增加贡献不小,并且在library module中使用ButterKnife有着诸多不便,因此项目优化弃坑ButterKnife势在必行。
(1)解析@BindView注解得到view id,并在ButterKnife.bind调用处生成findViewById,从而达到替换效果。
(2)支持指定目录参数,遍历目录,递归替换,脚本运行完后需要手动清除imports。
(3)暂不支持替换@OnClick注解。
(4)console输出文件完成状态。
sky:~ sky$ python replace_butterknife.py DiscountStartShowController.java
#!/usr/bin/env python
# by sky
import os, sys, re
butter_knife_egx = re.compile('.*@BindView\(R[2]?.id.(.*)\)')
view_define_egx = re.compile('\s*(.*)\s+(.*);')
butter_bind_egx = re.compile('\s*ButterKnife.bind\([^,]+[,]?\s*(.*)\);')
def convert(file):
butterknifeline = False
view_id = None
list = file.readlines()
new_list = []
findview_lines = []
for index in range(len(list)):
line = list[index]
if butterknifeline:
result = view_define_egx.match(line)
if result:
# private TextView tvTitle
view_define_line = '\t' + 'private ' + result.group(1) + ' ' + result.group(2) + ';\n'
# tvTitle = findViewById(R.id.tv_title)
new_line = '\t' + result.group(2) + ' = '
if result.group(1) != 'View':
new_line += '(' + result.group(1) + ')'
new_line += 'findViewById(R.id.' + view_id +');\n'
findview_lines.append(new_line)
new_list.append(view_define_line)
butterknifeline = False
else:
new_list.append(line)
else:
m = butter_knife_egx.match(line)
if m:
view_id = m.group(1)
butterknifeline = True
elif len(findview_lines) > 0:
m = butter_bind_egx.match(line)
if m:
parent_view = m.group(1)
create_findviewbyid(new_list, findview_lines, parent_view)
findview_lines = []
else:
new_list.append(line)
else:
new_list.append(line)
file.seek(0)
file.writelines(new_list)
# tvTitle = view.findViewById(R.id.tv_title)
def create_findviewbyid(new_list, findview_lines, parent_view):
for line in findview_lines:
if parent_view:
line = line.replace('findViewById', parent_view + '.findViewById')
new_list.append(line)
# recurse replace butter knife
def convert_recurse(path_name):
if os.path.isfile(path_name):
if not path_name.endswith('.java'):
return
with open(path_name, 'r+') as f:
convert(f)
print path_name + ' is done'
elif os.path.isdir(path_name):
for file_name in os.listdir(path_name):
convert_recurse(path_name + '/' + file_name)
else:
print path_name + ' is invalid'
if __name__ == "__main__":
if len(sys.argv) < 2:
print 'args error'
quit()
path_name = sys.argv[1]
convert_recurse(path_name)