什么是组件化

为了降低项目耦合性,在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. [实战演练]Intel面试题目 - 进栈出栈顺序问题

    电话面试中写C++,逻辑比较清楚的一个题目,一紧张就不能好好地写下来,漏洞百出.以前经常在完善的编译环境中写代码,换了一个白板子上写反而写的不通顺了,犯了一些基础错误,比如stack中的首个元素是to ...

  2. 564. Find the Closest Palindrome

    Given an integer n, find the closest integer (not including itself), which is a palindrome. The 'clo ...

  3. Swift5 语言参考(一) 关于语言参考

    本系列文章的这一部分描述了Swift编程语言的形式语法.此处描述的语法旨在帮助您更详细地理解语言,而不是允许您直接实现解析器或编译器. Swift语言相对较小,因为Swift代码中几乎无处不在的许多常 ...

  4. 人工智能_机器学习——pandas - 箱型图

    箱型图对数据的展示也是非常清晰的,这是箱型图的一些代码 #导报 机器学习三剑客 import numpy as np import pandas as pd from matplotlib impor ...

  5. 配置django图片上传与保存展示

    近来在研究django,发现有好多好玩的功能,比如图片上传,以前处理这个比较麻烦,现在我们来看看如何来处理图片上传与保存 1.在数据库设计的时候需要配置upload_to image = models ...

  6. jq04--jq与ajax

    之前我们学习了一些有关jq函数的知识,现在我们看看jq与ajax方面的一些东西: 1.ajax(Asynchronous JavaScript and XML 异步的JavaScript与xml):  ...

  7. Java代码操作HDFS测试类

    1.Java代码操作HDFS需要用到Jar包和Java类 Jar包: hadoop-common-2.6.0.jar和hadoop-hdfs-2.6.0.jar Java类: java.net.URL ...

  8. Apple Pay 支付集成

    Refer:https://open.unionpay.com/ajweb/product/detail?id=80 交易步骤: 1.浏览并选购商品:用户通过手机客户端与商户系统交互浏览选购商品,客户 ...

  9. StreamSets学习系列之启动StreamSets时出现Caused by: java.security.AccessControlException: access denied ("java.util.PropertyPermission" "test.to.ensure.security.is.configured.correctly" "read")错误的解决办法

    不多说,直接上干货! 问题详情 [hadoop@master streamsets-datacollector-]$ ./bin/streamsets dc Java 1.8 detected; ad ...

  10. 全网最详细的CentOS7里安装MySQL时出现No package mysql-server available错误的解决办法(图文详解)

    不多说,直接上干货! 直接yum install mysql的话会报错,原因在于yum安装库里没有直接可以用的安装包,此时需要用到MariaDB了,MariaDB是MySQL社区开发的分支,也是一个增 ...