Android面试指南:说说你对组件化/模块化的理解

news2025/2/25 2:45:30

到现在组件化真的不是什么新鲜东西了,大公司都用的滚瓜烂熟,龙飞凤舞了,也就是现在部分中型项目和小项目在组件化的路上努力。所以同志们,组件化没玩过的,不熟悉的赶紧搞起来,说一点,你不会组件化,发布影子工程那么对你来说就是个噩梦。从本质上来讲任何技术进步都是在现实需求的逼迫下抓耳挠腮,耗尽无数头发才想出来的。哈哈,这里说个笑话罢了。所以呢组件化这个东西出来这么久了,页发展了这么久了,用的人越来越多,那肯定是对我们显示开发大有裨益的,下伙伴们不会,不熟悉抓紧啦,要不面试问你你怎么回答呢!

下面来正式说说组件化

组件化这个东西其实并不复杂,他就是种思路,本质上是一种 app 架构思路,说穿了很简单的,难在组件化改造的时候,真正写起代码会出现不少棘手的问题,当然这些坑基本前人都趟完了,这里我主要是记录下,要是你看到熟悉的部分,请不要骂我啊,毕竟都是前辈们的东西啊。

这里补充一下,组件化是一种 app 架构,他的发展也是沿着正常的技术发展脉络来的,也是为了以追求高复用,高可维护性的目的的代码封装,区别是组件化是对整个 app 的再次封装。

废话了这么多,那么什么是组件化呢,各位看官想不要着急,在详细说组件化之前,我们要搞懂2个概念,就是上面说的组件和模块。

首先组件和模块都不是官方规定的,都是这些技术发展下来大家约定俗成的概念,其实很简单,一说就明白

  • 模块:android 中的模块就是业务模块,单指业务,是按照业务对 app 进行拆分,比如说订单我们搞成一个模块,个人中心我们搞成一个模块,视频,音频这些都搞成模块,在app中的体现就是 一个个module,module 的中文意思也是模块,这不准这就是 google 对我们的暗示呢。模块化的目的是为了搭积木,随便拿几个模块module 出来就可以谁谁便便的上线一个 app,你还别说现在影子 app 的需求很旺盛,你去看看大公司的项目那个不是一堆影子工程,头条还搞出一个头条视频的马甲呢,这其实就是把视频 module 拿出来,加上一个启动页。这样的例子是比比皆是的,要不说不会组件化影子工程对你就是噩梦呢,哈哈,到时候维护那是想也别想了,代码你要搞多少份啊。
  • 组件:这个一样简单啊,说穿了就是我们平时干的事,对功能的封装,这就是组件,一个功能就是一个组件,IO,数据库,网络等等这些功能都是组件,这么说你就明白了吧。既然这样那为毛线我们还要搞出来这个一个组件的概念,当然了任何事都是有其意义的,因为组件对功能代码的封装有个很高了明确的要求:一处封装,处处使用。要我们把维护性,复用性,扩展性,性能做到极致,因为这样才能真正做到一处封装,处处使用。当然组件的范围现在也是覆盖的很广的,app 中的一切都是组件,基本上我们分为:基础功能组件,通用UI组件,基础业务组件。

以上我谈了下我自己对于模块化,组件化的理解,是目前开发中对于模块和组件的理解。在模块化和组件化的发展中概念也是有些调整变化的,大家只要看现在是什么样子就好了,深入学习的话有兴趣可以看看组件化,模块化的发展历程。

我认为 Android 模块化探索与实践 对于模块化,组件化概念的解释是最优秀的。

组件化和模块化在现在看是一回事了,如果把一个项目看成是袋中的组合的话,那么模块就是体积最大的哪些袋子,组件就是体积小的袋子,大的袋子是最直接可被外接观测和接触的袋子,大的袋子也是用小的袋子组成的,一个不太恰当的比喻吧,模块和组件就是这样的关系,是我们对业务和功能拆分,封装的理解。

好了正式开始介绍了组件化啦

组件化在工程表现上就是我们把 app 按照其业务的不同,划分为不同的 module模块,把各种功能封装成一个个 library,module 之间是严格禁止横向依赖的,要不怎么单独使用呢,我不能为了用一个 module,把相关的module 都带上吧,要是这么 module 还有依赖的module 呢,这样谈复用性就是扯淡了。

主 app 就是我们常说的壳工程依赖这些 module,library 由需求的 module 依赖,但是要考虑library 版本的问题,随着业务和功能的扩展,library 的数量也是巨大的,微信在组件化拆分时据说拆分出80多个 module,可见 library 也是少不了的。

module 和 library 多数时候我们是提供arr 和 jar 来给壳工程引用的,arr 和 jar 在编译时是不会再编译的,只会检查版本,保留一个最新的版本,既提高了 app 的编译速度,也提供一种资源冲突解决方式。

下面我放一些图来描述一下组件化,大伙仔细看看,图比文字可生动多了

img

img

img

img

img

项目如何组件化:

img

img

img

组件化核心:router

我们在抽象 module 时,module 之间是没有相互依赖的,是严格解耦的,为了达到我们复用的目的。module 之间不能相互依赖,就没法调用别的 module 的代码了,那么面对业务之间的页面相互调起,相互通信这些常见的需求我们该怎么办,没错就是大伙在上面的图里面看见的东西 router。

router 是我们统一制定的模块间通讯协议,router 中我们主要是处理以下几个问题:

模块之间页面跳转 模块之间数据传递 模块初始化处理

img

router 这东西有现成的,你也可以自己封装。使用的思路都是把 router 作为一个组件,所有的业务 module 都依赖这个 router 组件,当然壳app 也是,然后我们把需要的模块间页面跳转,数据传递,初始化都注册到 router 中,这里面就体现到我们定义的统一,通用的模块通讯协议的重要性了,router 维护多个集合保存这里关系,然后我们通过router 就可以实现模块间的通讯了。

router 的封装还是挺麻烦的,要写好了不容易,现在用的比较多的有:

  • 阿里的 ARouter
  • 最早出现的 ActivityRouter
  • spiny同学的router这是我的最爱,目前不维护了,思路很棒,并且考虑到了进程化的问题,可惜没有使用 APT 注解技术
  • 练手的 router

上面我介绍了几个 router 路由,基本上不论是自己写还是用现成的,router 基本上都是上面这几个的样子了,当然了现在好的 router 还是要使用 APT注解技术来动态去 router 注册模块方法,自己写代码去注册的话使用很使用,有些问题不好处理,比如 router 的静态实例要是被回收了,你再 new 一个出来,那么模块注册的方法怎么办,写起来太麻烦,还不如 APT 注解来的方便,扩展性也好。这里有个ToyBricks_Android项目模块化解决方案 可以解决 APT不能扫描 arr 包的问题。

最后说一下,module 间的通讯其实可以分成3种:

  • 页面调起
  • 某种事件的通知
  • 直接调用某些模块的业务方法

页面调起现在的 router 都可以很好的完成这个任务。

某些事件的通知,比如我切换城市了,通知某些页面去显示或是刷新数据,这个根据业务来说影响的范围会很广的,会影响多个业务的,因为 module 的复用性,我们在 module 中是不能确定会具体影响哪些业务module 的,那么这种场景使用 eventbus/广播比较合适了。

直接调用默写模块的业务方法,这属性业务模块间在业务上的强耦合了,这个碰到产品这么设计你也没办法,一般碰到这样的场景也是会保证相关的业务module 都是会加载的,所以呢在定义 router 灵活一些,可以做到调用指定module 的某些方法

找到另一个说法,我很喜欢,和我的理念也很接近 出自:Android 架构设计:MVC、MVP、MVVM和组件化

所谓的组件化,通俗理解就是将一个工程分成各个模块,各个模块之间相互解耦,可以独立开发并编译成一个独立的 APP 进行调试,然后又可以将各个模块组合起来整体构成一个完整的 APP。它的好处是当工程比较大的时候,便于各个开发者之间分工协作、同步开发;被分割出来的模块又可以在项目之间共享,从而达到复用的目的。组件化有诸多好处,尤其适用于比较大型的项目。

各个模块之间如何进行数据共享和数据通信?我们可以把需要共享的数据划分成一个单独的模块来放置公共数据。各个模块之间的数据通信,我们可以使用阿里的 ARouter 进行页面的跳转,使用封装之后的 RxJava 作为 EventBus 进行全局的数据通信。

router 我不想说太多,也说不好,这部分大伙看我最后的链接吧,或是看看上面4个路由也可以,不论如何实现,router 是为了给 module 模块搭建一个通讯的中间平台,目的就是这样。仔细的大家多看吧,这里我也是看别人的。

我再逼逼一下,module 和 library 我们尽量不要提供源代码的方式提供依赖,这不符合我们复用的目的,到时候你发布几个影子功能或是别的 app,那么你使用源代码依赖方式的 module 和 library 你怎么提供维护,所以尽量使用 arr 和 jar 的方式。我们可以把一些定位相近library 打包成一个 module 也是不错的。

我们一定要熟悉gradle的使用,在组件化中我们会大量的使用 gradle 提供各种资源加载的配置和环境配置

组件化最核心的目的就是代码的高可复用和高可维护和高可扩展性能,其他的优点都是属于连带性质的,我们要先把握住核心点学习,其他的都不是主要,有时间再看

组件化碰到的问题

1. 子模块单独编译测试

在做组件化开发时,我们测试 module 库都是把 module 单独达成 apk 文件,在发布module时 提供 library 供外界依赖,这都是通过配置 module 的 gradle 的编译模式实现的

首先在子模块build.gradle中定义常量,来标示模块目前是否处于开发模式

def isDebug = true 在子模块的build.gradle中进行模式配置。debug模式下编译成独立app,release模式下编译成library。

if (isDebug.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

两种模式下模块AndroidManifest.xml文件是有差别的。作为独立运行的app,有自己的Application,要加Launcher的入口intent,作为library不需要。这个问题很好解决,写两个不同的AndroidManifest.xml即可,并在gradle中进行配置。

img

在 gradle 脚本中配置

android {
    sourceSets {
        main {
            if(isDebug.toBoolean()) {
                manifest.srcFile 'src/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/release/AndroidManifest.xml'
            }
        }
    }
}

2. sdk和第三方库的版本一致性

不同module依赖sdk版本不一致,会因兼容性问题导致编译问题。 不同module引用了同一个第三方库的不同版本,并且这个库没有做到向前兼容,就有可能出现方法找不到、参数不对应等问题。 所以有必要统一整个project的依赖版本。

在最外层build.gradle中定义的常量能被整个project的build.gradle文件引用,统一的版本定义可以放在这里。

ext {
   android_compileSdkVersion = 25
    android_buildToolsVersion = '25.0.2'
   android_minSdkVersion = 21
    android_targetSdkVersion = 25

    lib_appcompat = 'com.android.support:appcompat-v7:25.1.1'
    lib_picasso = 'com.squareup.picasso:picasso:2.5.2'
    lib_gson = 'com.google.code.gson:gson:2.6.1'
}

我没试过 arr资源的 module 是否还可以使用这种方式

3. 资源id冲突

android 中 module的资源文件最后都是会合并到主项目中的,资源文件的 id 最终和 moudle 是的 id 是不一样的,所以这就会出现资源重名的问题,解决这个问题,我们的做法就是module 资源加一个统一的前缀

 andorid{
     ...
 
     buildTypes{
         ...
     }
 
     resourcePrefix "moudle_prefix"
 
}

但是注意 res 文件夹下的文件可以用 gradle 脚本加前缀,但是图片资源不行,图片资源我们还是需要在命名时自己添加前缀

4. application初始化的问题

子模块作为application时,有一些初始化的工作需要在Application.onCreate时进行。而作为library时,调不到这个onCreate。所以自己写一个静态方法,供主工程的Application调用。

 public class ApplicationA extends Application {
 
 @Override public void onCreate() {
   super.onCreate();
   //给底层library设置context
   AppContext.init(getApplicationContext());
 }
   /**
    * 作为library时需要初始化的内容
   */
  public static void onCreateAsLibrary() {
    //给FunctionBus传入接口的实例
    FunctionBus.setFunction(new FunctionA() {
      @Override public String getData(String key) {
        return "xixi";
      }
    });
  }
}

主工程的Application onCreate时记得初始化子模块。

public class MainApplication extends Application {

  @Override public void onCreate() {
    super.onCreate();
    AppContext.init(getApplicationContext());
    ApplicationA.onCreateAsLibrary();
    ApplicationB.onCreateAsLibrary();
  }
}

除了提供方法在壳工程里面调用,还可以结合使用了 APT 技术的 router 来做,使用注解,就不用我们自己去调用了,彻底解耦

5. library依赖问题

先说一个问题,在组件化工程模型图中,多媒体组件和Common组件都依赖了日志组件,而A业务组件有同时依赖了多媒体组件和Common组件,这时候就会有人问,你这样搞岂不是日志组件要被重复依赖了,而且Common组件也被每一个业务组件依赖了,这样不出问题吗?

其实大家完全没有必要担心这个问题,如果真有重复依赖的问题,在你编译打包的时候就会报错,如果你还是不相信的话可以反编译下最后打包出来的APP,看看里面的代码你就知道了。组件只是我们在代码开发阶段中为了方便叫的一个术语,在组件被打包进APP的时候是没有这个概念的,这些组件最后都会被打包成arr包,然后被app壳工程所依赖,在构建APP的过程中Gradle会自动将重复的arr包排除,APP中也就不会存在相同的代码了;

但是虽然组件是不会重复了,但是我们还是要考虑另一个情况,我们在build.gradle中compile的第三方库,例如AndroidSupport库经常会被一些开源的控件所依赖,而我们自己一定也会compile AndroidSupport库 ,这就会造成第三方包和我们自己的包存在重复加载,解决办法就是找出那个多出来的库,并将多出来的库给排除掉,而且Gradle也是支持这样做的,分别有两种方式:根据组件名排除或者根据包名排除,下面以排除support-v4库为例:

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile("com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion") {
        exlude module: 'support-v4'//根据组件名排除
        exlude group: 'android.support.v4'//根据包名排除
    }
}

library重复依赖的问题算是都解决了,但是我们在开发项目的时候会依赖很多开源库,而这些库每个组件都需要用到,要是每个组件都去依赖一遍也是很麻烦的,尤其是给这些库升级的时候,为了方便我们统一管理第三方库,我们将给给整个工程提供统一的依赖第三方库的入口,前面介绍的Common库的作用之一就是统一依赖开源库,因为其他业务组件都依赖了Common库,所以这些业务组件也就间接依赖了Common所依赖的开源库。

 dependencies {
     compile fileTree(dir: 'libs', include: ['*.jar'])
     //Android Support
     compile "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion"
     compile "com.android.support:design:$rootProject.supportLibraryVersion"
     compile "com.android.support:percent:$rootProject.supportLibraryVersion"
     //网络请求相关
     compile "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion"
     compile "com.squareup.retrofit2:retrofit-mock:$rootProject.retrofitVersion"
    compile "com.github.franmontiel:PersistentCookieJar:$rootProject.cookieVersion"
    //稳定的    compile "com.github.bumptech.glide:glide:$rootProject.glideVersion"
    compile "com.orhanobut:logger:$rootProject.loggerVersion"
    compile "org.greenrobot:eventbus:$rootProject.eventbusVersion"
    compile "com.google.code.gson:gson:$rootProject.gsonVersion"
    compile "com.github.chrisbanes:PhotoView:$rootProject.photoViewVersion"

    compile "com.jude:easyrecyclerview:$rootProject.easyRecyclerVersion"
    compile "com.github.GrenderG:Toasty:$rootProject.toastyVersion"

    //router
    compile "com.github.mzule.activityrouter:activityrouter:$rootProject.routerVersion"
}

6. module不同业务环境使用不同代码

我们做项目至少会有测试和线上2套环境吧,组件化让我们开始重视 gradle,通过 gradle 配置我们可以减少很多代码的书写的,切换环境我们也是可以用 gradle 实现的,在不通过的环境下注册不同的代码文件,看下面这张图

img

我们有 debug 和 release2个环境,里面放的是不同环境执行的代码,main 里面是跟环境切换无关的代码部分,我我们这样设置 gradle 就可以了

 android {
     // ...
     sourceSets {
         debug {
             java.srcDirs = ['src/main/java', 'src/debug/java']
         }
         release {
             java.srcDirs = ['src/main/java', 'src/release/java']
         }
    }
}

此外在发布该 library 时,需要指定一些设置,如下:

android {
    // ...
    defaultConfig {
        // ...
        defaultPublishConfig 'release'
        publishNonDefault true
    }
}

说明:

  • defaultPublishConfig ‘release’,默认 library 只会生产 release 下的版本,此版本将会被所有项目使用,通过defaultPublishConfig可以控制默认生产哪个版本的库。
  • publishNonDefault true,默认情况下不能生产所有版本的 library,通过设置publishNonDefault为true,可以同时生产所有版本的 library。 业务组件 module 依赖不同的基础组件生产的 library,如下:
dependencies {
    // ...
    debugCompile project(path: ':baselibrary', configuration: "debug")
    releaseCompile project(path: ':baselibrary', configuration: "release")
}

在使用通过这样的配置脚本解决了多个 APK 包依赖同一组件生产的不同的 library,最终得到我们需要的开发/测试/生产 APK 包。

合并多个 module 到一个文件夹

studio 中的 module 我们在引用时都是用,项目名 + :冒号来表示的

implementation project(':basecomponents')

注意这只是表示我们要引用这个名字的 module 了,而这个 module 的地址这里我们不管

那么就可以理解为 module 的地址可以随我们任意配置,那么在哪里配置 module 的地址呢,答案就是在 setting.gradle 文件里,我们给 ‘:basecomponents’ 这个 module 指定他的地址就行

比如我们想新建一个名字为 components 的文件夹存放我们的组件 module ,组件我们给2个 ( basecomponents,aaa ),然后我们在 setting.gradle 里指定每个项目的文件路径

include ':app', ':basecomponents', ':aaa'
project(':basecomponents').projectDir = new File( 'components/basecomponents' )
project(':aaa').projectDir = new File( 'components/aaa' )

img

一个好的组件化文档是必须的

组件化是 android 开发步入新时代的未来,是代码膨胀,支持快速开发的必然,一个好的组件化文档在现今来看也是必须的了

下面贴个图

img

img

组件化的坑

组件化是好,但是坑也是不少,不好填,尤其是 databinding,dagger,bufferkinft,这是源于 studio 编译的问题。

studio 中 module 虽然是在代码上独立于壳工程的,但是在编译时最后还是要合并到壳工程中的,要不怎么达成一个 APK 文件,要是多个 APK 文件把不成了插件化了嘛,插件化坑更多啊。合并 module 到壳工程就会产生一个根本问题,module 的 R 文件数值改变了。module 的 R文件数据不是固定的,只有壳工程的 R 文件才是常量值,时不变的,module 的 R 文件数值在把 modul 的资源合并到壳工程后才会确定下来,那么这就对依靠编译时注解的技术造成了难题,你指定的 R 路径最后找不到,并且据说这里面还涉及注解的 final ,不了解,看到有人这么说,所以大家在开发组件化时对于带注解技术的框架要多注意,有坑要多看才能爬过去

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/522155.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【牛客小白月赛72】BCD题

B、数数 比赛AC代码&#xff1a; #include <iostream> using namespace std;int t, n; int ans1;int check(int x) {int ans 0;for(int i 1; i < x/i; i)if(x%i 0){ans ;if(i ! x/i) ans;}ans1 ans;return ans1; } int main() {cin>> t;while(t--){cin&…

每日一练 | 华为认证真题练习Day44

1、如Display信息所示&#xff0c;当此交换机需要转发目的IAC地址为5489-98ec-f011的帧时&#xff0c;下面描述正确的是&#xff08;&#xff09;。 A. 交换机将会在除了收到该帧的端口之外的所有端口泛洪该帧 B. 交换机将会发送目标不可达的消息给源设备 C. 交换机在MAC地址…

easyExcel 与 POI 基础知识

文章目录 POI 与 easyExcel一、 了解1.1 Apache POI1.2 easyExcel 二、 准备工作2.1 Maven坐标2.2 Excel讲解 三、 Excel基本写操作&#xff08;导出Excel&#xff09;3.1 03 版本Excel导出操作3.2 07版本Excel导出操作3.3 大数据量的导出&#xff08;数据批量导入到磁盘&#…

含电动汽车的区域综合能源系统优化调度研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

django路由(多应用配置动态路由正则路由)

一、配置全局路由 在应用下&#xff0c;定义视图函数views.py from django.http import HttpResponse from django.shortcuts import render# Create your views here.def get_order(request):return HttpResponse("orders应用下的路由") 在项目的urls路由配置中&…

输入URL到显示界面的整个过程

以如下这个比较简单的网络拓扑模型作为例子&#xff0c;探究中间发生的整个过程&#xff1a; 1 HTTP 浏览器做的第一步工作就是要对 URL 进行解析&#xff0c;从而生成发送给 Web 服务器的请求信息。下图展示了一条长长的URL里各个元素代表什么&#xff1a; 所以整个长长的URL…

〖C++11〗智能指针详解

「前言」文章是关于C11的智能指针方面 「归属专栏」C嘎嘎 「笔者」枫叶先生(fy) 「座右铭」前行路上修真我 「枫叶先生有点文青病」 「每篇一句」 人生就是一列开往坟墓的列车&#xff0c; 路途上会有很多站&#xff0c; 很难有人自始至终陪着你走完。 当陪你的人要下车时&am…

42个网工高效率工具,我只告诉你(二)

晚上好&#xff0c;我是老杨。好用工具上期更新之后&#xff0c;不少小友催我更新下期&#xff0c;这不就来了&#xff1f; 今日文章阅读福利&#xff1a;《42个好用工具下载包》 私信老杨&#xff0c;回复关键词“工具42”&#xff0c;领取2022网工好用工具大全&#xff0c;同…

软件工程师,要么不写代码,要么就写优雅的代码

何为优雅的代码 优雅的代码&#xff0c;至少需要遵循以下几个原则&#xff1a; 遵守规范 优雅的代码&#xff0c;首先让人看起来就是很整洁的。而这种整洁&#xff0c;则来源于代码规范。严格地遵守代码规范&#xff0c;是提高且保证代码质量的最有效方法。从个人开发的角度来看…

【Python入门】Python的判断语句(判断语句的嵌套)

前言 &#x1f4d5;作者简介&#xff1a;热爱跑步的恒川&#xff0c;致力于C/C、Java、Python等多编程语言&#xff0c;热爱跑步&#xff0c;喜爱音乐的一位博主。 &#x1f4d7;本文收录于Python零基础入门系列&#xff0c;本专栏主要内容为Python基础语法、判断、循环语句、函…

Linux高级(shell)

文章目录 一、shell概述Linux 提供的 Shell 解析器有bash 和 sh 的关系Centos 默认的解析器是 bash 二、shell脚本入门脚本格式第一个shell脚本&#xff1a;helloworld.sh 三、变量系统预定义变量自定义变量特殊变量 四、删除变量五、运算符六、条件判断七、流程控制if判断case…

猜谜游戏、彩云词典爬虫、SOCKS5代理的 Go(Golang) 小实践,附带全代码解释

猜谜游戏在编程语言实践都已经和 HelloWord 程序成为必不可少的新手实践环节&#xff0c;毕竟&#xff0c;它能够让我们基本熟悉 for 循环、变量定义、打印、if else 语句等等的使用&#xff0c;当我们基本熟悉该语言基础之后&#xff0c;就要学会其优势方面的程序实践&#xf…

基本类型的比较VS引用类型的比较

基本类型的比较VS引用类型的比较 数据类型的介绍 类型的比较&#xff0c;在Java中有两种数据类型&#xff0c;一种是基本数据类型&#xff0c;一中是引用类型。 基本数据类型包含一些我们常用的数据&#xff1a; 类型类型名称byte字节型int整型char字符型double浮点型boole…

计算机视觉的深度学习 Lecture15:Object Detection 笔记 EECS 498.007/008

一些介绍&#xff1a; amodal box &#xff08;非模态box&#xff1f;&#xff09; 标记隐含的物体大小 只检测一个目标的流程 同时计算分类分数和Box坐标、使用Multitask Loss。Multitask Loss现在是一种非常常见的方法。 滑动窗口检测多个目标 开销巨大&#xff08;右下…

springboot+vue银行OA系统(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的银行OA系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风歌&…

声音合成——Foley Sound——DECASE项目——多模态智能感知与应用——Autoencoder代码实现(6)

文章目录 概述encoder的编写过程代码编写运行结果问题总结 decoder的编写过程知识补充关于逆卷积 代码编写运行结果总结 Autoencoder模型编写 compile方法 train方法 保存和加载模型模块编写实现代码——autoencoder代码实现代码——train代码实现代码——保存和加载模型的代…

【半监督学习】Match系列.3

半监督语义分割旨在利用尽可能少的有标注图像以及大量的无标注图像来学得一个较好的分割模型。其中&#xff0c;对有标注图像的学习一般类似于全监督语义分割&#xff0c;如计算预测结果与人工标注之间的交叉熵损失&#xff0c;问题的关键在于如何利用无标注图像。 本文简单介…

【Unity】读写ProjectSettings、UserSettings、Library文件夹中的文件

【Unity】读写ProjectSettings、UserSettings、Library文件夹中的文件 AssetDatabase 类提供的 LoadAssetAtPath 方法和 CreateAsset 方法只能读写Assets、Packages文件夹中的资产&#xff0c;如果想要读写其他文件夹&#xff08;ProjectSettings、UserSettings、Library等&am…

【运筹优化】元启发式算法详解:模拟退火算法(Simulated Annealing,SA)+ 案例讲解代码实战

文章目录 一、介绍二、基础知识2.1 局部搜索(或蒙特卡罗)算法2.2 Metropolis 算法2.3 模拟退火算法 三、原理3.1 Statistical Equilibrium 统计平衡3.2 Asymptotic Convergence 渐近收敛 四、实际问题4.1 Finite-Time Approximation 有限时间近似4.2 Geometric Cooling 几何冷却…

区块元素和超连结

DIV (区块元素) <div>元素&#xff08;HTML 文件区块元素&#xff09;是无标签语意的容器元素&#xff0c;虽然它不代表任何意义&#xff0c;却是使用最多的标签之一&#xff0c;主要用来把相似或者被划分为同一区块的内容包在同个div 内&#xff0c;以便后续添加css 样…