Android组件化开发详解

学习目标:

 

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


学习内容:

在使用组件化开发前首先要明确项目整体框架,划分模块及业务(重点),好的开始才会有好的结果。模块划分明确后开始配置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实例进行初始化。

最终结果:

  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
封面 1 序 2 捐助说明 5 目 录 7 第一章 View的绘图流程 12 1.1、概述 12 1.2、Activity的组成结构 13 1.3、View树的绘图流程 15 1.3.1 测量组件大小 16 1.3.2 确定子组件的位置 17 1.3.3 绘制组件 18 1.4、说点别的 22 1.5 练习作业 22 第二章 Graphics2D API 23 2.1、概述 23 2.2、Point类和PointF类 23 2.3、Rect类和RectF类 25 2.4、Bitmap类和BitmapDrawable类 32 2.5、Canvas类与Paint类 34 2.5.1 绘图概述 34 2.5.2 Paint类 34 2.5.3 Canvas类 39 2.6 练习作业 63 第三章 使用Graphics2D实现动态效果 64 3.1 概述 64 3.2 invalidate()方法 65 3.3 坐标转换 69 3.4 剪切区(Clip) 73 3.5 案例:指针走动的手表 82 3.6 练习作业 88 第四章 双缓存技术 89 4.1 双缓存 89 4.2 在屏幕上绘制曲线 90 4.3 在屏幕上绘制矩形 99 4.4 案例:绘图App 104 4.4.1 绘图属性 106 4.4.2 软件参数 108 4.4.3 绘图缓冲区 109 4.4.4 撤消操作 111 4.4.5 图形绘制 113 4.4.6 绘图区 118 4.4.7 主界面 119 4.5 练习作业 122 第五章 阴影、渐变和位图运算 123 5.1 概述 123 5.2 阴影 123 5.3 渐变 125 5.3.1 线性渐变(LinearGradient) 126 5.3.2 径向渐变(RadialGradient) 130 5.3.3 扫描渐变(SweepGradient) 135 5.3.4 位图渐变(BitmapShader) 138 5.3.5 混合渐变(ComposeShader) 140 5.3.6 渐变与Matrix 142 5.4 位图运算 143 5.4.1 PorterDuffXfermode 143 5.4.2 图层(Layer) 146 5.4.3 位图运算技巧 148 5.5 案例1:圆形头像 152 5.6 案例2:刮刮乐 156 5.7 练习作业 161 第六章 自定义组件 163 6.1 概述 163 6.2 自定义组件的基本结构 164 6.3 重写onMeasure方法 166 6.4 组件属性 175 6.4.1 属性的基本定义 175 6.4.2 读取来自style和theme中的属性 181 6.5 案例1:圆形ImageView组件 186 6.6 案例2:验证码组件CodeView 190 6.7 练习作业 202 第七章 自定义容器 204 7.1 概述 204 7.2 ViewGroup类 205 7.2.1 ViewGroup常用方法 205 7.2.2 ViewGroup的工作原理 208 7.2.3 重写onLayout()方法 213 7.3 CornerLayout布局 217 7.3.1 基本实现 217 7.3.2 内边距padding 224 7.3.3 外边距margin 228 7.3.4 自定义LayoutParams 238 7.4 案例:流式布局(FlowLayout) 246 7.5 练习作业 256 第八章 Scroller与平滑滚动 257 8.1 概述 257 8.2 认识scrollTo()和scrollBy()方法 258 8.3 Scroller类 264 8.4 平滑滚动的工作原理 271 8.5 案例:触摸滑屏 272 8.5.1 触摸滑屏的技术分析 272 8.5.2 速度跟踪器VelocityTracker 273 8.5.3 触摸滑屏的分步实现 274 8.6 练习作业 285 第九章 侧边栏 287 9.1 概述 287 9.2 使用二进制保存标识数据 289 9.2.1 位运算符 289 9.2.2 位运算的常用功能 292 9.3 继承自ViewGroup的侧边栏 293 9.4 继承自HorizontalScrollView的侧边栏 304 9.5 练习作业 312 第十章 加强版ListView 313 10.1 概述 313 10.2 ListView的基本使用 314 10.3 ListItem随手指左右滑动 318 10.4 向右滑动删除ListItem 326 10.5 滑动ListItem出现删除按钮 336 10.5.1 列表项专用容器ExtendLayout 337 10.5.2 列表项能滑出删除按钮的ListView 342 10.5.3 定义布局文件 350 10.5.4 显示ListView 351 10.6练习作业 353 案例代码说明 354

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值