学习目标:
熟练使用组件化开发,路由配置
学习内容:
**在使用组件化开发前首先要明确项目整体框架,划分模块及业务(重点),好的开始才会有好的结果。**模块划分明确后开始配置Module。
如图我们要完成以下功能:
1.点击商城进入ShoppingModule
2.点击登录进入LoginModule
3.点击账单红色区域展示账单列表(其他Module中的Fragment)
(shareModule为公共模块)
根据业务需求创建如下:
在App的gradle.properties文件下添加,用于控制module是否独立运行。
#配置某个组件是否可以独立运行
isShoppingRunAlone = true
isLoginRunALone = true
然后配置App build.gradle。
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 30
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "com.example.moduledemo"
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
//导入公共模块
implementation project(':ShareModule')
// 根据gradle中的配置来决定是否引用module
if (!isLoginRunALone.toBoolean()){
implementation project(':LoginModule')
}
if (!isShoppingRunAlone.toBoolean()){
implementation project(':ShoppingModule')
}
}
继续配置其他Module的build.gradle文件。
if (isShoppingRunAlone.toBoolean()){
apply plugin: 'com.android.application'
}else {
apply plugin: 'com.android.library'
}
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 30
buildToolsVersion "29.0.3"
defaultConfig {
minSdkVersion 16
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
consumerProguardFiles "consumer-rules.pro"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
sourceSets{
main{
// 在独立运行或者作为Libarary调试时,使用不同的AndroidManifest.xml文件
if (isShoppingRunAlone.toBoolean()){
manifest.srcFile 'src/main/manifest/AndroidManifest.xml'
}else {
manifest.srcFile 'src/main/AndroidManifest.xml'
}
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
//导入公共模块
implementation project(':ShareModule')
}
在不同运行模式下使用不同的Manifest文件。
需要在对应module的Main目录下新建manifest文件夹(不然单独运行会找不到Manifest文件)。
单独运行的manifest文件设置如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.loginmodule">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
并入主Module运行时manifest文件设置如下:
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.shoppingmodule">
<application>
<activity android:name=".ShoppingActivity"/>
</application>
</manifest>
全部配置完成之后,可以在gradle.properties中修改变量的值,编译查看配置是否正确,manifest文件是否替换。运行查看是否正常。
接下来开始配置路由。
好多人心中有疑惑,在引用Module之后是可以直接获取到子Module的Activity的为什么还要使用路由跳转。是因为组件化开发是为了使单独Module可以独自编译,运行如果主Module引用子Module的类名,当子Module单独运行时主Module会编译异常。
我们要知道一个项目不可能只有一个子Module,当我们其他子Module要进行相互跳转时如何使用路由呢?所以我们要在ShareModule进行路由的配置,在之前的配置中我们将ShareModule导入了每个Module。
第一步
我们创建对应Module的跳转模板
import android.content.Context;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
public interface ILoginService {
void launch(Context ctx, String targetClass);
}
import android.content.Context;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
public interface IShoppingService {
void launch(Context ctx, String string);
Fragment newBillFragment(FragmentManager fragmentManager, int viewId, Bundle bundle);
}
第二步
在对应的moudle中实现跳转逻辑及传值操作
package com.example.loginmodule;
import android.content.Context;
import android.content.Intent;
import com.example.sharemodule.ILoginService;
public class LoginService implements ILoginService {
@Override
public void launch(Context ctx, String targetClass) {
Intent intent = new Intent(ctx, LoginActivity.class);
ctx.startActivity(intent);
}
}
package com.example.shoppingmodule;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import com.example.sharemodule.IShoppingService;
public class ShoppingService implements IShoppingService {
@Override
public void launch(Context ctx, String string) {
Intent intent = new Intent(ctx, ShoppingActivity.class);
ctx.startActivity(intent);
}
@Override
public Fragment newBillFragment(FragmentManager fragmentManager, int viewId, Bundle bundle) {
BillFragment fragment = new BillFragment();
fragment.setArguments(bundle);
fragmentManager.beginTransaction().replace(viewId, fragment).commit();
return fragment;
}
}
第三步
接下来我们创建一个ServiceFactory,为我们提供跳转实例,并且处理单独运行时可能会出现的异常
package com.example.sharemodule;
public class ServiceFactory {
private static final ServiceFactory instance = new ServiceFactory();
private ILoginService mLoginService;
private IShoppingService mShoppingService;
private ServiceFactory(){}
public static ServiceFactory getInstance() {
return instance;
}
public ILoginService getLoginService() {
if (mLoginService == null){
mLoginService = new EmptyLoginService();
}
return mLoginService;
}
public void setLoginService(ILoginService mLoginService) {
this.mLoginService = mLoginService;
}
public IShoppingService getSignService() {
if (mShoppingService == null){
mShoppingService = new EmptyShoppingService();
}
return mShoppingService;
}
public void setSignService(IShoppingService mSignService) {
this.mShoppingService = mSignService;
}
}
package com.example.mylibrarySharedLibrary;
import android.content.Context;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
public class EmptyLoginService implements ILoginService {
@Override
public void launch(Context ctx, String targetClass) {
}
@Override
public Fragment newUserInfoFragment(FragmentManager fragmentManager, int viewId, Bundle bundle) {
return null;
}
}
package com.example.mylibrarySharedLibrary;
import android.content.Context;
public class EmptySignService implements ISignService {
@Override
public void launch(Context ctx, String userId) {
}
}
这样处理即使我们单独运行主Moudle时也不会发生异常。
以上我们跳转的代码就写完了接下来就是对serviceFactory中
private ILoginService mLoginService;
private IShoppingService mSignService;
进行赋值
package com.example.sharemodule;
import android.app.Application;
public interface IComponentApplication {
void initialize(Application application);
}
提供统一初始化的接口
package com.example.moduledemo;
import android.app.Application;
import android.util.Log;
import com.example.sharemodule.AppConfig;
import com.example.sharemodule.IComponentApplication;
public class MainApplication extends Application implements IComponentApplication {
private static Application application;
public static Application getApplication(){
return application;
}
@Override
public void onCreate() {
super.onCreate();
initialize(this);
}
@Override
public void initialize(Application application) {
for (String cpnt : AppConfig.Components){
try{
Class<?> clz = Class.forName(cpnt);
Object obj = clz.newInstance();
if (obj instanceof IComponentApplication){
((IComponentApplication) obj).initialize(this);
}
}catch (Exception e){
Log.e("TAG", e.getMessage());
}
}
}
}
package com.example.loginmodule;
import android.app.Application;
import com.example.sharemodule.IComponentApplication;
import com.example.sharemodule.ServiceFactory;
public class LoginApplication extends Application implements IComponentApplication {
private static Application application;
public static Application getApplication(){
return application;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void initialize(Application app) {
application = app;
ServiceFactory.getInstance().setLoginService(new LoginService());
}
}
package com.example.shoppingmodule;
import android.app.Application;
import com.example.sharemodule.IComponentApplication;
import com.example.sharemodule.ServiceFactory;
public class ShoppingApplication extends Application implements IComponentApplication {
private static Application application;
public static Application getApplication() {
return application;
}
@Override
public void onCreate() {
super.onCreate();
}
@Override
public void initialize(Application app) {
application = app;
ServiceFactory.getInstance().setSignService(new SignService());
}
}
package com.example.sharemodule;
public class AppConfig {
public static final String[] Components = {
"com.example.shoppingmodule.ShoppingApplication",
"com.example.loginmodule.LoginApplication"
};
}
进入App时进行初始化,通过反射获取子Module的Application实例进行初始化。
最终结果:
本文在开源项目:https://github.com/Android-Alvin/Android-LearningNotes 中已收录,里面包含了Android组件化最全开源项目(美团App、得到App、支付宝App、微信App、蘑菇街App、有赞APP…)等,资源持续更新中…