android maven 组件化
2018-05-28 16:10:29 weixin_34409822 阅读数 40

组件间的通信机制

组件化急需解决的问题就是组件间的通信问题。

EventBus 事件总线

事件总线机制通过记录对象,使用监听者模式来通知对象各种事件。

EventBus的主要功能是替代Intent,Handler,BroadCast,在Fragment,Activity,Service和线程之间传递消息。实现发送者和接收者解耦。订阅者可以订阅多个事件,发送者可以发布任何事件,同时也可以是订阅者。还可以设置事件处理线程和优先级。

ARouter 路由跳转

组件化开发中,可以将各个 module 当作不同的网络,路由器就是连接各个网络的中转站,可以拦截跳转或者设置特定的拦截器。路由跳转无需依赖类,无需在AndroidManifest.xml中注册,适合于组件化解耦。

greenDAO 数据库存储

greenDAO是一个ORM框架,能够提供一个接口,通过操作对象的方式操作关系型数据库。greenDAO的原理是将对象转化为一项数据,实现对象到关系数据库的映射。

AndPermission 权限管理

AndPermission对于国内手机的适配较好。具体可以访问对应的GitHub。可以将AndPermission和ARouter结合,在跳转之前进行权限的校验。

R.java 资源限制

R.java文件是 aapt 打包资源文件时生成的。

组件 module 中的静态变量并没有被赋予final属性。对应文件位置为:/build/generated/source/r/debug/包名/R.java。

可以看到每个资源文件在R中都是一个class,每个资源项名称都分配了一个id,id值是一个四字节无符号整数,格式是这样的:0xpptteeee,(p代表的是package,t代表的是type,e代表的是entry),最高字节代表Package ID,次高字节代表Type ID,后面两个字节代表Entry ID。

Package ID相当于是一个命名空间,限定资源的来源。Android系统当前定义了两个资源命令空间,其中一个系统资源命令空间,它的Package ID等于0x01,另外一个是应用程序资源命令空间,它的Package ID等于0x7f。所有位于[0x01, 0x7f]之间的Package ID都是合法的,而在这个范围之外的都是非法的Package ID。

Type ID是指资源的类型ID。资源的类型有animator、anim、color、drawable、layout、menu、raw、string和xml等等若干种,每一种都会被赋予一个ID。

Entry ID是指每一个资源在其所属的资源类型中所出现的次序。注意,不同类型的资源的Entry ID有可能是相同的,但是由于它们的类型不同,我们仍然可以通过其资源ID来区别开来。

各个 module 会生成.aar文件,并且被引用到 app module中,最终合并为apk文件。当各个 module 被解压到 app module 时,R.java中的id属性会被添加声明为final属性。对应文件位置为:/build/generated/source/r/debug/包名/R.java

当各个组件 module 被汇总到 app module时,也就是编译初期解析资源阶段,会检测所有的R.java文件,然后通过合并,最终生成唯一的一份R.java资源文件。

由于组件 module 中的R.java没有final关键字修饰id,所以在必须使用常量的地方都不能使用R.java中的变量。包括注解和switch语法等。

每个组件 module各自生成aar文件的时候,aapt会单独生成R.java文件,然后在app module中执行java compile命令编译class文件时会将常量变化成具体的值。

ButterKnife使用替换的方法,原理是将R.java文件复制一份,命名为R2.java,然后再R2.java中为所有变量加上final修饰符,在相关的地方使用R2资源。

资源冲突

组件化中,被依赖的 module 会被编译成aar文件,然后解压到依赖的 module中,最终汇集到 app module中。此时会通过gradle的机制防止出现重复的aar文件。不添加重复的库资源到编译中。我们有时候会遇到在被依赖的库中出现两个重复的库,此时默认会使用版本较高的依赖。若果想使用版本较低的依赖,可以使用exclude方式排除。

对于资源名重复的情况,会出现资源引用出错的情况。后编译的模块会覆盖之前编译的模块的资源字段的内容。

混淆

Android Studio使用ProGuard进行混淆,可以压缩,优化和混淆Java字节码文件,压缩和优化体现在可以删除无用的类,字段,方法,属性和注解,还可以删除无用的注解,优化字节码文件。还可以使用简短无意义的名称来重命名已经存在的类,字段,方法和属性。

minifyEnabled true:打开混淆

shrinkResource true:打开资源压缩

proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro':设置proguard文件的路径

混淆之后为了根据出错的信息找回原本出错的地方可以到对应文件下找对应的文件:/build/outputs/mapping/client/release/

dump.txt:包含目录内所有class文件的结构 mapping.txt:记录混淆后路径和原路径的映射关系。 resources.txt:记录资源文件信息。 seeds.txt:未混淆的类和成员,验证是否持有不想混淆的类。 usage.txt:累出apk被删除的代码。

微信用来提供混淆机制。

在每个module创建之后都会有自己的proguard文件,每个proguard文件可以有自己的混淆规则。如果每个module都使用自己的混淆规则就会造成重复混淆,造成定位不到源文件的问题。可以在app module中设置所有混淆操作来避免。

多渠道打包

Android Studio提供了多渠道模块配置

productFlavors属性用于设置不同的渠道

manifestPlaceholders属性用于设置不同渠道中的不同属性,这些属性需要在AndroidManifest.xml文件中声明才能使用。

设置xxxCompile用来配置不同渠道需要引用的module文件。

    /**
     * 获取meta-data值
     * @param context
     * @param metatName  key名
     * @return
     */
    public static Object getMetaData(Context context, String metatName){
        Object obj= null;
        try {
            if (context !=null){
                String pkgName = context.getPackageName();
                ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(pkgName, PackageManager.GET_META_DATA);
                obj = appInfo.metaData.get(metatName);
            }
        }catch (Exception e){
            Log.e("AppMetaUtil",e.toString());
        }finally {
            return obj;
        }
    }

    /**
     * 获取渠道号
     * @param context
     * @return
     */
    public static int getChannelNum(Context context){
        if (channelNum <= 0){
            Object obj = AppMetaUtil.getMetaData(context,"channel");
            if (obj!=null && obj instanceof Integer){
                return (int)obj;
            }
        }
        return channelNum;
    }

    /**
     * 获取设置信息类路径
     * @param context
     * @return
     */
    public static String getSettingInfo(Context context){
        if (settingInfo ==null){
            Object obj = AppMetaUtil.getMetaData(context,"setting_info");
            if (obj!=null && obj instanceof Integer){
                return (String)obj;
            }
        }
        return settingInfo;
    }
复制代码
2018-05-28 13:17:40 weixin_34387468 阅读数 44

组件化是什么

项目开发的时候,我们一般会将公用的代码提取出来封装成基础库 Base module,作为工具类库。然后将实现某些功能的代码封装到功能库 Library module。最后根据业务来划分 module,这样就可以让不同的人开发不同的模块,实现并行开发。 随着以上安排的发展最终我们的项目将演化为多 module 划分业务和基础功能

组件:实现单一功能的 module。如推送组件(PushSDK),支付组件(PaySDK)类似。

模块:独立的业务 module。登陆模块(LoginModule),首页模块(HomeModule)类似。

模块化和组件化都是为了代码的重用和解耦。但是由于每个项目的不同,所以可能会出现已经组件化的 module 在新项目中有部分功能没有使用,造成代码的冗余,随着项目越来越大,造成65535方法数的问题,这时需要选择MultiDex或者使用插件化方式开发项目。

如何实现组件化

Android Studio为我们提供了实现组件化的设计 —— module 依赖。通过依赖我们可以访问被依赖 module 的代码和资源。

如图所示,在项目的Project Structure中,提供了基本的三种依赖方式:

(1)Library dependency:根据设置的仓库来完成仓库索引的依赖。可以设置为网络和本地。

(2)Jar dependency:通过引入libs文件夹中所有的.jar和.aar后缀文件来进行依赖。

(3)Module dependency:使用基础模块库(lib module)作为依赖。实质上也是将其打包为.aar文件进行依赖。

这些操作都会同步到配置的build.gradle文件中。

我们在创建新的 module 时都会生成对应的AndroidManifest.xml文件用于记载模块的信息。而最终合成的APK中只存在一份AndroidManifest.xml,所以Android Studio将多个AndroidManifest.xml合并为一个。一般都是以一个app module 搭配多个业务 module 组成我们的项目。最终生成的目录为app/build/intermediates/manifests/full/debug/AndroidManifest.xml,它记录了所有模块的信息。这个过程包括了资源的汇总和解决冲突。

(1)资源汇总:每个 module 在编译的时候都会在模块的当前目录/build/outputs/aar下生成对应的.aar文件。当编译app module的时候会将其依赖的每个module重新编译,将生成的.aar文件放在app module的app/build/intermediates/exploded-aar文件夹中(因为exploded-aar文件夹在新版AS中添加了缓存,需要在gradle.properties文件中添加android.enableBuildCache = false才能显示)。

其中 class.jar文件是 module 的代码包。可以看到对应的AndroidManifest.xml文件。 (2)资源变更:在每个 module 注册的Activity在汇总的时候会补全包名+文件名。否则会找不到对应的Activity。四大组件都一样。如果 module 中有多个替换了Application,那么替换规则:解决冲突后,app module 的优先级大于功能 module。权限会被汇总到app module中,且不会重复。Activity的主题会引用最后合并的AndroidManifest.xml中声明的主题,要是没有声明,默认使用Android自带的。通过声明相同的sharedUserId的多个APP可以运行在同一个进程中,可以访问各自的数据。只有在app module中声明才会被最终打包到AndroidManifest.xml中。

Application

Application提供了很多有用的回调方法。

(1)onCreate():初始化操作。

(2)onTrimMemory():在系统内存不足,所有后台程序(优先级为background的进程,不是指后台运行的进程)都被杀死时,系统会调用OnLowMemory。

(3)onLowMemory():Android 4.0之后提供的API,系统会根据不同的内存状态来回调。

(4)onConfigurationChanged(Configuration newConfig):当设备的信息改变的时候将会调用,某些时候你需要在系统一些配置信息改变的时候activity避免重新onCreate(),你就可以在android:configChanges声明,某个属性已经声明了,那么在这个属性的信息改变的时候就只会回调onConfigurationChanged(Configuration newConfig)方法。

(5)registerActivityLifecycleCallbacks():APP内所有Activity的生命周期监听。全局Toast可以引用Application的context对象。全局弹框就需要引用此方法回调函数返回的当前窗口对象的顶层的Activity。

2017-07-14 16:07:37 EthanCo 阅读数 320

随着时间的推移,项目变得越来越臃肿,随便修改一些东西,重新编译一次,也要几分钟,极大影响开发效率。
故在一个月前,决定引入组件化,来缓解这种情况。
先对组件化的优势和主要的问题做下记录。

Router框架

Intent intent = new Intent(mContext, XxxActivity.class);
intent.putExtra("key","value"); 
startActivity(intent); 

在Android开发中,是最常用的功能就是页面的跳转,先排除从浏览器或者其他App跳转到自己App中页面的需求,仅仅是简简单单的页面跳转,随着时间的推移,也会遇到一些问题:

  • 集中式的URL管理
    • 谈到集中式的管理,总是比较蛋疼,多人协同开发的时候,大家都去AndroidManifest.xml中定义各种IntentFilter,使用隐式Intent,最终发现AndroidManifest.xml中充斥着各种Schame,各种Path,需要经常解决Path重叠覆盖、过多的Activity被导出,引发安全风险等问题
  • 可配置性较差
    • Manifest限制于xml格式,书写麻烦,配置复杂,可以自定义的东西也较少
  • 跳转过程中无法插手
    • 直接通过Intent的方式跳转,跳转过程开发者无法干预,一些面向切面的事情难以实施,比方说登录、埋点这种非常通用的逻辑,在每个子页面中判断又很不合理,毕竟activity已经实例化了
  • 跨模块无法显式依赖
    • 在App小有规模的时候,我们会对App做水平拆分,按照业务拆分成多个子模块,之间完全解耦,通过打包流程控制App功能,这样方便应对大团队多人协作,互相逻辑不干扰,这时候只能依赖隐式Intent跳转,书写麻烦,成功与否难以控制。

而Router框架很好地解决了以上问题,比如ARouter。

ARouter路由框架

  • 支持通过URL进行跳转,并自动注入参数到目标页面
  • 支持多模块工程使用
  • 支持添加多个拦截器,自定义拦截顺序
  • 支持依赖注入,可单独作为依赖注入框架使用
  • 等等…

详见 传送门

组件化

随着时间的推移,项目变得越来越臃肿,随便修改一些东西,重新编译一次,也要几分钟,极大影响开发效率。

使用组件化可实现

  • 模块间进一步解耦,所有的基础公共库按需进行依赖,各个项目可共用
  • 开发阶段仅对单个模块进行运行和调试,提高开发效率
  • 减少协同开发中的冲突,提高开发效率
  • 可自由选择开发姿势(MVC/MVP/MVVM等)
  • 可进行针对性的的测试(仅需对修改过的模块进行测试)
  • 实现各模块的配置和组装,当一个功能不需要的时候,随时可去掉,当又需要该功能的时候,随时又可加上。

而使用Router框架是实现组件化的一个很好的方式。

公司项目组件

最终,把公司项目拆分为如下结构,各个业务模块之间可以独立运行,亦可组合在一起打Release包。

这里写图片描述

使用组件化的注意事项

Debug和Release模式下,Manifest不同

使用两个Manifest,在Debug模式下,使用Debug路径下的Manifest,Release模式下,使用Release的manifest。

这里写图片描述

在Module的gradle文件中,进行配置

sourceSets {
        main {
            if (rootProject.ext.is_build_module) {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
                java.srcDirs += "src/main/debug/java/";
            } else {
                manifest.srcFile 'src/main/release/AndroidManifest.xml'
                java.srcDirs += "src/main/release/java/";
            }
        }
    }

资源ID冲突

合并多个组件到主程序,资源名相同时,会引起资源冲突。比较简单的解决方式是通过配置资源前缀名resourcePrefix来避免。配置了resourcePrefix后,该组件所有的资源都需要添加这个前缀才能编译通过。(但是 resourcePrefix 这个值只能限定 xml 里面的资源,并不能限定图片资源,所有图片资源仍然需要你手动去修改资源名)

android {
    resourcePrefix "news_"
}  

初始化

Debug模式下,可以配置组件自己的Application,进行初始化一些类库,但是在Release模式下,就无法做到一个组件一个Application了,一个较好的解决方案是使用ARouter的拦截器,ARouter拦截器的init方法,会在程序启动的时候,进行调用,并且会传入Context对象。

@Interceptor(priority = 8)
public class TestInterceptor implements IInterceptor {
    @Override
    public void init(Context context) {

    }
}

Demo程序

ModularizationTest

2018-07-13 17:02:43 T_yoo_csdn 阅读数 57

1. 1. 在项目中配置config.gradle文件

ext {
    isModule = true
    isUserSingleModule = true
。。。
}

1.2. 某一个模块的build.gradle文件中

if (rootProject.ext.isUserModule) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}
defaultConfig {
        if (rootProject.ext.isUserModule) {
            applicationId rootProject.ext.android.applicationId
        }
。。。}

 

sourceSets {
        main {
            if (rootProject.ext.isUserModule) {
                manifest.srcFile 'src/main/module/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/AndroidManifest.xml'
                java {//排除java/debug文件夹下的所有文件
                    exclude '*module'
                }
            }
        }
    }

在主app的build.gradle中

if(!rootProject.ext.isModule){
        implementation project(':bundle_user')
    }

2.1. (在Android项目中的任何一个build.gradle文件中都可以把gradle.properties中的常量读取出来)在gradle.properties中

然后我们在业务组件的build.gradle中读取 isModule,但是 gradle.properties 还有一个重要属性: gradle.properties 中的数据类型都是String类型,使用其他数据类型需要自行转换;也就是说我们读到 isModule 是个String类型的值,而我们需要的是Boolean值,代码如下:

# 每次更改“isModule”的值后,需要点击 "Sync Project" 按钮
isModule=false
if (isModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
}

参考:https://blog.csdn.net/guiying712/article/details/55213884


组件之间通讯

ARouter

ARouter 核心实现思路是,我们在代码里加入的@Route注解,会在编译时期通过apt生成一些存储path和activityClass映射关系的类文件,然后app进程启动的时候会拿到这些类文件,把保存这些映射关系的数据读到内存里(保存在map里),然后在进行路由跳转的时候,通过build()方法传入要到达页面的路由地址,ARouter会通过它自己存储的路由表找到路由地址对应的Activity.class(activity.class = map.get(path)),然后new Intent(),当调用ARouter的withString()方法它的内部会调用intent.putExtra(String name, String value),调用navigation()方法,它的内部会调用startActivity(intent)进行跳转,这样便可以实现两个相互没有依赖的module顺利的启动对方的Activity了。

2018-04-25 13:56:25 Liujiayut800 阅读数 73

一、背景。

当项目逐渐变大时,每次做一点小小的改动都需要重新编译整个App。组件化的产生就是为隔离各个module,使其单独运行,实现组件间解耦,单独调试

二、多模块。

组件化之前,需要先将应用中的各个业务组件拆解成aar。形成app+lib的形式。这是组件化的第一步,所谓组件化就是让各个模块可以自由切换成application/library。


app即为整个应用的外壳子。lib1和lib2是两个业务模块。base是所有整个应用的基础组件。

可能会有疑问,当lib1想要调用lib2中的内容的时候该怎么处理呢,这个后面会说,先来看这一步。

要想自由切换每个lib的类型到底是library还是application,先修改build.gradle文件。先在project目录下新建一个.gradle文件,这个.gradle用来记录所有的build.gradle文件的配置信息,然后在project下的build.gradle文件下引入这个.gradle文件。

apply from:"config.gradle"//apply这个函数后面要传入的是一个Map<String, ?> options,当key为from时,就会去寻找这个文件。平时我们看到的key大多是plugin。

举个简单的例子,我的config.gradle文件的内容如下:

ext {
    ddgjversion = [
            compileSdkVersion: 26,
            minSdkVersion    : 16,
            targetSdkVersion : 26,
            buildToolsVersion: "26.0.2",
            versionCode      : 360,
            versionName      : "3.6"
    ]
    ddgId = ["app"  : "com.*****.***",
             "lib": "com.*****.***",
             ]
}

引用:

def cfg = rootProject.ext.ddgjversion
def ddgjid = rootProject.ext.ddgjId
......
compileSdkVersion cfg.compileSdkVersion
    buildToolsVersion cfg.buildToolsVersion

    defaultConfig {
        applicationId ddgjId["app"]
        minSdkVersion cfg.minSdkVersion
        targetSdkVersion cfg.targetSdkVersion
        versionCode cfg.versionCode
        versionName cfg.versionName
        multiDexEnabled true
        ndk {
            abiFilters "armeabi", "x86"
        }
......










Android组件化

阅读数 20

后期我会面试50家互联网公司,不断总结完善和思考,写出一本完美的安卓面试书籍,大家支持我吗?1、组件化和插件化区别:1.1组件化开发:(1)、组件化是将一个app分成多个Module,每个Module都是一个组件;(2)、组件化在发布的时候,所有组件以lib的形式被主app工程依赖,并打包成1个apk,不支持动态加载。插件化开发:(1)、插件化...

博文 来自: weixin_34205076

android 组件化

阅读数 1

一、模块化、组件化与插件化项目发展到一定程度,随着人员的增多,代码越来越臃肿,这时候就必须进行模块化的拆分。在我看来,模块化是一种指导理念,其核心思想就是分而治之、降低耦合。而在Android工程中如何实施,目前有两种途径,也是两大流派,一个是组件化,一个是插件化。提起组件化和插件化的区别,有一个很形象的图:组件化和插件化对比.png上面的图看上去比较清...

博文 来自: weixin_33698043

Android 组件化

阅读数 19

前言为什么要组件化?app业务迭代,模块越来越多,代码量超10万是很正常的,代码混杂在一起,对工程所做的任何修改都必须要编译整个工程,维护成本越来越高,因此就需要进行模块化,组件化Android工程的组件一般分为两种,lib组件和application组件。lib组件是指该组件属于app的一部分,可以供其它组件使用但是本身不能打包成apk;appl...

博文 来自: weixin_33849215

android组件化

阅读数 94

参考:总结一波安卓组件化开源方案Android组件化方案

博文 来自: yu75567218

android 组件化

阅读数 20

Android组件化项目地址:Android组件化项目AndroidModulePatternAndroid组件化之终极方案地址:http://blog.csdn.net/guiying712/article/details/780571201为什么要项目组件化2如何组件化3组件化实施流程1组件模式和集成模式的转换2组件之间AndroidManifest合...

博文 来自: weixin_34208185
没有更多推荐了,返回首页