您当前的位置:首页 > 电脑百科 > 程序开发 > 移动端 > Android

Android组件化开发思想与实践

时间:2020-05-12 11:56:15  来源:  作者:

作者:popular_linda

链接:https://juejin.im/post/5eb019b8e51d45338806f2c0

 

什么是组件

项目按功能拆分成功若干个组件,每个组件负责相应的功能,如login、pay、live。组件化与模块化类似,但不同的是模块化是以业务为导向,组件化是以功能为导向。组件化的颗粒度更细,一个模块里可能包含多个组件。实际开发中一般是模块化与组件化相结合的方式。

 

为什么要组件

(1)提高复用性避免重复造轮子,不同的项目可以共用同一组件,提高开发效率,降低维护成本。

(2)项目按功能拆分成组件,组件之间做到低耦合、高内聚,有利于代码维护,某个组件需要改动,不会影响到其他组件。

组件化方案

组件化是一种思想,团队在使用组件化的过程中不必拘泥于形式,可以根据自己负责的项目大小和业务需求的需要制定合适的方案,如下图就是一种组件化结构设计

Android组件化开发思想与实践
  • 宿主App

    在组件化中,app可以认为是一个入口,一个宿主空壳,负责生成app和加载初始化操作。

  • 业务层

    每个模块代表了一个业务,模块之间相互隔离解耦,方便维护和复用。

  • 公共层

    既然是base,顾名思义,这里面包含了公共的类库。如Basexxx、Arouter、ButterKnife、工具类等

  • 基础层

    提供基础服务功能,如图片加载、网络、数据库、视频播放、直播等。

注:以上结构只是示例,其中层级的划分和层级命名并不是定性的,只为更好的理解组件化。

组件化面临的五问题

 

一,跳转和路由

Activity跳转分为显示和隐示:

//显示跳转Intent intent = new Intent(cotext,LoginActivity.class);startActvity(intent)

 
//隐示跳转Intent intent = new Intent;intent.setClassName("app包名" , "activity路径");intent.setComponent(new Component(new Component("app报名" , "activity路径")));startActivity(intent);
 

1、显示跳转,直接依赖,不符合组件化解耦隔离的要求。

2、对于隐示跳转,如果移除B的话,那么在A进行跳转时就会出现异常崩溃,我们通过下面的方式来进行安全处理

//隐示跳转Intent intent = new Intent;intent.setClassName("app包名" , "activity路径");intent.setComponent(new Component(new Component("app报名" , "activity路径")));if (intent.resolveActivity(getPackageManager) != ) { startActivity(intent);}startActivity(intent);

原生推荐使用隐示跳转,不过在组件化项目中,为了更优雅的实现组件间的页面跳转可以结合路由神器ARouter,ARouter类似中转站通过索引的方式无需依赖,达到了组件间解耦的目的。

Aouter使用方式如下:

1、因为ARouter是所有模块层组件都会用到所以我们可以在Base中引入

api 'com.alibaba:arouter-api:1.5.0'annotationProcessor 'com.alibaba:arouter-compiler:1.2.2'

2、在每个子module里添加

Android { defaultConfig { ... JAVACompileOptions { annotationProcessorOptions { arguments = [AROUTER_MODULE_NAME: project.getName()] } } }}

annotationProcessor会通过javaCompileOptions这个配置来获取当前module的名字。

3、在Appliction里对ARouter进行初始化,因为ARouter是所有的模块层组件都会用到,所以它的初始化放在BaseAppliction中完成。

public class BaseApplication extends Application { @Override public void onCreate { super.onCreate; initRouter(this); }
 public void initRouter(Application application) { if (BuildConfig.DEBUG) { // 这两行必须写在init之前,否则这些配置在init过程中将无效 ARouter.openLog; //打印日志 ARouter.openDebug; // 开启调试模式(如果在InstantRun模式下运行,必须开启调试模式!线上版本需要关闭,否则有安全风险) } ARouter.init(application); //尽可能早,推荐在Application中初始化 }}

4、在Activity中添加注解Route

public interface RouterPaths { String LOGIN_ACTIVITY = "/login/login_activity";}
// 在支持路由的页面上添加注解(必选)// 这里的路径需要注意的是至少需要有两级,/xx/xx@Route(path = RouterPaths.LOGIN_ACTIVITY)public class LoginActivity extends BaseActivity {}

path是指跳转路径,要求至少两级,即/xx/xx的形式,第一个xx是指group,如果不同module中出现相同的group会报错,所以建议group用module名称标识。

5、发起跳转操作

ARouter.getInstance.build(RouterPaths. LOGIN_ACTIVITY).navigation;

ARouter的还有很多其他功能,这里不作详细说明。

 

二,Aplication动态加载

Application作为程序的入口通常做一些初始化,如上面提到的ARouter,由于ARouter是所有模块层组件都要用到,所以把它放在BaseApplication进行初始化。如果某个初始化操作只属于某个模块,为了降低耦合,我们应该把该初始化操作放在对应模块module的Application里。如下:

1、在BaseModule定义接口

public interface BaseApplicationImpl { void init; ...}

2、在ModuleConfig中进行配置

public interface ModuleConfig { String LOGIN = "com.linda.login.LoginApplication"; String DETAIL = "com.linda.detail.DetailApplication"; String PAY = "com.linda.pay.PayApplication";
 String modules = { LOGIN, DETAIL, PAY };}

3、在BaseApplicatiion通过反射的方式获取各个module中Application的实例并调用init方法。

public abstract class BaseApplication extends Application implements BaseApplicationImpl { @Override public void onCreate { super.onCreate; initComponent; initARouter; }
 /** * 初始化各组件 */ public void initComponent { for (String module : ModuleConfig.modules) { try { Class clazz = Class.forName(module); BaseApplicationImpl baseApplication = (BaseApplicationImpl) clazz.newInstance; baseApplication.init; } catch (ClassNotFoundException e) { e.printStackTrace; } catch (IllegalAccessException e) { e.printStackTrace; } catch (InstantiationException e) { e.printStackTrace; } } } ...}

4、子module中实现init方法,并进行相关初始化操作

public class LiveApplication extends BaseApplication { public void init { //在这里做一些的Live相关的初始化操作 }}

 

三,模块间通信

BroadcastReceiver:系统提供,比较笨重,使用不够优雅。

EventBus:使用简单优雅,将发送这与接收者解耦,2.x使用反射方式比较耗性能,3.x使用注解方式比反射快得多。

但是有些情况是BroadcastReceiver、EventBus解决不了的,例如想在detail模块中获取mine模块中的数据。因为detail和mine都依赖了base,所以我们可以借助base来实现。

1、在base中定义接口并继承ARouter的IProvider。

public interface IMineDataProvider extends IProvider { String getMineData;}

2、在mine模块中新建MineDataProvider类实现IMineDataProvider,并实现getMineData方法

@Route(path = RouterPaths.MINE_DATA_PROVIDER)public class MineDataProvider implements IMineDataProvider { @Override public String getMineData { return "***已获取到mine模块中的数据***"; }
 @Override public void init(Context context) { }}

3、在detail中获取MineDataProvider实例并调用IMineDataProvider接口中定义的方法

 IMineDataProvider mineDataProvider = (IMineDataProvider) ARouter.getInstance.build(RouterPaths.MINE_DATA_PROVIDER).navigation;if (mineDataProvider != ) { mGetMineData.setText(mineDataProvider.getMineData);}

 

四,资源冲突

组件化项目中有很多个module,这就难免会出现module中资源命名相同而引起引用错误的情况。为此我们可以在每个module的build.gradle文件进行如下配置(例如login模块)。

resourcePrefix "login_"

所有的资源必须以指定的字符串(建议module名称)做前缀,不然会报错。不过这种方式只限定与xml文件,对图片资源无效,图片资源仍需要手动修改。

//布局文件命名示例login_activity_login.xml
<resources> <!--字符串资源命名示例--> <string name="login_app_name">Login</string></resources>

 

五,单个组件运行调试

当项目越来越庞大时,编译或运行一次就需要花费很长时间,而组件化可以通过配置对每个模块进行单独调试,大大提高了开发效率。我们需要对每个module进行如下配置:

1、在项目根目录新建common_config.gradle文件并声明变量isModuleDebug;

project.ext { //是否允许module单独调试 isModuleDebug = false}

2、引入common_config配置,另外因为组件化中每个module都是一个library,如要单独运行调试需要将library换成application,在module的build.gradle中文件中做如下修改:

//引入common_config配置apply from: "${rootProject.rootDir}/common_config.gradle"
if (project.ext.isModuleDebug.toBoolean) { apply plugin: 'com.android.application'} else { apply plugin: 'com.android.library'}
android { defaultConfig { if (project.ext.isModuleDebug.toBoolean) { // 单独调试时需要添加 applicationId applicationId "com.linda.login" } ... } sourceSets { main { //在需要单独调试的module的src/main目录下新建manifest目录和AndroidManifest文件 // 单独调试与集成调试时使用不同的 AndroidManifest.xml 文件 if (project.ext.isModuleDebug.toBoolean) { manifest.srcFile 'src/main/manifest/AndroidManifest.xml' } else { manifest.srcFile 'src/main/AndroidManifest.xml' } } }}

关于两个清单文件的不同之处如下:

<!--单独调试--><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.linda.login"> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/login_app_name" android:supportsRtl="true" android:theme="@style/base_AppTheme"> <activity android:name=".ui.LoginActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application></manifest>
<!-- 集成调试--><manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.linda.login"> <application android:allowBackup="true" android:label="@string/login_app_name" android:supportsRtl="true" android:theme="@style/base_AppTheme"> <activity android:name=".ui.LoginActivity" /> </application></manifest>

3、如果module单独调试,那么在app就不能再依赖此module,因为此时app和module都是project,project之间不能相互依赖,在app的build.gradle文件中做如下修改

dependencies { if (!project.ext.isModuleDebug) { implementation project(path: ':detail') implementation project(path: ':login') implementation project(path: ':pay') } implementation project(path: ':main') implementation project(path: ':home') implementation project(path: ':mine')}

4、最后将isModuleDebug改为true,然后编译,便可以看到login、detail、pay模块可以独立运行调试了。

Android组件化开发思想与实践Android组件化开发思想与实践

组件化Demo地址:https://github.com/zhoulinda/ComponentDemo

  • 10个让你用了大呼 “我*NB” 的网站

  • Android面经分享,失业两个月,五一节前拿到offer

  • 7 款 mac 工具,提高你的效率!

你了解多少组件化呢?



Tags:Android   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
今天面试遇到同学说做过内存优化,于是我一般都会问那 Bitmap 的像素内存存在哪?大多数同学都回答在 java heap 里面,就比较尴尬,理论上你做内存优化,如果连图片这个内存大户内存...【详细内容】
2021-12-23  Tags: Android  点击:(8)  评论:(0)  加入收藏
Android logcat日志封装logcat痛点在Android开发中使用logcat非常频繁,logcat能帮我们定位问题,但是在日常使用中发现每次使用都需要传递tag,并且会遇到输出频率很高的log,在多...【详细内容】
2021-12-22  Tags: Android  点击:(8)  评论:(0)  加入收藏
对项目的基本介绍 1.整个框架主要是给MVVM框架使用的,自己写完interface接口后,通过自定义的注解就能自动生成接口方法 2.用Kotlin的Flow去代替Rxjava,因为我发现RxJava功能很...【详细内容】
2021-12-08  Tags: Android  点击:(16)  评论:(0)  加入收藏
前言在Android开发过程中,有些时候会根据需要引用别的项目到当前项目里面,而且以Module形式引用。所以本篇博文就来分享一下怎么以Module形式引用别的项目到当前项目中,方便开...【详细内容】
2021-12-07  Tags: Android  点击:(22)  评论:(0)  加入收藏
新型Android恶意木马程序伪装成数十款街机、射击和策略游戏,通过华为应用市场AppGallery进行分发,从而窃取设备信息和用户的手机号码,全球目前至少有930万台Android设备被该恶...【详细内容】
2021-12-01  Tags: Android  点击:(24)  评论:(0)  加入收藏
作者:fundroid这篇文章偏阅读一些,大家可以了解下 Android 的一些最新动向。每年9/10月份 Google 都会举行约为期2天的 Android Dev Summit,在活动上 Google 的技术专家们会分...【详细内容】
2021-11-30  Tags: Android  点击:(15)  评论:(0)  加入收藏
一、 准备工作1、安装JDK,下载地址(可能需要一个oracle账号,大家百度一下或者自行注册一个就行。尽可能选择8或者11,这两个是长期版本)Java SE | Oracle Technology Network | Or...【详细内容】
2021-11-23  Tags: Android  点击:(28)  评论:(0)  加入收藏
如果你是一名忠实的Android玩家,那么可能会知道,今年的Android 12系统在版本规划上与“往届”相比可以说是很有些特殊。具体来说,除了前段时间刚刚推出正式版的Android 12外,谷...【详细内容】
2021-11-10  Tags: Android  点击:(24)  评论:(0)  加入收藏
使用Maven Publish Plugin插件。(官方支持)一、在Library的build.gradle中配置plugins { id &#39;com.android.library&#39; id &#39;kotlin-android&#39; id &#39;k...【详细内容】
2021-11-05  Tags: Android  点击:(37)  评论:(0)  加入收藏
今年5月,谷歌推出了Android 12,这是原生安卓系统史上最大的设计变化,10月4日,谷歌推出全新的Android12正式版本,并且宣布会在今年晚些时候应用于安卓设备,对比Android11的挤牙膏式...【详细内容】
2021-10-29  Tags: Android  点击:(125)  评论:(0)  加入收藏
▌简易百科推荐
今天面试遇到同学说做过内存优化,于是我一般都会问那 Bitmap 的像素内存存在哪?大多数同学都回答在 java heap 里面,就比较尴尬,理论上你做内存优化,如果连图片这个内存大户内存...【详细内容】
2021-12-23  像程序那样思考    Tags:Android开发   点击:(8)  评论:(0)  加入收藏
Android logcat日志封装logcat痛点在Android开发中使用logcat非常频繁,logcat能帮我们定位问题,但是在日常使用中发现每次使用都需要传递tag,并且会遇到输出频率很高的log,在多...【详细内容】
2021-12-22  YuCoding    Tags:Android   点击:(8)  评论:(0)  加入收藏
对项目的基本介绍 1.整个框架主要是给MVVM框架使用的,自己写完interface接口后,通过自定义的注解就能自动生成接口方法 2.用Kotlin的Flow去代替Rxjava,因为我发现RxJava功能很...【详细内容】
2021-12-08  网易Leo    Tags:Android开发   点击:(16)  评论:(0)  加入收藏
前言在Android开发过程中,有些时候会根据需要引用别的项目到当前项目里面,而且以Module形式引用。所以本篇博文就来分享一下怎么以Module形式引用别的项目到当前项目中,方便开...【详细内容】
2021-12-07  网易Leo    Tags:Android开发   点击:(22)  评论:(0)  加入收藏
作者:fundroid这篇文章偏阅读一些,大家可以了解下 Android 的一些最新动向。每年9/10月份 Google 都会举行约为期2天的 Android Dev Summit,在活动上 Google 的技术专家们会分...【详细内容】
2021-11-30  像程序那样思考    Tags:Android开发   点击:(15)  评论:(0)  加入收藏
一、 准备工作1、安装JDK,下载地址(可能需要一个oracle账号,大家百度一下或者自行注册一个就行。尽可能选择8或者11,这两个是长期版本)Java SE | Oracle Technology Network | Or...【详细内容】
2021-11-23  永沧    Tags:Android   点击:(28)  评论:(0)  加入收藏
使用Maven Publish Plugin插件。(官方支持)一、在Library的build.gradle中配置plugins { id &#39;com.android.library&#39; id &#39;kotlin-android&#39; id &#39;k...【详细内容】
2021-11-05  羊城小阳    Tags:Android   点击:(37)  评论:(0)  加入收藏
谷歌离推出Play Store应用程序的新数据隐私部分又近了一步。应用程序开发人员现在可以通过谷歌在Play控制台的新 "数据安全表 "填写相关细节。该公司表示,所需信息将从2022年...【详细内容】
2021-10-20    中关村在线  Tags:安卓   点击:(58)  评论:(0)  加入收藏
架构究竟是什么?如何更好的理解架构?我们知道一个APP通常是由class组成,而这些class之间如何组合,相互之间又如何产生作用,就是影响这个APP的关键点。细分的话我们可以将其分为类...【详细内容】
2021-09-17  像程序那样思考    Tags:Android架构   点击:(52)  评论:(0)  加入收藏
概述当Android应用程序需要访问设备上的敏感资源时,应用程序开发人员会使用权限模型。虽然该模型使用起来非常简单,但开发人员在使用权限时容易出错,从而导致安全漏洞。本文中,...【详细内容】
2021-09-07  SecTr安全团队    Tags:Android开发   点击:(66)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条