本篇简单谈谈Android及其搭建方案
浅谈对组件化的理解
什么是组件化?如何实现组件化?
不得不提的模块化
Android的模块化应用已久,它的使用让我们的代码变得整洁和易于维护。虽然我们有MVC、MVP以及MVVM等等,但是所有的M、所有的V以及所有的C或者P、VM堆叠在一起也是一件很头疼的事情。引入模块化后,根据项目的具体功能模块,分由不同的包管理,公共部分更是以库的形式引用,大大的提高了项目的可维护性,降低了协同开发在代码管理上的难度,使得项目结构更具层次感。
模块化的升级版组件化
回到最初提出的问题,什么是组件化?它是以具体业务为单元,对项目整体进行分割,并以Module的形式进行管理的一种构架方式。
使用组件化的好处
前面说到了,组件化是模块化的升级,这是从作用上来说的,他们也是有区别的。模块化更多的是站在开发的角度上,以功能为单位对项目整体进行分割;而组件化主要是以业务为单元。其次,在模块化时,除库以外,我们都只是以包的形式将各个模块进行分离,哪怕用到了Module,也大多不能独立编译。引入组件化之后,随着项目的逐渐增大,每个业务模块可以单独编译,加快了编译速度,同时也为单元模块测试提供了支持;多人开发只负责各自的业务,减少了版本管理中的代码冲突概率。
如何实现组件化
创建项目,并根据项目所包含业务进行搭建
‘:app’, ‘:major’, ‘:library’, ‘:account’, ‘:order’, ‘:goods’, ‘:business’, ‘:customer’, ‘:article’, ‘:prepurchase’, ‘:comment’
项目根目录下创建版本控制文件config.gradle,避免各个模块间版本冲突
ext {
androidVersion = [
compileSdkVersion : 28,
minSdkVersion : 21,
targetSdkVersion : 28,
versionCode : 1,
versionName : "1.0",
testInstrumentationRunner : "android.support.test.runner.AndroidJUnitRunner"
]
dependenciesVersion = [
appcompat_v7 : 'com.android.support:appcompat-v7:28.0.0',
constraint_layout : 'com.android.support.constraint:constraint-layout:1.1.3',
junit : 'junit:junit:4.12',
runner : 'com.android.support.test:runner:1.0.2',
espresso_core : 'com.android.support.test.espresso:espresso-core:3.0.2',
android_common_mvp :'com.github.ww7hcom:Android-common-mvp:1.0.9',
android_common :'com.github.ww7hcom:Android-common:1.0.31',
design : 'com.android.support:design:28.0.0',
kotlin : 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.31',
gms_play : 'com.google.android.gms:play-services-ads:17.2.0',
lifecycle_extensions : 'android.arch.lifecycle:extensions:1.1.1'
]
}
根目录build.gradle头部添加 apply from: ‘config.gradle’
apply from: 'config.gradle'
def androidVersion = ext.androidVersion
def dependenciesVersion = ext.dependenciesVersion
buildscript {
repositories {
google()
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:3.3.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.31"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
maven { url 'https://jitpack.io' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
gradle.properties文件添加各模块当前是否使用组件化
majorUseModule = true
orderUseModule = true
accountUseModule = true
goodsUseModule = true
businessUseModule = true
customerUseModule = true
articleUseModule = true
prepurchaseUseModule = true
commentUseModule = true
app壳build.gradle示例,根据各个模块是否使用组件化来判断是否需要作为库引入
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion androidVersion.compileSdkVersion
defaultConfig {
applicationId "com.ww7h.assistant"
minSdkVersion androidVersion.minSdkVersion
targetSdkVersion androidVersion.targetSdkVersion
versionCode androidVersion.versionCode
versionName androidVersion.versionName
testInstrumentationRunner androidVersion.testInstrumentationRunner
javaCompileOptions { annotationProcessorOptions { includeCompileClasspath = true } }
javaCompileOptions{
annotationProcessorOptions {
includeCompileClasspath true
}
}
}
buildTypes {
release {
minifyEnabled false
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation dependenciesVersion.kotlin
implementation dependenciesVersion.appcompat_v7
implementation dependenciesVersion.constraint_layout
implementation dependenciesVersion.design
testImplementation dependenciesVersion.junit
androidTestImplementation dependenciesVersion.runner
androidTestImplementation dependenciesVersion.espresso_core
implementation dependenciesVersion.android_common_mvp
implementation dependenciesVersion.android_common
implementation project(path: ':library')
if (majorUseModule.toBoolean()) {
implementation project(path: ':major')
}
if (accountUseModule.toBoolean()) {
implementation project(path: ':account')
}
if (orderUseModule.toBoolean()) {
implementation project(path: ':order')
}
if (goodsUseModule.toBoolean()) {
implementation project(path: ':goods')
}
if (businessUseModule.toBoolean()) {
implementation project(path: ':business')
}
if (customerUseModule.toBoolean()) {
implementation project(path: ':customer')
}
if (articleUseModule.toBoolean()) {
implementation project(path: ':article')
}
if (prepurchaseUseModule.toBoolean()) {
implementation project(path: ':prepurchase')
}
if (commentUseModule.toBoolean()) {
implementation project(path: ':comment')
}
}
major主模块build.gradle示例,其他模块类似major
if (majorUseModule.toBoolean()) {
apply plugin: 'com.android.library'
} else {
apply plugin: 'com.android.application'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion androidVersion.compileSdkVersion
defaultConfig {
if (!majorUseModule.toBoolean()) {
applicationId "com.ww7h.assistant.major"
}
minSdkVersion androidVersion.minSdkVersion
targetSdkVersion androidVersion.targetSdkVersion
versionCode androidVersion.versionCode
versionName androidVersion.versionName
testInstrumentationRunner androidVersion.testInstrumentationRunner
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
sourceSets {
main {
if (majorUseModule.toBoolean()) {
manifest.srcFile 'src/main/module/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation dependenciesVersion.kotlin
implementation dependenciesVersion.appcompat_v7
implementation dependenciesVersion.constraint_layout
implementation dependenciesVersion.design
testImplementation dependenciesVersion.junit
androidTestImplementation dependenciesVersion.runner
androidTestImplementation dependenciesVersion.espresso_core
implementation dependenciesVersion.android_common_mvp
implementation dependenciesVersion.android_common
implementation project(path: ':library')
}
各个组件之间依赖关系如下图,均为单向依赖,除app,library外,其他组件间均无依赖关系
组件之间的通信
个人觉得各个组件之间的通信是做好组件化的关键,那么如何让这些没有相互依赖的组件间实现通信了?
路由ARouter
!!文档很详细,就不复制粘贴了
URL Scheme跳转
定义拦截器
<intent-filter>
<data
android:host="goods"
android:path="/goodsDetail"
android:port="8080"
android:scheme="ww7h"/>
<category android:name="android.intent.category.DEFAULT"/>
<action android:name="android.intent.action.VIEW"/>
<category android:name="android.intent.category.BROWSABLE"/>
</intent-filter>
跳转代码
private void jump() {
if (schemeValid("ww7h://goods:8080/goodsDetail?goodsId=100")) {
Intent action = new Intent(Intent.ACTION_VIEW);
action.setData(Uri.parse("ww7h://goods:8080/goodsDetail?goodsId=100"));
startActivity(action);
}
}
private boolean schemeValid(String url) {
PackageManager manager = mContext.getPackageManager();
Intent action = new Intent(Intent.ACTION_VIEW);
action.setData(Uri.parse(url));
List list = manager.queryIntentActivities(action, PackageManager.GET_RESOLVED_FILTER);
return list != null && list.size() > 0;
}
包名加Activity类名跳转
var intent:Intent= Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_LAUNCHER);
var cn : ComponentName = ComponentName("com.ww7h.assistant",
"com.ww7h.assistant.major.v.MajorMainActivity");
intent.component = cn;
startActivity(intent);
自定义路由
…
对象传递跳转
!!根据app依赖了所有组件,library被所有组件依赖,得到:app可以调用所有组件类;library可以被所有组件调用
library定义接口,提供跳转、fragment获取、fragment显示功能
package com.ww7h.assistant.library;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import com.ww7h.common.mvp.views.BaseViewFragment;
/**
* ================================================
* 描述:
* 来源: Android Studio.
* 项目名: PurchasingAssistant
* 包名: com.ww7h.assistant.library
* 创建时间: 2019-05-22 16:36
*
* @author ww Github地址:https://github.com/ww7hcom
* ================================================
*/
public interface IClassManager {
void jump(Context context, Intent intent, String route);
void jumpForResult(Activity activity, Intent intent, String route, int requestCode);
<F extends BaseViewFragment> F showFragment(Context context, FragmentManager fragmentManager, F f, F lastF, int frameLayoutId, String route);
<F extends BaseViewFragment> F getFragment(String route);
<F extends BaseViewFragment> F showFragment(Context context, FragmentManager fragmentManager, int frameLayoutId, String route);
}
library定义组件class管理器
package com.ww7h.assistant.library;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.FragmentManager;
import android.widget.Toast;
import com.ww7h.common.mvp.views.BaseViewFragment;
/**
* ================================================
* 描述:
* 来源: Android Studio.
* 项目名: PurchasingAssistant
* 包名: com.ww7h.assistant.library
* 创建时间: 2019-05-21 20:42
*
* @author ww Github地址:https://github.com/ww7hcom
* ================================================
*/
public class ModuleClassManager {
private IClassManager iClassManager;
private ModuleClassManager() {
}
private static class Instance {
private static final ModuleClassManager INSTANCE = new ModuleClassManager();
}
public static ModuleClassManager getInstance() {
return Instance.INSTANCE;
}
public void setIClassManager(IClassManager iClassManager) {
this.iClassManager = iClassManager;
}
public void jump(Context context, Intent intent, String route) {
if (iClassManager != null) {
iClassManager.jump(context, intent, route);
} else {
Toast.makeText(context, "app中未注册监听", Toast.LENGTH_SHORT).show();
}
}
public void jumpForResult(Activity activity, Intent intent, String route, int requestCode) {
if (iClassManager != null) {
iClassManager.jumpForResult(activity, intent, route, requestCode);
} else {
Toast.makeText(activity, "app中未注册监听", Toast.LENGTH_SHORT).show();
}
}
public <F extends BaseViewFragment> F showFragment(Context context, FragmentManager fragmentManager, F f, F lastF, int frameLayoutId, String route) {
if (iClassManager != null) {
return iClassManager.showFragment(context, fragmentManager, f, lastF, frameLayoutId, route);
} else {
Toast.makeText(context, "app中未注册监听", Toast.LENGTH_SHORT).show();
}
return null;
}
public <F extends BaseViewFragment> F getFragment(String route) {
if (iClassManager != null) {
return iClassManager.getFragment(route);
}
return null;
}
public <F extends BaseViewFragment> F showFragment(Context context, FragmentManager fragmentManager, int frameLayoutId, String route) {
if (iClassManager != null) {
return iClassManager.showFragment( context, fragmentManager, frameLayoutId, route);
}
return null;
}
}
各个模块中均定义一个class管理器,用来注册各自模块中可供外部调用的类
package com.ww7h.assistant.article;
import com.ww7h.assistant.article.v.ArticleMainFragment;
import java.util.HashMap;
/**
* ================================================
* 描述:
* 来源: Android Studio.
* 项目名: ArticleClassManager
* 包名: com.ww7h.assistant.article
* 创建时间: 2019-05-22 17:59
*
* @author ww Github地址:https://github.com/ww7hcom
* ================================================
*/
public class ArticleClassManager {
private final HashMap<String, Class<?>> activityMap = new HashMap<String, Class<?>>() {
{
put("article_article_main", MainActivity.class);
}
};
private HashMap<String, Class<?>> fragmentMap = new HashMap<String, Class<?>>(){
{
put("article_article_main", ArticleMainFragment.class);
}
};
private ArticleClassManager() {
}
private static class Instance {
private static final ArticleClassManager INSTANCE = new ArticleClassManager();
}
public static ArticleClassManager getInstance() {
return Instance.INSTANCE;
}
public HashMap<String, Class<?>> getActivityMap() {
return activityMap;
}
public HashMap<String, Class<?>> getFragmentMap() {
return fragmentMap;
}
}
app中定义ClassManager,用来收集各个子组件供外部调用的类,并实现IClassManager
package com.ww7h.assistant;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.widget.Toast;
import com.ww7h.assistant.account.AccountClassManager;
import com.ww7h.assistant.article.ArticleClassManager;
import com.ww7h.assistant.business.BusinessClassManager;
import com.ww7h.assistant.comment.CommentClassManager;
import com.ww7h.assistant.customer.CustomerClassManager;
import com.ww7h.assistant.goods.GoodsClassManager;
import com.ww7h.assistant.major.MajorClassManager;
import com.ww7h.assistant.order.OrderClassManager;
import com.ww7h.assistant.prepurchase.PrePurchaseClassManager;
import com.ww7h.assistant.library.IClassManager;
import com.ww7h.common.mvp.views.BaseViewFragment;
import java.util.HashMap;
import java.util.Objects;
/**
* ================================================
* 描述:
* 来源: Android Studio.
* 项目名: PurchasingAssistant
* 包名: com.ww7h.assistant
* 创建时间: 2019-05-22 16:08
*
* @author ww Github地址:https://github.com/ww7hcom
* ================================================
*/
public class ClassManager implements IClassManager {
private HashMap<String, Class<?>> activityMap = new HashMap<String, Class<?>>() {{
putAll(AccountClassManager.getInstance().getActivityMap());
putAll(ArticleClassManager.getInstance().getActivityMap());
putAll(BusinessClassManager.getInstance().getActivityMap());
putAll(CommentClassManager.getInstance().getActivityMap());
putAll(CustomerClassManager.getInstance().getActivityMap());
putAll(GoodsClassManager.getInstance().getActivityMap());
putAll(MajorClassManager.Companion.getInstance().getActivityMap());
putAll(OrderClassManager.getInstance().getActivityMap());
putAll(PrePurchaseClassManager.getInstance().getActivityMap());
}};
private HashMap<String, Class<?>> fragmentMap = new HashMap<String, Class<?>>() {{
putAll(AccountClassManager.getInstance().getFragmentMap());
putAll(ArticleClassManager.getInstance().getFragmentMap());
putAll(BusinessClassManager.getInstance().getFragmentMap());
putAll(CommentClassManager.getInstance().getFragmentMap());
putAll(CustomerClassManager.getInstance().getFragmentMap());
putAll(GoodsClassManager.getInstance().getFragmentMap());
putAll(MajorClassManager.Companion.getInstance().getFragmentMap());
putAll(OrderClassManager.getInstance().getFragmentMap());
putAll(PrePurchaseClassManager.getInstance().getFragmentMap());
}};
private ClassManager() {
}
private static class Instance {
private static final ClassManager INSTANCE = new ClassManager();
}
public static ClassManager getInstance() {
return Instance.INSTANCE;
}
@Override
public void jump(Context context, Intent intent, String route) {
if (activityMap.containsKey(route)) {
intent.setClass(context, Objects.requireNonNull(activityMap.get(route)));
context.startActivity(intent);
} else {
Toast.makeText(context, "目标页面不存在", Toast.LENGTH_SHORT).show();
}
}
@Override
public void jumpForResult(Activity activity, Intent intent, String route, int requestCode) {
if (activityMap.containsKey(route)) {
intent.setClass(activity, Objects.requireNonNull(activityMap.get(route)));
activity.startActivityForResult(intent, requestCode);
} else {
Toast.makeText(activity, "目标页面不存在", Toast.LENGTH_SHORT).show();
}
}
@Override
public <F extends BaseViewFragment> F showFragment(Context context, FragmentManager fragmentManager, F f, F lastF, int frameLayoutId, String route) {
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
if (f == null && fragmentMap.containsKey(route)) {
try {
f = (F) fragmentMap.get(route).newInstance();
if (lastF != null) {
fragmentTransaction.hide(lastF);
}
fragmentTransaction.add(frameLayoutId, f).commit();
} catch (IllegalAccessException e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
e.printStackTrace();
} catch (InstantiationException e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
e.printStackTrace();
} catch (Exception e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
} else if (f != null && f.isAdded()) {
if (lastF != null) {
fragmentTransaction.hide(lastF);
}
fragmentTransaction.show(f).commit();
} else {
Toast.makeText(context, "目标页面不存在", Toast.LENGTH_SHORT).show();
}
lastF = f;
return f;
}
@Override
public <F extends BaseViewFragment> F getFragment(String route) {
try {
return (F) fragmentMap.get(route).newInstance();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
return null;
}
@Override
public <F extends BaseViewFragment> F showFragment(Context context, FragmentManager fragmentManager, int frameLayoutId, String route) {
BaseViewFragment f = null;
try {
f = (F) fragmentMap.get(route).newInstance();
if (f != null && !f.isAdded()) {
fragmentManager.beginTransaction().add(frameLayoutId, f).commit();
}
} catch (IllegalAccessException e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
e.printStackTrace();
} catch (InstantiationException e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
e.printStackTrace();
} catch (Exception e) {
Toast.makeText(context, e.getMessage(), Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
return (F) f;
}
}
在application中注册library中ModuleClassManager中的回调
ModuleClassManager.getInstance().setIClassManager(ClassManager.getInstance());
使用
ModuleClassManager.getInstance().jump(this, Intent(), "major_major_main")
ModuleClassManager.getInstance().showFragment(
this,
supportFragmentManager,
fragment,
mLastFragment,
R.id.major_content_fl,
fragmentKeyMap[itemId]
)
自此并可以自由于各个子模块之间的调用