Skip to content

Instantly share code, notes, and snippets.

@aJIEw
Last active September 28, 2017 06:56
Show Gist options
  • Save aJIEw/4c666af6c4f9b5fb8f9786e236c76073 to your computer and use it in GitHub Desktop.
Save aJIEw/4c666af6c4f9b5fb8f9786e236c76073 to your computer and use it in GitHub Desktop.
/**
* 有时候为了直接跑别人的Android Studio项目而不去下载gradle或者SDK Platform,
* 常常需要手动修改 build.gradle文件和 gradle-wrapper.properties文件,
* 当有很多个项目的时候,这种工作就很难忍受了(比如当你要跑很多Sample的时候),
* 所以写了这个工具类(使用正则表达式)来批量修改同一个文件夹下的多个项目。
* <p>
* 调用 {@link #setupOneLevelProjects(String)} 修改只有单层的项目文件夹,或者
* {@link #setupTwoLevelProjects(String, String, String[])} 修改具有两层目录的项目文件夹。
* <p>
* 默认匹配的 module 为 app,可以在{@link #DEFAULT_MODULES}中继续添加。
* <p>
* <p>
* Tips:使用前请先复制一个旧项目试验一下
*/
@SuppressWarnings({"ConstantConditions", "WeakerAccess"})
public class ChangeGradleFiles {
public static void main(String[] args) {
ChangeGradleFiles change = new ChangeGradleFiles();
// 修改只有一层的文件夹,即该目录下就是所有的 Android Studio 项目
String path = "C:\\WorkSpace\\AndroidProject\\Library";
change.setupOneLevelProjects(path);
// 修改具有两层的文件夹,即第一层是目录,第二层才是项目,后两个参数可以为空
//change.setupTwoLevelProjects(path, null, null);
// 修改该文件夹下所有以数字开头的项目文件夹,并跳过“14_”开头的文件夹
/*
path = "D:\\WorkSpace\\AndroidProject\\FirstLineOfCodes";
String filterRule = "\\d+_.+";
String[] skipDirs = {"14_.+"};
change.setupTwoLevelProjects(path, filterRule, skipDirs);
*/
}
/**
* 默认匹配的module名,在{@link #modifyModuleGradle(File)}方法中遍历并修改匹配上的module
*/
private static final String[] DEFAULT_MODULES = {
"app",
"Application",
"lib",
"library",
"sample",
"demo",
};
/**
* gradle wrapper版本
*/
private static final String NEW_GRADLE_WRAPPER_VERSION = "distributionUrl=https\\://services.gradle.org/distributions/gradle-3.3-all.zip";
/**
* 需要用正则表达式匹配的内容
*/
private static final String MATCH_GRADLE_PLUGIN_VERSION = "\\s+classpath 'com.android.tools.build:gradle.+";
private static final String MATCH_BUILD_TOOLS_VERSION = "\\s+buildToolsVersion.+";
private static final String MATCH_COMPILE_SDK_VERSION = "\\s+compileSdkVersion.+";
private static final String MATCH_TARGET_SDK_VERSION = "\\s+targetSdkVersion.+";
/**
* 匹配上后用于替换的新内容
* <i>为简单起见,直接复制一整行的内容<i/>
*/
private static final String NEW_GRADLE_PLUGIN_VERSION = " classpath 'com.android.tools.build:gradle:2.3.3'";
private static final String NEW_BUILD_TOOLS_VERSION = " buildToolsVersion \"25.0.3\"";
private static final String NEW_COMPILE_SDK_VERSION = " compileSdkVersion 25";
private static final String NEW_TARGET_SDK_VERSION = " targetSdkVersion 25";
/**
* ----💥重要内容💥----<BR />
* 匹配的数组和用于替换的新内容的数组,这两个数组长度、添加顺序必须一致
* --------------------
*/
private static String[] sMatchContent, sNewContent;
static {
sMatchContent = new String[]{
MATCH_BUILD_TOOLS_VERSION,
MATCH_COMPILE_SDK_VERSION,
MATCH_TARGET_SDK_VERSION,
};
sNewContent = new String[]{
NEW_BUILD_TOOLS_VERSION,
NEW_COMPILE_SDK_VERSION,
NEW_TARGET_SDK_VERSION,
};
}
private String mMatchModulesRegex;
private List<String> mMatchModules = new ArrayList<>();
private Map<String, String> mWrapperMap = new TreeMap<>();
private Map<String, String> mProjectGradleMap = new TreeMap<>();
private Map<String, String> mModuleGradleMap = new TreeMap<>();
/**
* 初始化待匹配的moduel名,待匹配的内容和用于替换的内容
*/
public ChangeGradleFiles() {
mMatchModules.addAll(Arrays.asList(DEFAULT_MODULES));
mWrapperMap.put("distributionUrl.+", NEW_GRADLE_WRAPPER_VERSION);
mProjectGradleMap.put(MATCH_GRADLE_PLUGIN_VERSION, NEW_GRADLE_PLUGIN_VERSION);
for (int i = 0; i < sMatchContent.length; i++) {
mModuleGradleMap.put(sMatchContent[i], sNewContent[i]);
}
}
/**
* 添加待匹配的module名,也可以直接在{@link #DEFAULT_MODULES}中继续添加
*/
public void addMatchModules(String... modules) {
mMatchModules.addAll(Arrays.asList(modules));
}
/**
* 根据正则匹配module
* */
public void setMatchModulesRegex(String matchModulesRegex) {
mMatchModulesRegex = matchModulesRegex;
}
/**
* 修改只有一层的文件夹,即路径所在的目录下就是所有的Android项目
*/
public void setupOneLevelProjects(String path) {
File dir = new File(path);
if (dir.isDirectory()) setup(dir);
}
/**
* 修改有两层的项目,即路径下第一层还是目录,第二层才是Android项目
* 对于第一层的目录可以添加过滤规则,前提是你的目录的命名是有规则的,比如都以数字开头
* 另外还可以利用第三个参数跳过某些文件夹,注意也是针对第一层的目录的
*
* @param path the directory of projects
* @param filterRule add filter rule to your first level directory, support regex
* @param skipDirs add directories you need to skip, support regex
*/
public void setupTwoLevelProjects(String path, String filterRule, String[] skipDirs) {
for (File projectParentDir : new File(path).listFiles()) {
String rule = filterRule == null ? ".+" : filterRule;
if (projectParentDir.isDirectory() && projectParentDir.getName().matches(rule)) {
if (skipDirs == null || skipDirs.length == 0) {
setup(projectParentDir);
} else {
for (String projectName : skipDirs) {
if (projectName != null && projectParentDir.getName().matches(projectName)) {
System.out.println("Skipped " + projectName);
break;
} else {
setup(projectParentDir);
}
}
}
}
}
}
/**
* 遍历当前目录下所有安卓项目(默认所有文件夹都是安卓项目)并修改其中的文件
*
* @param projectParentDir The parent directory of your android projects
*/
protected void setup(File projectParentDir) {
for (File projectDir : projectParentDir.listFiles()) {
if (projectDir.isDirectory()) {
for (File rootFile : projectDir.listFiles()) {
modifyWrapper(rootFile);
modifyProjectGradle(rootFile);
modifyModuleGradle(rootFile);
}
}
}
}
/**
* 修改gradle文件夹下的gradle-wrapper.properties
*/
private void modifyWrapper(File projectFile) {
if (projectFile.isDirectory() && projectFile.getName().matches("gradle")) {
File gradleWrapper = new File(
projectFile.getAbsolutePath() + "\\wrapper\\gradle-wrapper.properties");
modifyFileAccord(gradleWrapper, mWrapperMap);
}
}
/**
* 修改项目根目录下的 gradle.build 文件
*/
private void modifyProjectGradle(File projectFile) {
if (projectFile.isFile() && projectFile.getName().matches("build\\.gradle")) {
modifyFileAccord(projectFile, mProjectGradleMap);
}
}
/**
* 修改module下的gradle.build文件
*/
private void modifyModuleGradle(File projectFile) {
String projectDir = projectFile.getParent();
String moduleDir = projectFile.getName();
/*
* 从github上下载下来的开源库的名字格式为:项目名-master
* 而很多库的module名都与项目名相同,所以首先先匹配这些module
* */
int hyphenIndex = projectDir.indexOf("-");
if (hyphenIndex != -1) {
String projectName = projectDir.substring(0, hyphenIndex);
if (projectFile.isDirectory() && moduleDir.equalsIgnoreCase(projectName)) {
// modify the matched module's build.gradle
File moduleGradle = new File(projectFile.getAbsolutePath() + "\\build.gradle");
modifyFileAccord(moduleGradle, mModuleGradleMap);
}
}
// 匹配之前定义的module名
if (projectFile.isDirectory()) {
for (String matchModule : mMatchModules) {
if (moduleDir.matches(matchModule)) {
File moduleGradle = new File(projectFile.getAbsolutePath() + "\\build.gradle");
modifyFileAccord(moduleGradle, mModuleGradleMap);
}
}
}
if (projectFile.isDirectory() && mMatchModulesRegex != null) {
if (moduleDir.matches(mMatchModulesRegex)) {
File moduleGradle = new File(projectFile.getAbsolutePath() + "\\build.gradle");
modifyFileAccord(moduleGradle, mModuleGradleMap);
}
}
}
/**
* 根据传入的Map修改文件,map中的key为待匹配的内容,value为用于替换的内容
*
* @param file The file you need to change. Key is for the content you need to match,
* the value is for the new content to replace the matched content.
* @param matchReplaceMap the mWrapperMap contains regular expression as key and new content as values
*/
private void modifyFileAccord(File file, Map<String, String> matchReplaceMap) {
if (!file.exists()) {
System.out.println("-------------" + file + " does not exists!----------");
return;
}
StringBuilder result = new StringBuilder();
BufferedReader gradleFile = null;
int replacedNum = 0;
try {
gradleFile = new BufferedReader(new FileReader(file));
String[] regexs = convertToArray(
matchReplaceMap.keySet().iterator(),
matchReplaceMap.keySet().size());
String[] replaces = convertToArray(
matchReplaceMap.values().iterator(),
matchReplaceMap.values().size());
boolean changed = false;
String line;
while ((line = gradleFile.readLine()) != null) {
for (int i = 0; i < regexs.length; i++) {
if (line.matches(regexs[i])) {
changed = true;
result.append(replaces[i]).append(System.getProperty("line.separator"));
replacedNum++;
break;
}
}
if (!changed) result.append(line).append(System.getProperty("line.separator"));
changed = false;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
close(gradleFile);
}
System.out.println("Replaced " + replacedNum + " line(s) in the " + file.getAbsolutePath());
writeIntoFile(file, result.toString());
}
private String[] convertToArray(Iterator<String> itr, int size) {
String[] result = new String[size];
for (int i = 0; itr.hasNext(); i++) {
result[i] = itr.next();
}
return result;
}
private void writeIntoFile(File file, String content) {
BufferedWriter bufferedWriter = null;
try {
bufferedWriter = new BufferedWriter(new FileWriter(file));
bufferedWriter.write(content);
} catch (IOException e) {
e.printStackTrace();
} finally {
close(bufferedWriter);
}
}
private static void close(Closeable file) {
try {
if (file != null) {
file.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment