Android打造专有Hook第四篇,实战增量代码规范检查

简介: 在全量文件检查中,我们只需要得到Git提交的文件,然后逐一针对文件内容,获取,做相关的逻辑检查即可,但是增量就不能这样搞了,我们都知道,每次增量的提交,是没有规律可言的,也许增量中只有一行,也许有百行,而且增量的代码位置,有可能是在方法中,有可能是在资源中,所以针对增量代码的检查,是必须要做出取舍的,因为,通过一行,或者几行,很难达到一定的规范标准

Hello啊各位老铁,上篇的文章,我们把全量代码规范检查做了一个整体的实现,唯独缺少了关于增量代码的检查,今天,这篇我们就重点把增量这块,做一个简单的分析。


在全量文件检查中,我们只需要得到Git提交的文件,然后逐一针对文件内容,获取,做相关的逻辑检查即可,但是增量就不能这样搞了,我们都知道,每次增量的提交,是没有规律可言的,也许增量中只有一行,也许有百行,而且增量的代码位置,有可能是在方法中,有可能是在资源中,所以针对增量代码的检查,是必须要做出取舍的,因为,通过一行,或者几行,很难达到一定的规范标准,OK,废话不多说,我们开搞!


今天的内容大概如下


1、增量代码简单说明

2、增量中如何获得代码

3、增量代码规范检查

4、补充说明


一、增量代码简单说明


前边说过,增量代码相对于全量代码有着巨大的差异,在增量代码中,所提交的更改代码,有可能是没有包含我们所制定的规范的,比如下面的这段提交的增量代码:


+valstrings: List<String>=token.split(".")
+if (strings.size!=3) {
+LogUtil.d("Incorrect token, strings size = ${strings.size}")
+returndefault+                }
+


从上边的增量代码中,你能判断什么?方法命名规范?方法注释规范,变量命名规范?还是其他的规范,根据我们的规范标准,其实啥也判断不了,所以,在增量代码的提交中,我们需要着重和我们的规范标准进行相匹配,既然匹配,那么我们就不得不做出一些取舍,也就是说,有些能规范检查的,我们进行检查,不能做检查的,我们只能舍弃。


二、增量中如何获得代码


增量代码检查的前提,必须得到当前所提交的增量代码,如何获取到增量代码呢?Git中给我们提供了相关命令,如下,通过下述的命令,我们就可以得到当前commit的所有提交文件的增量代码,并生成我们指定的对应文件。


gitdiff>>androidCommit.diff


需要注意的是,增量代码的生成,是生成到一个文件下,而且必须是在命令行的情况下才会生效,所以啊,各位老铁,使用增量代码检查时,一定要注意这个,并且在暴露给开发者的时候,在配置文件里也一定要标注。


当命令执行后,所生成的增量内容大致如下(检查的代码都是用于测试的,大家只关注增量提交的信息即可):


diff--gita/app/src/HomeActivity.ktb/app/src/HomeActivity.ktindex29ef3c5..0cdc619100644---a/app/src/HomeActivity.kt+++b/app/src/HomeActivity.kt@@-701,7+701,8@@classHomeActivity : DataBindingBaseActivity<ActivityHomeBinding, HomeViewModel>     }
-overridefunonResume() {
++overridefunonResume() {
super.onResume()
mActivityStatus=ACTIVITY_ON_RESUME     }
@@-727,7+728,9@@classHomeActivity : DataBindingBaseActivity<ActivityHomeBinding, HomeViewModel>      }
-funon_sHttp(){
+++funon_sHttp(){
      }
diff--gita/app/src/HomeViewModel.ktb/app/src/HomeViewModel.ktindex477da32..c43897e100644---a/app/src/HomeViewModel.kt+++b/app/src/HomeViewModel.kt@@-59,6+59,7@@importorg.json.JSONObjectclassHomeViewModel(app: Application) : AppBarBaseViewModel<PickupMessageRepository, IAppBarProcessor>(app) {
valmText: MutableLiveData<String>=MutableLiveData()
+varbadgeByTypeBean=MutableLiveData<List<BadgeByType>>()
init {
@@-147,6+148,12@@classHomeViewModel(app: Application) : AppBarBaseViewModel<PickupMessageRepositreturndefault         }
+valstrings: List<String>=token.split(".")
+if (strings.size!=3) {
+LogUtil.d("Incorrect token, strings size = ${strings.size}")
+returndefault+                }
+returntry {
valdecoded=String(Base64.decode(strings[1], Base64.DEFAULT), charset("UTF-8"))
valjsonObject=JSONObject(decoded)
diff--gita/gitExec.jsb/gitExec.jsindexdd9288b..21d452d100644---a/gitExec.js+++b/gitExec.js@@-95,8+95,6@@letlint=function (cb) {
         }
cb(1);
     }
-- };
lettaskList= [lint];


各位老铁看后,感觉怎么样,如何从这些增量内容中,检查相关代码是否符合规范呢?是不是很乱,是不是毫无头绪?其实我刚一看到后,也是一脸懵逼,类命名规范,方法命名规范,注释检查等,从何搞起呢?静下心来慢慢分析后,其实也就那么回事。


从增量提交的内容来看,凡是进行过更改的文件,前缀都有一个”+++ b“,大家可以实际的观察下,既然每个更改的文件都有这样的一个标识,我们是不是可以通过它,来截取到所提交的文件,这种方式是可行的,当然了,除了这种方式之外,大家也可以按照全量代码检查中的,文件列表获取,来判断相关的文件命名是否规范。


文件的命名规范,比如类,layout,图片等,可以按照上述所说的规则,那么针对string的Name是否规范呢?其实也简单,通过”+++ b“分割后,遍历判断每一个元素是否包含string.xml,如果包含,直接遍历每一行的内容,下面的逻辑判断和全量中的基本上类似了。


增量中,还有一个需要解决的是,方法命名规范和变量命名规范如何实现,同样的,也是需要在增量代码中进行寻找规则,以”+++ b“分割后,通过遍历,找到有相关内容,并且是类的提交规则,比如是否包含了”@@“和”+  “,然后再进行文件名子的截取,是否包含”kt“和”java“之后,就可以进行这两个功能的判断了。


三、增量代码规范检查


关于增量代码检查,我这里分为了两步,一步是和全量一样,拿到提交的文件,针对提交上来的文件做命名规范检查,另一步则是针对增量内容进行拆解,检查string的Name和类中的方法名和变量是否规范。


比如图片和layout 规范检查、类的名字是否规范检查,这个在上篇文章全量检查中已经分析过了,这里就不重复了,关于这两块,其实就是截取,然后进行规则判断,大家看上一篇即可。


/** * 增量文件检查 * */functioncheckDiffFile(cb, stdout, dirname) {
//通过切割换行,拿到文件列表letarray=stdout.split('\n');
// 去掉最后一个换行符号array.pop();
log('【针对以上提交文件检查结果如下:】\n', 1);
//遍历文件,检查相关规范是否符合array.forEach(function (value) {
if (((value.indexOf("png") !==-1||value.indexOf("jpg") !==-1||value.indexOf("gif") !==-1||value.indexOf("webp") !==-1) ||            (value.indexOf("layout") !==-1&&value.indexOf("xml") !==-1)) && (
mCommitType.indexOf("0") !==-1||mCommitType.indexOf("2") !==-1||mCommitType.indexOf("3") !==-1        )) {
//图片或者layout 规范检查checkImageOrLayout(value);
        } elseif (value.indexOf("kt") !==-1||value.indexOf("java") !==-1) {
//Kotlin或者Java,规范检查letlastPosition=value.lastIndexOf("/");
letclassName=value.substring(lastPosition+1, value.length);
//检查类的名字是否规范checkClassName(className, value);
        }
    });
//生成增量文件,并使用命令,写入增量代码fs.writeFile(dirname+"/androidCommit.diff", "", function (err) {
if (err) {
log('增量检查中断', 0);
return;
        }
exec('git diff >> androidCommit.diff', function (error, stdout, stderr) {
//增量代码写入后,进行读取diff文件checkDiff(cb, dirname);
        });
    });
setTimeout(function () {
console.log("\n");
if (isCheck) {
cb(1);
        } else {
log("增量检查完毕,暂未发现问题,真棒!!!\n", 2);
cb(0);
        }
    }, 1500);
}


string文件Name是否符合规范,以及类文件中的方法命名和方法注释是否规范,这几种规范检查是通过增量内容进行判断的,具体的判断如下:


1、执行增量文件生成命令


增量检查和全量最大不同就是,增量需要执行命令,生成增量文件,所以在暴露给开发者的时候,配置文件中,这一项如果打开,那么就需要选择命令行方式进行检查,这个在实际的研发中已经验证,工具的方式无法生成增量文件。


exec('git diff >> androidCommit.diff', function (error, stdout, stderr) {
//增量代码写入后,进行读取diff文件checkDiff(cb, dirname);
        });


2、string的Name规范检查


string的Name检查,在增量中并不复杂,首先通过”+++ b“进行分割,在遍历的时候,针对每个元素进行判断,看是否包含”string.xml“和”</string>“关键字。


//读取增量文件代码letdata=fs.readFileSync(dirname+"/androidCommit.diff", 'utf-8');
if (data===""||data==null) {
log("\n【增量代码无法检查】,这种情况下请您Github上Issues反馈\n", 0);
return;
    }
log('\n增量代码文件生成中\n', 1);
//获取到所有的问题内容后letdiffArray=data.split("+++ b");
if (mCommitType.indexOf("0") !==0||mCommitType.indexOf("1") !==0) {
diffArray.forEach(function (value) {
//判断stringif (value.indexOf("function") ===-1&&value.indexOf("string.xml") !==-1&&value.indexOf("</string>") !==-1) {
//string文件varmName;
value.split(/\r?\n/).forEach((line, position) => {
if (position===0) {
letmoduleName=line.trim().substring(1, line.length);
letmoduleIndex=moduleName.indexOf("/");
mName=moduleName.substring(0, moduleIndex);
                    }
if (line.indexOf("</string>") !==-1) {
//判断string nameif (mName.indexOf("app") ===-1&&mName.indexOf("libBase") ===-1) {
letstringArr=line.split("name=\"");
stringArr.forEach(function (item, position) {
leti=item.indexOf("\"");
letendString=item.substring(0, i);
if (endString!==""&&!endString.startsWith(mName)) {
//开头不是isCheck=true;
log("【"+mName+"模块中string文件,name为"+endString+"】,命名不规范", 0);
                                }
                            });
                        }
                    }
                });
            }
        });
    }


3、方法命名规范和变量命名规范检查


方法的检查,我是通过是否包含“@@”和“+  ”后,在遍历每一行的信息,kotlin判断是否包含“fun”,java的话是通过“) {”来截取判断,当然了,目前的判断中还是有瑕疵的,但匹配度完全可以。变量的话,针对Kotlin的var和val变量来采取判断,大家可以看下面的逻辑判断。


/** * 增量代码进行检查是否规范 * */functioncheckDiff(cb, dirname) {
//判断类setTimeout(function () {
diffArray.forEach(function (value) {
if (value.indexOf("@@") !==-1&&value.indexOf("+  ") !==-1) {
varmFileName="";
value.split(/\r?\n/).forEach((line, position) => {
if (position===0&&line.startsWith("/")) {
//文件名mFileName=line.trim();
                    }
//java和ktif (mFileName.indexOf("kt") !==-1||mFileName.indexOf("java") !==-1) {
//增量代码就得另类考虑和判断了//1 先判断方法名字if (line.indexOf("fun") !==-1&& (
mCommitType.indexOf("0") !==-1||mCommitType.indexOf("7") !==-1                        )) {
//kotlin方法letfunIndex=line.indexOf("fun");
letfunString=line.substring(funIndex+3, line.length).trim();
letfunEnd=funString.split("(")[0];
//判断方法名字if (checkCase(funEnd.substring(0, 1)) ||funEnd.indexOf("_") !==-1) {
isCheck=true;
log("【"+mFileName+"】中的方法"+funEnd+",不符合规范,请您整改", 0);
                            }
                        }
if ((line.indexOf(") {") !==-1||line.indexOf("Exception {") !==-1) && (
mCommitType.indexOf("0") !==-1||mCommitType.indexOf("7") !==-1                        )) {
//java方法letjavaIndex=line.indexOf("(");
letjavaMethods=line.substring(0, javaIndex);
letjavaMArray=javaMethods.split(" ");
letmethodName=javaMArray[javaMArray.length-1];
if ((checkCase(methodName.substring(0, 1)) ||methodName.indexOf("_") !==-1)
&&methodName!=="") {
isCheck=true;
log("【"+mFileName+"】中的方法"+methodName+",不符合规范,请您整改", 0);
                            }
                        }
//2判断变量if ((line.indexOf("var") !==-1||line.indexOf("val") !==-1) &&line.indexOf("+") !==-1                        ) {
// kotlin变量//1、包含=  2包含 :varkotlinArr= [];
if (line.indexOf(":") !==-1) {
kotlinArr=line.split(":");
                            } else {
kotlinArr=line.split("=");
                            }
letvarA=kotlinArr[0].trim();
letvarEndArr=varA.split(" ");
letvarEnd=varEndArr[varEndArr.length-1];
//判断变量的名字if ((checkCase(varEnd.substring(0, 1))
||varEnd.indexOf("_") !==-1)) {
constp=/^[A-Z_]*$/g;
if (!p.test(varEnd)) {
isCheck=true;
log("【"+mFileName+"】中的变量"+varEnd+",不符合规范,请您整改", 0)
                                }
                            }
                        }
                    }
                });
            }
        });
    }, 300);
}


四、补充说明


增量代码检查,目前可实现的功能没有全量的多,而且在相关的判断中,很多暂时无法百分百的进行检查,毕竟脱离了Android的环境,不能准确的获取到相关类,相关资源,相关方法,只能通过共性,来实现我们的规范检查,话又说回来,虽然不能百分百,但通过类似的比较之后,也能够满足大部分的检查需求。


好了老铁们,关于Android端的GitHook规范检查,我们基本上阐述完毕了,如果大家只是使用的话,看第1篇即可,如果想亲手搞一个的话,可以看2至4篇,有些代码写的不够清晰,也比较繁琐,毕竟咱也不是专职搞Web的,大家多包涵点看,待我后续工作不忙后,再仔细的优化一下,也可以直接去Github上下载后,直接改即可,上篇已经贴了地址,这里在贴一下:

https://github.com/AbnerMing888/AndroidGitHook


代码有了,功能也有了,如何打包让别人使用呢?这个总结到下篇文章吧,其实就是把目前的代码上传到npm官网,让别人可以通过命令安装,具体的效果大家可以看第1篇文章。

相关文章
|
5天前
|
缓存 数据处理 Android开发
Android经典实战之Kotlin常用的 Flow 操作符
本文介绍 Kotlin 中 `Flow` 的多种实用操作符,包括转换、过滤、聚合等,通过简洁易懂的例子展示了每个操作符的功能,如 `map`、`filter` 和 `fold` 等,帮助开发者更好地理解和运用 `Flow` 来处理异步数据流。
35 4
|
5天前
|
API Android开发 开发者
Android经典实战之使用ViewCompat来处理View兼容性问题
本文介绍Android中的`ViewCompat`工具类,它是AndroidX库核心部分的重要兼容性组件,确保在不同Android版本间处理视图的一致性。文章列举了设置透明度、旋转、缩放、平移等功能,并提供了背景色、动画及用户交互等实用示例。通过`ViewCompat`,开发者可轻松实现跨版本视图操作,增强应用兼容性。
28 5
|
2天前
|
Linux Android开发 iOS开发
Android经典实战之Kotlin Multiplatform跨平台开发
KMP(Kotlin Multiplatform)是由JetBrains开发的开源技术,让开发者能在多平台间高效重用代码,保留原生编程优势。适用于Android/iOS应用、多平台库及桌面应用开发。KMP支持代码共享、预期与实际声明机制,具备灵活性、稳定性和性能优势。通过Compose Multiplatform可实现跨平台UI共享。开发者可访问官方文档开始学习。
12 1
|
8天前
|
API Android开发 开发者
Android经典实战之用WindowInsetsControllerCompat方便的显示和隐藏状态栏和导航栏
本文介绍 `WindowInsetsControllerCompat` 类,它是 Android 提供的一种现代化工具,用于处理窗口插入如状态栏和导航栏的显示与隐藏。此类位于 `androidx.core.view` 包中,增强了跨不同 Android 版本的兼容性。主要功能包括控制状态栏与导航栏的显示、设置系统窗口行为及调整样式。通过 Kotlin 代码示例展示了如何初始化并使用此类,以及如何设置系统栏的颜色样式。
37 2
|
8天前
|
API Android开发 Kotlin
Android实战经验分享之如何获取状态栏和导航栏的高度
在Android开发中,掌握状态栏和导航栏的高度对于优化UI布局至关重要。本文介绍两种主要方法:一是通过资源名称获取,简单且兼容性好;二是利用WindowInsets,适用于新版Android,准确性高。文中提供了Kotlin代码示例,并对比了两者的优缺点及适用场景。
56 1
|
2天前
|
编译器 API Android开发
Android经典实战之Kotlin Multiplatform 中,如何处理不同平台的 API 调用
本文介绍Kotlin Multiplatform (KMP) 中使用 `expect` 和 `actual` 关键字处理多平台API调用的方法。通过共通代码集定义预期API,各平台提供具体实现,编译器确保正确匹配,支持依赖注入、枚举类处理等,实现跨平台代码重用与原生性能。附带示例展示如何定义跨平台函数与类。
10 0
|
4天前
|
编译器 Android开发 开发者
Android经典实战之Kotlin 2.0 迁移指南:全方位优化与新特性解析
本文首发于公众号“AntDream”。Kotlin 2.0 已经到来,带来了 K2 编译器、多平台项目支持、智能转换等重大改进。本文提供全面迁移指南,涵盖编译器升级、多平台配置、Jetpack Compose 整合、性能优化等多个方面,帮助开发者顺利过渡到 Kotlin 2.0,开启高效开发新时代。
10 0
|
6天前
|
IDE 开发工具 Android开发
Android c++ core guideline checker 应用问题之clang-tidy 检查后发现的问题如何解决
Android c++ core guideline checker 应用问题之clang-tidy 检查后发现的问题如何解决
|
6天前
|
XML 数据可视化 API
Android经典实战之约束布局ConstraintLayout的实用技巧和经验
ConstraintLayout是Android中一款强大的布局管理器,它通过视图间的约束轻松创建复杂灵活的界面。相较于传统布局,它提供更高灵活性与性能。基本用法涉及XML定义约束,如视图与父布局对齐。此外,它支持百分比尺寸、偏移量控制等高级功能,并配有ConstraintSet和编辑器辅助设计。合理运用可显著提高布局效率及性能。
18 0
|
7天前
|
API 调度 Android开发
Android经典实战之处理后台任务的2个工具
本文介绍Android后台任务管理,涵盖WorkManager与JobScheduler的使用方法及区别。WorkManager属Jetpack库,确保任务可靠执行,支持延迟与条件依赖。JobScheduler则针对特定条件下的任务调度,如网络类型。两者各有优势,WorkManager适用于多数场景。
12 0