Android Module依赖关系的可视化实现

Android Module依赖关系的可视化实现,从入门到深入 val ISSUE: Issue = Issue .create( id = “DependencyProjectDetector”, briefDescription = “app dependency relationship”, explanation = “”" app dependency relationship 《Android学习笔记总结+最新移动架构视频+大厂安卓面试真题+项目实战源码讲义》 【docs.qq.com/doc/DSkNLaERkbnFoS0ZF】 完整内容开源分享 “” 阅读详情

背景

大力教育下的Android工程代码,从出生开始都是组件化的结构,所以多module是必然的事情。伴随着模块的新增/删除/移动,都会导致之前梳理好的模块依赖图变得老旧,需要额外的人力去维护模块依赖结构。特别是对于刚融入的新同学,参考这老版本的依赖图,会有一定程度上的信息滞后。

那么是不是可以写一个这样的自动化工具,代替人力来动态维护module之间的依赖关系?本文就基于lint方案,自动实现模块之间的依赖关系视图。

Lint

大家对Lint的认知,基本上都觉得它只是一个静态代码分析工具,通过规则规范团队代码。但我想为Lint证明:「只有你想不到,没有我做不到」的能力。Lint在运行期间一个重要环节,就是依赖于gradle环境:

./gradlew lintDebug

既然运行在了gradle环境中,必然可以拿到不同模块之间的依赖关系,该方案去做模块可视化,看去可行。

关键类:

  • Detector:文件检测类,可以扫描gradle/java/kt/res等等文件。

  • Issue:每个Detector关注的焦点问题。

这里不多介绍Lint开发环境的搭建,可以直接参考官方demo:github.com/googlesampl… 。 具体效果可以参考ToastDetector:提醒Toast增加show()方法。

关键方法:

Detector中有两个和project相关的方法:

  • beforeCheckRootProject(context: Context),遍历根module之前的回调,即项目中最顶层app module

  • beforeCheckEachProject(context: Context),遍历每个子module之前的回调。

该两个方法中都有Context对应的上下文,在构造函数中惊奇的发现Project这个属性。

open class Context(
    main: Project?,
    project: Project?,
    val file: File,
    private var contents: CharSequence? = null
) 

是不是感觉和gradle中的project很像?通过debug发现,该project确实包含了build.gradle中的depedency信息,那么基于Lint方案的思路就肯定可以落地了。窃窃自喜:「只要你想要,没有我给不了」。

关键代码:

class DependencyProjectDetector : Detector(), Detector.UastScanner {
        // 各个module之间的依赖树
    private val treeMap = HashMap<String, ElementNode>()
    // 根module结点
    private var rootNode = ElementNode()
    companion object {
        const val TAG = "DependencyProjectDetector"
        private val IMPLEMENTATION = Implementation(
            DependencyProjectDetector::class.java,
            Scope.JAVA_FILE_SCOPE
        )
        val ISSUE: Issue = Issue
            .create(
                id = "DependencyProjectDetector",
                briefDescription = "app dependency relationship",
                explanation = """
                app dependency relationship
            """.trimIndent(),
                category = Category.CORRECTNESS,
                priority = 9,
                severity = Severity.FATAL,
                androidSpecific = true,
                implementation = IMPLEMENTATION
            )

    }
        //这里可以随意,主要是为了借助遍历method的能力。
    override fun getApplicableMethodNames(): List<String>? {
        return listOf("a")
    }

    override fun beforeCheckRootProject(context: Context) {
        rootNode = collectNode(context.project.name)
        super.beforeCheckRootProject(context)
    }
        //分析当前模块依赖情况,并收集依赖信息。
    private fun analysisCurrentProjectDependency(project: Project) {
        val projectName = project.name
        val currentNode = collectNode(projectName)
        val artifact = project.currentVariant.mainArtifact
        val list = project.directLibraries.filter { !it.isExternalLibrary }
        list.forEach {
            val childNode = collectNode(it.name)
            if (!currentNode.dependencyNode.map { it.moduleName }.contains(childNode.moduleName)) {
                currentNode.dependencyNode.add(childNode)
            }
        }
    }

    private fun collectNode(moduleName: String): ElementNode {
        val currentNode = if (treeMap.containsKey(moduleName)) {
            treeMap[moduleName]!!
        } else {
            ElementNode().apply {
                this.moduleName = moduleName
                treeMap[moduleName] = this
            }
        }
        return currentNode
    }

        //输出markdown格式的文本信息
    private fun generateMarkDownResult() {
        for (element: ElementNode in treeMap.values) {
            removeUnnecessaryDependency(element)
        }
        println("```mermaid")
        val head = "graph TD"
        println(head)
        for (element: ElementNode in treeMap.values) {
            val currentName = element.moduleName
            for (childElement: ElementNode in element.dependencyNode) {
                println("${currentName}[${currentName}]-->${childElement.moduleName}[${childElement.moduleName}]")
            }
        }
        println("```")
    }

    /**
     * 优化:移除不需要的依赖,原则:currentNode的孩子队列,最短依赖,有可以被非最短依赖替代的,则删除最短依赖。
     */
    private fun removeUnnecessaryDependency(currentNode: ElementNode) {
        val dependencyList = currentNode.dependencyNode
        //为了避免remove失败,创建了一个temp list
        val tempDependencyList = ArrayList(dependencyList)
        val iterator = tempDependencyList.iterator()
        while (iterator.hasNext()) {
            val targetElement = iterator.next()
            val leftTargetElements = tempDependencyList.filter { it != targetElement }
            for (otherItem: ElementNode in leftTargetElements) {
                if (containNode(otherItem, targetElement)) {
                    dependencyList.remove(targetElement)
                    continue
                }
            }
        }
    }

    /**
     * rootNode下面是否包含targetNode结点
     */
    private fun containNode(rootNode: ElementNode, targetNode: ElementNode): Boolean {
        if (rootNode == targetNode) {
            return true
        }
        val rootChildren = rootNode.dependencyNode
        for (child: ElementNode in rootChildren) {
            if (rootNode == child) {
                return true
            } else {
                var result = containNode(child, targetNode)
                if (result) {
                    return result
                }
            }
        }
        return false
    }

    override fun beforeCheckEachProject(context: Context) {
        if (context.project.isExternalLibrary) {
            return
        }
        analysisCurrentProjectDependency(context.project)
        super.beforeCheckEachProject(context)
    }

    override fun afterCheckRootProject(context: Context) {
        generateMarkDownResult()
        super.afterCheckRootProject(context)
    }
}

核心原理: 通过遍历project,收集当前project的projectName和projectDepedencyList,最后把每个module对应的project进行汇总,梳理出整个树状依赖结构。既然整个依赖数据结构已经有了,下一步是不是要可视化输出了?

Markdown

为什么要选择Markdown?工具不重要,核心是要能出图。基于以下原因考虑:

  1. 熟悉,不需要额外学习成本。
  2. 简单,流程图语法参考:mermaid-js.github.io/mermaid/#/?… 。
  3. 满足我的需求,当下只需要流程图能力。
  4. 无缝链接编程语言,无需额外依赖。

结果图:

通过一个demo来展示当前依赖效果。

虽然只有几个模块,但由于一些重复依赖的线条,导致该图不那么的清晰简洁。所以做一些精简:

  1. app->account-api->common
  2. app->common

改善的原则:「保留多结点依赖,删除直接依赖」。也就是保留【1】,删除【2】,具体实现参考上述代码中的removeUnnecessaryDependency。

改善后:

怎么样,模块依赖是不是比较清晰?

坑

Lint默认运行是不会跨module的,比如./gradlew lint,会对每个module进行独立的分析和输出,这就导致每一份分析结构都是针对当前module的,所以无法收集相互之间的依赖。解决方法:

 

lintOptions {
    checkDependencies true
}

果不其然,api文档中也没有checkDependencies属性,google.github.io/android-gra… 那只能你自己慢慢去debug吧。

lint在gradle插件的不同版本3.+和4.+上,会有很大的不同,所以你想要集成该能力,建议自己根据当前工程的gradle版本,再做兼容调试。

价值

  1. 在新人的角度,可以快速获取最新依赖图,加速对项目结构的理解。
  2. 在非新人的角度,定期输出依赖图,分析其中不合理的依赖并及时改进。
Android Module依赖关系的可视化实现,移动应用与开发就业方向 大力教育下的Android工程代码,从出生开始都是组件化的结构,所以多module是必然的事情。伴随着模块的新增/删除/移动,都会导致之前梳理好的模块依赖图变得老旧,需要额外的人力去维护模块依赖结构。特别是对于刚融入的新同学,参考这老版本的依赖图,会有一定程度上的信息滞后。 那么是不是可以写一个这样的自动化工具,代替人力来动态维护module之间的依赖关系?本文就基于lint方案,自动实现模块之间的依赖关系视图。 Lint ==== 大家对Lint的认知,基本上都觉得它只是一个静态代码分析工具,通过规则规范 阅读详情

相关推荐

Android-Android依赖可视化工具

Android依赖可视化工具。它是一个工具,有助于可视化项目的当前状态,很容易看出你类的耦合度

Android项目中多Module项目架构以及优雅的解决第三方库依赖问题

Android项目中多Module项目架构以及优雅的解决第三方库依赖问题

weixin_42602900的博客 2822

从零到一:手把手教你用DJI E-Port套件为M350 RTK开发第三方负载(PSDK V3实战)

本文详细介绍了如何使用DJI E-Port套件为M350 RTK开发第三方负载,涵盖硬件配置、开发环境搭建及实战应用。通过PSDK V3开发套件,开发者可以高效实现负载与无人机的通信,适用于电力巡检等工业场景,提升开发效率与系统稳定性。

weixin_26841743的博客 704

Android组件化开发1解决组件间相互依赖问题

最近在把项目封装成组件化,出现一个很奇怪的问题,由于说刚上手项目,组件化经验也不是特别丰富,导致出现了很多小问题,这里就不过多纠结,实战出真知,遇到问题去分析问题产生的原因及解决它就可以 了。实现上代码,出现的错误日志如下: 1.我们翻译一下日志: 意思就是说组件home和组件common间相互依赖,循环引用,所以发生错误。 2.在看我的代码: module_home组件 module_common组件 果然和错误日志一样,就是两个组件之间相互依赖引用导致的问题...

u012556114的博客 3946

瑞萨RH850/U2A8以太网模块mcal说明

如下所有功能的支持需要在达芬奇中开启EthGlobalTimeSupport。秒[32bit], 纳秒[32bit], 纳秒小数[20bit]每一帧发送数据的相关结构缓冲区都放在变量。每一帧接收数据的相关结构缓冲区都放在变量。

至道无难,唯嫌拣择。 1864

Spring AI Alibaba实战训练营-16 NL2SQL (自然语言转 SQL)应用开发指南

本文介绍了一个基于AI的智能BI应用开发项目,主要实现自然语言转SQL(NL2SQL)功能,包含对话模块、MCP模块、向量数据管理模块。项目采用Spring AI Alibaba框架,集成通义千问大模型,支持内存向量库和AnalyticDB向量数据库两种存储方案。重点展示了chat模块的三个控制器实现:AnalyticNl2SqlController用于生产环境,SimpleChatController适用于开发测试,Nl2sqlForGraphController支持基于图的复杂对话流程。系统通过将业务知

program哲学 1589

Android 组件化场景下多module依赖优雅实践方案,h5移动端开发适配

通过pom文件 举个例子: 我们新建一个module,看一下依赖: dependencies { implementation “org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version” implementation ‘androidx.core:core-ktx:1.3.2’ implementation ‘androidx.appcompat:appcompat:1.2.0’ implementation ‘com.google.android.materi

m0_64382743的博客 465

最优化方法与数学建模

在本篇博客中,我们将探讨最优化方法在数学建模中的应用。首先,我们将介绍几种常见的最优化方法,如梯度下降、牛顿法和遗传算法。接着,我们将详细阐述这些方法的原理和算法。最后,我们将通过一个数学建模案例,展示如何使用Matlab实现这些方法。本文旨在为大家提供一个清晰的思路,以便更好地理解和应用最优化方法。

YOLO 439

gradle绘制各个模块的依赖图

1. 使用 Gradle 插件:可以使用如 org.dm.gradle.plugins.dependency-graph 这样的 Gradle 插件,来生成模块依赖图。在 IntelliJ IDEA 中打开 Gradle 项目,然后转到 菜单栏中的 View -> Tool Windows -> Gradle。2. 使用 IntelliJ IDEA:IntelliJ IDEA 可以自动为您绘制 Gradle 项目中的依赖图。在 Gradle 中,您可以使用多种方式绘制项目中各个模块的依赖关系图。

qq_31433709的博客 1060

VMware报错系列:虚拟机配置文件(.vmx)损坏或丢失---修复

虚拟机配置文件(.vmx)损坏或丢失---修复

weixin_54626591的博客 7014

YOLOv8改进策略【注意力机制篇】| CVPRW-2024 分层互补注意力混合层 H-RAMi 针对低质量图像的特征提取模块

许多证据表明层次化网络对图像恢复(IR)任务通常不太有效,因为IR的目标是逐个预测像素值(密集预测),而缩小特征图会丢失重要的像素级信息。然而,层次化结构有降低时间复杂度以及学习语义级和像素级特征表示的优点。为了弥补缺点并利用优点,设计了H - RAMi层。

Limiiiing的博客 379

Android Studio入门(8)— Module之间相互引用

Android Studio中新建Project,在Project下可以创建多个Module,每个Module都相当于一个APP,可以独立运行,如果一个Module想依赖于其他Module,应该如何设置呢? Ctrl+Alt+Shift+S打开Project Structure,将想要调用的类所在的Module加入到Module dependency中即可。 ...

长颈鹿的专栏 3102

SimSwap复现指引及代码分析【2023有更新】

我的环境!!!😚😚😚 之后会遇到:还会遇到至此环境配置完毕!下面是所需要的checkpoint。至此环境准备结束。先试试环境是否有问题,图片是代码包中给的。我用的是512的checkpoint,无水印,a是源脸,b是目标脸。输出到./output/result_whole_swapsingle.jpg 如图是可以成功换脸的:(为了方便查看将图像裁剪到了128x128) 表示图像预处理,相应的改为也是一样。不加就是对已经面部对齐的图像进行简单的面部交换。 加上就是无水印。 交换一张图片中的所有面孔。

zik的博客 2050

Android Module依赖关系的可视化实现,kotlin标准库

该两个方法中都有Context对应的上下文,在构造函数中惊奇的发现Project这个属性。 open class Context( main: Project?, project: Project?, val file: File, private var contents: CharSequence? = null ) 是不是感觉和gradle中的project很像?通过debug发现,该project确实包含了build.gradle中的depedency信息,那么基于Lint方案的思路就肯定可以落地了

m0_64603776的博客 278

L2正则化与Ridge回归的关系

非常感谢您提供的详细任务描述和要求。我会尽力按照您的指引来撰写这篇技术博客文章。 L2正则化与Ridge回归的关系 1. 背景介绍 机器学习模型在训练过程中容易出现过拟合的问题,这会导致模型在训练集上表现很好但在测试集上泛化性能较差。为了解决这一问题,正则化技术应运而生。其中

AGI×大数据,开启智能时代的认知跃迁;解码AGI,赋能数据驱动的智能革命。 661

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

Android Module依赖关系的可视化实现,mmkv缺点

关键代码: class DependencyProjectDetector : Detector(), Detector.UastScanner { // 各个module之间的依赖树 private val treeMap = HashMap<String, ElementNode>() // 根module结点 private var rootNode = ElementNode() companion object { const val TAG = “DependencyProject

m0_66684727的博客 208

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

Android studio 工程的 module 依赖关系图绘制 、 Android Module 依赖关系的可视化实现

整体步骤:1、利用gradle脚本生成dot;2、利用graphviz将dot可视化转为图片。

火星男孩的分享空间 1729

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

Android Module依赖关系的可视化实现,kotlinlambda表达式

这里不多介绍Lint开发环境的搭建,可以直接参考官方demo:github.com/googlesampl… 。 具体效果可以参考ToastDetector:提醒Toast增加show()方法。 关键方法: Detector中有两个和project相关的方法: beforeCheckRootProject(context: Context),遍历根module之前的回调,即项目中最顶层app module beforeCheckEachProject(context: Context)

m0_66264881的博客 223

Android Module依赖关系的可视化实现,android图书管理系统源码

private var contents: CharSequence? = null ) 是不是感觉和gradle中的project很像?通过debug发现,该project确实包含了build.gradle中的depedency信息,那么基于Lint方案的思路就肯定可以落地了。窃窃自喜:「只要你想要,没有我给不了」。 关键代码: class DependencyProjectDetector : Detector(), Detector.UastScanner { // 各个module之间的依赖树 p

m0_66144992的博客 1103

Daggraph源码探秘:如何解析@Module与@Component生成依赖关系图

Daggraph是一款专为Android开发者打造的Dagger依赖关系图生成工具,它能够自动解析项目中的@Module与@Component注解,将复杂的依赖关系可视化呈现。对于新手开发者来说,理解Dagger依赖注入的工作原理往往是一个挑战,而Daggraph通过直观的图形化展示,让这一过程变得简单易懂。 ## 核心功能:从代码到图形的转换 Daggraph的核心功能是将Android项目

gitblog_00168的博客 194

终极Dagger依赖可视化方案:Daggraph如何解决Android项目组件依赖难题

在Android开发中,随着项目规模扩大,Dagger依赖注入框架的使用往往会导致组件关系错综复杂,开发者常面临依赖链不清晰、调试困难等问题。Daggraph作为一款专为Android开发者设计的Dagger依赖图生成工具,能够自动解析项目中的组件依赖关系,通过直观的可视化方式帮助开发者快速梳理架构脉络,提升代码维护效率。 ## 为什么Android开发者需要依赖可视化工具? 当项目中使用Da

gitblog_00827的博客 420

探秘模块依赖图:Streamlining Gradle Module Complexity with module-dependency-graph

探秘模块依赖图:Streamlining Gradle Module Complexity with module-dependency-graph 在复杂的多模块Gradle项目中导航,往往像是在迷宫中寻找出路。但有了module-dependency-graph这一利器,您将能够一目了然地洞察整个项目的骨骼结构,轻松管理依赖关系。本文将带您深入了解这一高效工具,探索其技术精粹,应用场景,并揭示...

gitblog_00078的博客 840

android项目中有哪几种依赖关系,Android Studio项目中三种依赖的添加方式

通常一个AS项目中的依赖关系有三种,一是本地依赖(主要是对本地的jar包),二是模块依赖,三是远程依赖;添加这些依赖的目的在于上我们想要在项目的某一个模块中使用其中的功能,比如okttp这个网络框架库,如果我们想要在项目的app模块下使用这个库的功能,则需要在app模块的build.gradle文件中添加相应的依赖,Gradle插件首先会在本地文件系统上去查找是否存在,如果不存在,然后会到全局的b...

weixin_42509267的博客 1632

Android开发中Module引用的实用技巧与步骤详解

在Android项目开发中,模块化引用是提高代码复用性的重要手段。通过将通用功能封装成独立Module,不仅便于团队协作,还能实现功能的快速迭代更新。实际使用中发现,平台的项目预览功能特别适合验证模块集成效果,无需完整编译就能查看结构调整情况。对于初学者来说,这种可视化操作方式能大幅降低学习曲线。平台内置的Android项目模板可以一键生成基础框架,省去环境配置时间。注意检查模块的build.gradle配置是否与主项目兼容。对于想快速验证模块化方案的开发者,推荐使用。

MoonbeamFalcon67的博客 402
上一篇: 如何让自己的代码显得高深莫测 - Android
下一篇: “我辞退了一位面试简历造假的程序员”
懂你的大海
博客等级 码龄6年 9049粉丝 1381原创
评论 8
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值