Android-组件化学习

2017/8/24 posted in  Android

网上关于组件化的文章有很多了,大部分都知道实现的思路,不过还是要自己去实践一下才会发现问题。文章的项目地址,项目用到CleanArchitecture框架,本文会介绍CleanArchitecture框架和dagger2在组件化的使用。

项目关系图

20171014150797219986560.png
20171014150797219986560.png

  • sdk: 一些公用库,各种辅助类,和第三方view
  • basic: (依赖sdk) 网络访问初始化,本地缓存和第三方包等。
  • commonbusiness: (依赖basic) 这里为什么我会多出这一层,因为有很多公共的业务,好比公司的app是强登录的,我会把登录模块写在这里,里面也包含了一些baseActivity和BaseApplication和各个组件的一些公共方法还有组件各种的服务接口的定义。
  • module_archives和module_knowledge: 就是两个组件,可以单独运行。

application和library切换

想必大家都知道了,定义一个isBuildModule=false,在组件的build.gradle中加入

if (isBuildModule.toBoolean()) {
    apply plugin: 'com.android.application'
} else {
    apply plugin: 'com.android.library'
    apply from: 'maven-release-kline-aar.gradle'
}

 sourceSets {
        main {
            if (isBuildModule.toBoolean()) {
                manifest.srcFile 'src/main/debug/AndroidManifest.xml'
            } else {
                manifest.srcFile 'src/main/release/AndroidManifest.xml'
                java {
                    exclude '**/debug/**'
                }
            }
        }
    }

maven-release-kline-aar.gradle是一个打包aar的文件,在切换的时候也会使用不同的AndroidManifest.xml,因为在组件是debug的时候它是单独单独运行的,还有就是代码可以在建立一个debug包,可以在单独运行的时候做些初始化app的,打包的时候回剔除这部分代码。

library依赖和资源问题

我把所以的library都依赖在basic,每个组件都会依赖这个包,这样就不会存在library的版本问题,资源的问题就是

 defaultConfig {

        if (isBuildModule.toBoolean()) {
            applicationId "com.wkw.archives"
        }

        minSdkVersion rootProject.ext.minSdkVersion
        targetSdkVersion rootProject.ext.targetSdkVersion
        versionCode rootProject.ext.versionCode
        versionName rootProject.ext.versionName

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        resourcePrefix "archives"
    }

组件之间的通信

你可以选择阿里的ARouter库,但是我的项目没有那么复杂,组件也就4个左右,所以没有使用阿里的库,我采用的是以下就是核心代码,

Object result = null;
Class<?> c = Class.forName(className);
if (c != null) {
    result = c.newInstance();
}
return result;

但是采用Class.forName会有个问题那就是在混淆的时候,className是指定的,所以要在类上加@Keep 。

CleanArchitecture框架和dagger在组件化的使用

CleanArchitecture框架的github地址,这里再介绍分享一篇文章小鄧子的Easy Clean architecture on Android,我把data和domain会写在各自的业务模块中,自己的模块只要定义自己的就可以了,有个ApplicationModule会定义一些每个模块都需要的,

public class ApplicationModule {

    @Provides
    @Singleton
    Context provideContext(Application application) {
        return application;
    }

    @Provides
    @Singleton
    UserSystem provideUserSystem() {
        return new UserSystem();
    }

    @Provides
    @Singleton
    ThreadExecutor provideThreadExecutor(JobExecutor jobExecutor) {
        return jobExecutor;
    }

    @Provides
    @Singleton
    PostExecutionThread providePostExecutionThread(UIThread uiThread) {
        return uiThread;
    }

    @Provides
    @Singleton
    MrService provideMrService() {
        return new MrService();
    }

    @Provides
    @Singleton
    UserCache provideUserCache(UserCacheImpl userCache) {
        return userCache;
    }
}

然后在module_archives模块中会有ArchivesDataRepositoryModule和ArchivesActivityModule
其中KnowledgeDataRepositoryModule用于提供如下:

@Module
public class ArchivesDataRepositoryModule {

    @Provides
    @Singleton
    ArchivesApi providesArchivesApi(MrService mrService) {
        return mrService.createApi(ArchivesApi.class);
    }

    @Provides
    @Singleton
    ArchivesRepository prvidesArchivesRepository(ArchivesDataRepository archivesDataRepository) {
        return archivesDataRepository;
    }
}

ArchivesActivityModule的代码如下:

@Module
public abstract class ArchivesActivityModule {
   @PerActivity
   @ContributesAndroidInjector()
   abstract ArchivesActivity contributeArchivesActivity();
}

这样在主app的AppComponent类中加入:

@Singleton
@Component(modules = {
       AndroidInjectionModule.class, ApplicationModule.class,
       ArchivesDataRepositoryModule.class, ArchivesActivityModule.class,
       KnowledgeDataRepositoryModule.class, KnowledgeActivityModule.class
})
public interface AppComponent {
   @Component.Builder
   interface Builder {
       @BindsInstance
       Builder application(Application application);
       AppComponent build();
   }
   void inject(MrApplication mrApplication);
}

在module_archives为debug模式下也会有个AppComponent

@Singleton
@Component(modules = {
        AndroidInjectionModule.class, ApplicationModule.class,
        ArchivesDataRepositoryModule.class, ArchivesActivityModule.class
})
public interface AppComponent {
    @Component.Builder
    interface Builder {
        @BindsInstance
        Builder application(Application application);
        AppComponent build();
    }
    void inject(ArchivesApplication mrApplication);
}

各种模块初始化api和Repository,你要是嫌弃每个模块都要引入两个.class文件,你可以使用一个然后采用include的方式好比dagger中的AndroidSupportInjectionModule类方式

@Beta
@Module(includes = AndroidInjectionModule.class)
public abstract class AndroidSupportInjectionModule {
  @Multibinds
  abstract Map<Class<? extends Fragment>, AndroidInjector.Factory<? extends Fragment>>
      supportFragmentInjectorFactories();

  private AndroidSupportInjectionModule() {}
}

这样就会很清楚的知道自己模块需要初始化什么和使用什么,也不用考虑其它模块的初始化的数据,之后只需要在主app加入就行,也是比较方便的。

打包

各种模块当为library是要打包成aar的,maven-release-kline-aar.gradle文件代码如下:

apply plugin: 'maven'

ext {// ext is a gradle closure allowing the declaration of global properties
    PUBLISH_GROUP_ID = 'com.wkw'
    PUBLISH_ARTIFACT_ID = 'archives'
    PUBLISH_VERSION = rootProject.ext.versionName
}

uploadArchives {
    repositories.mavenDeployer {
        //这里就是最后输出地址,在自己电脑上新建个文件夹,把文件夹路径粘贴在此
        //注意”file://“ + 路径,有三个斜杠,别漏了
        repository(url: "file:///Users/wukewei/Documents/android/ModularizationExample/repo")

        pom.project {
            groupId project.PUBLISH_GROUP_ID
            artifactId project.PUBLISH_ARTIFACT_ID
            version project.PUBLISH_VERSION
        }
    }
}

//以下代码会生成jar包源文件,如果是不开源码,请不要输入这段
//aar包内包含注释
task androidSourcesJar(type: Jar) {
    classifier = 'sources'
    from android.sourceSets.main.java.sourceFiles
}

artifacts {
    archives androidSourcesJar
}

我只是打包到本地,你可以自己建立一个本地maven库。