什么是组件化

为了降低项目耦合性,在Android工程中如何实施,目前有两种途径,也是两大流派,一个是组件化,一个是插件化。在这里只介绍组件化,插件化暂不介绍

正常的APP只有一个application的module和多个libary的module,而组件化开发是为了多个module都可以是application单独运行,发布时在合并。

代码解耦

APP:作为主的application的入口,主要作用是了初始化项目的第三方库、欢迎页和主页。

组件:拆分的业务模块,这种module是一个完整的功能模块,组件可以有多个,具体划分根据业务情况进行划分。

公用库:基础库library,这些代码被其他组件直接引用,也可以创建一个module专门放资源的库。

模块通信

目前使用的是阿里的ARouter进行模块的通信,使用URL进行跳转,达到主项目和组件、组件与组件之间不能直接使用类的相互引用来进行数据交互效果。详细使用,请自行查看github项目或者google,这里只介绍简单介绍:

1、添加注解
// 在支持路由的页面上添加注解(必选)
// 这里的路径需要注意的是至少需要有两级,/xx/xx
@Route(path = "/test/activity")
public class YourActivity extend Activity {
...
} 2、初始化SDK
if (isDebug()) { // 这两行必须写在init之前,否则这些配置在init过程中将无效
ARouter.openLog(); // 打印日志
ARouter.openDebug(); // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险)
}
ARouter.init(mApplication); // 尽可能早,推荐在Application中初始化 3、发起路由操作
ARouter.getInstance().build("/test/activity").navigation();
详细使用

1、在global.gradle定义一个参数

ext {
isLibrary = true //false:application;true:library
}

注:global.gradle是全局定义版本库文件

2、在各业务组件module的build.gradle

if (rootProject.ext.isLibrary) {
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
} android {
defaultConfig {
if (!rootProject.ext.isLibrary) {
applicationId "自己的applicationId"
}
} sourceSets {
main {
if (rootProject.ext.isLibrary) {
manifest.srcFile 'src/main/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
}
}
}
}

3、当业务module当成一个library,需要实现原先Application类中功能,可以使用委托方式实现。

a、先定义一个IModuleConfig接口,主要实现app生命周期的管理和activity回收管理

public interface IModuleConfig {
void injectAppLifecycle(Context context, List<IAppLife> lifeList); void injectActivityLifecycle(Context context, List<Application.ActivityLifecycleCallbacks> callbacksList); }

b、定义IAppLife接口,实现Application相关类功能

public interface IAppLife {
void attachBaseContext(Context base); void onCreate(BaseApp application); void onTerminate(BaseApp application); void onTrimMemory(int level); void onLowMemory();
}

c、在业务module的library里的AndroidManifest.xml配置IModuleConfig

    <meta-data
android:name="业务模块包名的实现Application类"
android:value="IModuleConfig" />

d、解析配置IModuleConfig业务module的Application类

public final class ManifestParser {
private static final String MODULE_VALUE = "IModuleConfig"; private final Context context; public ManifestParser(Context context) {
this.context = context;
} public List<IModuleConfig> parse() {
List<IModuleConfig> modules = new ArrayList<>();
try {
ApplicationInfo appInfo = context.getPackageManager().getApplicationInfo(context.getPackageName(), PackageManager.GET_META_DATA);
if (appInfo.metaData != null) {
for (String key : appInfo.metaData.keySet()) {
if (MODULE_VALUE.equals(appInfo.metaData.get(key))) {
modules.add(parseModule(key));
}
}
}
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException("解析Application失败", e);
} return modules;
} private static IModuleConfig parseModule(String className) {
Class<?> clazz;
try {
clazz = Class.forName(className);
} catch (ClassNotFoundException e) {
throw new IllegalArgumentException(e);
} Object module;
try {
module = clazz.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
return (IModuleConfig) module;
}
}

e、实现IAppLife的具体类

public class ApplicationDelegate implements IAppLife {
private List<IModuleConfig> configList;
private List<IAppLife> appLifeList;
private List<Application.ActivityLifecycleCallbacks> callbacksList; public ApplicationDelegate() {
appLifeList = new ArrayList<>();
callbacksList = new ArrayList<>();
} @Override
public void attachBaseContext(Context base) {
ManifestParser manifestParser = new ManifestParser(base);
configList = manifestParser.parse();
if (configList != null && configList.size() > 0) {
for (IModuleConfig configModule : configList) {
configModule.injectAppLifecycle(base, appLifeList);
configModule.injectActivityLifecycle(base, callbacksList);
}
}
if (appLifeList != null && appLifeList.size() > 0) {
for (IAppLife life : appLifeList) {
life.attachBaseContext(base);
}
}
} @Override
public void onCreate(BaseApp application) {
Ln.d("ApplicationDelegate onCreate:%s", appLifeList.size());
if (appLifeList != null && appLifeList.size() > 0) {
for (IAppLife life : appLifeList) {
life.onCreate(application);
}
}
if (callbacksList != null && callbacksList.size() > 0) {
for (Application.ActivityLifecycleCallbacks life : callbacksList) {
application.registerActivityLifecycleCallbacks(life);
}
}
} @Override
public void onTerminate(BaseApp application) {
if (appLifeList != null && appLifeList.size() > 0) {
for (IAppLife life : appLifeList) {
life.onTerminate(application);
}
}
if (callbacksList != null && callbacksList.size() > 0) {
for (Application.ActivityLifecycleCallbacks life : callbacksList) {
application.unregisterActivityLifecycleCallbacks(life);
}
}
} @Override
public void onTrimMemory(int level) {
if (appLifeList != null && appLifeList.size() > 0) {
for (IAppLife life : appLifeList) {
life.onTrimMemory(level);
}
}
} @Override
public void onLowMemory() {
if (appLifeList != null && appLifeList.size() > 0) {
for (IAppLife life : appLifeList) {
life.onLowMemory();
}
}
}
}

f、公用的Application类

public class BaseApplication extends BaseApp {
private static BaseApplication instance;
private ApplicationDelegate applicationDelegate; public static BaseApplication getInstance() {
return instance;
} @Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
Ln.init(this);
MultiDex.install(this);
applicationDelegate = new ApplicationDelegate();
applicationDelegate.attachBaseContext(base);
} @Override
public void onCreate() {
super.onCreate();
if (!HostUtils.isInMainProcess(this)) return;
// 这两行必须写在init之前,否则这些配置在init过程中将无效
ARouter.openLog(); // 打印日志
Ln.d("BaseApplication:%s", isDebug);
ARouter.openDebug();
ARouter.init(this);
instance = this;
applicationDelegate.onCreate(this);
Ln.d("BaseApplication applicationDelegate.");
} @Override
public void onTerminate() {
super.onTerminate();
applicationDelegate.onTerminate(this);
} @Override
public void onLowMemory() {
super.onLowMemory();
applicationDelegate.onLowMemory();
} @Override
public void onTrimMemory(int level) {
super.onTrimMemory(level);
applicationDelegate.onTrimMemory(level);
}
}

注:module1 和 module2 分别都依赖了 common,不会导致 library 重复依赖。

module为library的填坑

1、资源ID报无法访问的错误,因为在library中,资源ID是一个变量,而不是常量,在switch中、ButterKnife、注解使用资源ID都会提示错误。switch报错解决办法是用if-else,而注解解决办法使用string参数,注解实现类中根据这个string参数读取资源。

2、使用Dagger2后,无法自动生成相关的Dagger类,解决办法:

定义一个注解
@Scope
@Retention(RetentionPolicy.RUNTIME)
public @interface PerApplication {
} 在Dagger接口类上加上注解
@PerApplication
@Component(modules = {NetworkModule.class})
@Singleton
public interface AppComponent { }

3、资源冲突问题,当分别开发模块时,容易出资源重复命名的问题,可以在build.gradle中设置

resourcePrefix "module1_"

Android组件化搭建的更多相关文章

  1. Android 组件化/模块化之路——在展示层搭建MVP结构

    Android 组件化/模块化之路——在展示层搭建MVP结构 什么是MVP Model–View–Presenter (MVP) 源于 Model–View–Controller (MVC) 的结构设 ...

  2. 从零开始搭建Android组件化框架

    问题 在已经开发过几个项目的童鞋,如果这时需要重新开发一个新项目,是否需要自己重新搭建框架呢,还是从老项目中拷贝粘贴? 我们是否可以封装一个底层的lib库,这个底层的公共基础库 包括了一些第三方库(如 ...

  3. Android组件化框架设计与实践

    在目前移动互联网时代,每个 APP 就是流量入口,与过去 PC Web 浏览器时代不同的是,APP 的体验与迭代速度影响着用户的粘性,这同时也对从事移动开发人员提出更高要求,进而移动端框架也层出不穷. ...

  4. Android组件化之终极方案

    Android组件化项目地址:Android组件化项目AndroidModulePattern Fragment或View如何支持组件化 如何管理组件 Fragment或View如何支持组件化 距离 ...

  5. Android组件化方案

    Android组件化项目地址:Android组件化项目AndroidModulePattern Android组件化之终极方案地址:http://blog.csdn.net/guiying712/ar ...

  6. Android组件化

    附:Android组件化和插件化开发 App组件化与业务拆分那些事 Android项目架构之业务组件化 Android组件化核心之路由实现 Android组件化开发实践

  7. 我所理解的Android组件化之通信机制

    之前写过一篇关于Android组件化的文章,<Android组件化框架设计与实践>,之前没看过的小伙伴可以先点击阅读.那篇文章是从实战中进行总结得来,是公司的一个真实项目进行组件化架构改造 ...

  8. Android组件化demo实现以及遇坑分享

    首先贴出demo的github地址:GitHub - TenzLiu/TenzModuleDemo: android组件化demo 作者:TenzLiu原文链接:https://www.jianshu ...

  9. Gradle自动实现Android组件化模块构建

    背景 随着App的不断迭代,业务会变得越来越复杂,业务模块会越来越多,且每个模块的代码也会变得越来越多.为了应对这一场景,我们需要把不同的业务模块划分成一个个组件,在修改业务代码的时候只需要在对应模块 ...

随机推荐

  1. Code Chef April Cook-Off 2019题解

    传送门 \(PEWDSVTS\) 我哪根筋不对了要把所有可行的拿出来\(sort\)一下--还有忘开\(long\ long\)真的好难受-- int main(){ // freopen(" ...

  2. Docker Compose模板文件介绍

    模板文件是使用 Compose 的核心,涉及到的指令关键字也比较多,这里面大部分指令跟 docker run 相关参数的含义都是类似的.默认的模板文件名称为 docker-compose.yml ,格 ...

  3. Linux巩固记录(4) 运行hadoop 2.7.4自带demo程序验证环境

    本节主要使用hadoop自带的程序运行demo来确认环境是否正常 1.首先创建一个input.txt文件,里面任意输入些单词,有部分重复单词 2.将input文件拷贝到hdfs 3.执行hadoop程 ...

  4. 【dpdk】使用libpcap-PMD驱动收发包

    ref: Dpdk programmer’s guide 1.  概述 dpdk不仅提供针对物理和虚拟网卡的pmd驱动(Poll Mode Drivers),还提供两个纯软件的pmd驱动,libpca ...

  5. django~项目的文件位置的重要性

    前几天我犯了个很低级的错误 就是把文件的地址放错地方了~~ 我把templates文件放进mysite文件里面了 和templatetags文件同级了  所以一直报错  说找不到模板的文件 实际上te ...

  6. 好用的在线工具汇总:Iconfont图标,数据mock,时间函数库,颜色查询 等

    一   时间函数库 ———http://momentjs.com/ 非常全的时间处理函数库,引入使用非常方便. 二   Iconfont———http://www.iconfont.cn/ 各种小图标 ...

  7. 课程一(Neural Networks and Deep Learning),第一周(Introduction to Deep Learning)—— 0、学习目标

    1. Understand the major trends driving the rise of deep learning.2. Be able to explain how deep lear ...

  8. Math、Random、System、BigInteger、Date、DateFormat、Calendar类,正则表达式_DAY14

    1:Math&大数据类四则运算 X abs(X x) double random()         产生随机数 double ceil(double a)   向上取整 double flo ...

  9. Apater适配器模式(结构型模式)

    1.概要 适配:即在不改变原有实现的基础上,将原先不适合的接口转换成适合的接口. what is Apater?适配,这个概念在生活中无处不在,比如你的iphone 4手机充电器坏了,这是时候只有一个 ...

  10. Intellij IDEA 环境配置与使用

    Intellij IDEA 是我感觉最牛X的IDE开发工具,没有之一! 先share一篇教程: http://pan.baidu.com/s/1i3fzJff 调整字体 设置默认的JDK 显示行号 版 ...