美文网首页知识点Android技术知识Android开发
新手避坑指南:Android组件化开发详解

新手避坑指南:Android组件化开发详解

作者: 今日Android | 来源:发表于2021-01-11 20:30 被阅读0次

    学习目标:

    熟练使用组件化开发,路由配置


    学习内容:

    在使用组件化开发前首先要明确项目整体框架,划分模块及业务(重点),好的开始才会有好的结果。****模块划分明确后开始配置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...)等,资源持续更新中...

    相关文章

      网友评论

        本文标题:新手避坑指南:Android组件化开发详解

        本文链接:https://www.haomeiwen.com/subject/jlqjaktx.html