MVVM:MVVM设计模式的一套快速开发库,整合Okhttp+RxJava+Retrofit+Glide等主流模块,知足平常开发需求。使用该框架能够快速开发一个高质量、易维护的Android应用。html
ARouter:阿里出的一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通讯、解耦。java
MVVM + ARouter:MVVM模式 + 组件化方案,前者是设计模式,后者是方案架构,二者并用,相得益彰。有这两个框架做支撑,事半功倍,可快速开发组件化应用。android
ps:具体的技能点使用这里就不讲解的git
如图:github
关于app的入口module
整个项目的底层mvvm封装,并不涉及到项目相关的任何业务代码
关于项目公用的业务代码
登陆模块
个人模块
存放Application类的相关代码,好比Activity/Fragment生命周期监听。
存放dagger注入的相关代码
提供须要注入的桥梁
提供须要注入的实体
提供App须要的单例类,好比网络请求/数据库
提供Activity类
提供Fragment类
提供ViewModel类
主要是activity和fragment
全部的viewmodel
这里主要仍是网络的请求apiservice,实际上是省略了model模块
MVVM能够看做是一种特殊的MVP(Passive View)模式,或者说是对MVP模式的一种改良。数据库
MVVM表明的是Model-View-ViewModel,这里须要解释一下什么是ViewModel。ViewModel的含义就是 "Model of View",视图的模型。它的含义包含了领域模型(Domain Model)和视图的状态(State)。 在图形界面应用程序当中,界面所提供的信息可能不单单包含应用程序的领域模型。还可能包含一些领域模型不包含的视图状态,例如电子表格程序上须要显示当前排序的状态是顺序的仍是逆序的,而这是Domain Model所不包含的,但也是须要显示的信息。设计模式
能够简单把ViewModel理解为页面上所显示内容的数据抽象,和Domain Model不同,ViewModel更适合用来描述View。api
config.gradle =>三方依赖库和版本管理,统一放在该文件中网络
config_build.gradle =>整个项目(app/module) 通用的gradle配置。架构
component_build.gradle =>组件化开关的配置,lib和application的切换以及清单文件的配置
component_build代码以下:
if (isBuildModule.toBoolean()) {
apply plugin: 'com.android.application'
} else {
apply plugin: 'com.android.library'
}
apply from: "../config_build.gradle"
android {
//这里进行设置使用单独运行仍是合并运行的Manifest.xml
sourceSets {
main {
jniLibs.srcDirs = ['libs']
if (isBuildModule.toBoolean()) {
manifest.srcFile 'src/main/debug/AndroidManifest.xml'
} else {
manifest.srcFile 'src/main/release/AndroidManifest.xml'
}
}
}
}
复制代码
公司采用jenkins打包,配置三种类型productFlavor,方便提测和发版,一种开发环境,一种测试环境,一种正式环境,这样经过不一样的命令行便可打包不一样环境的apk
productFlavors {
versionDev{
dimension "verison"
buildConfigField "boolean","VERSION_ONLINE", "false"
buildConfigField "boolean","VERSION_TEST", "false"
//manifestPlaceholders = rootProject.ext.debugPlaceholders
}
versionTest {
dimension "verison"
buildConfigField "boolean","VERSION_ONLINE", "false"
buildConfigField "boolean","VERSION_TEST", "true"
//manifestPlaceholders = rootProject.ext.debugPlaceholders
}
versionOnline{
dimension "verison"
buildConfigField "boolean","VERSION_ONLINE", "true"
buildConfigField "boolean","VERSION_TEST", "true"
//manifestPlaceholders = rootProject.ext.releasePlaceholders
}
}
复制代码
打包命令行:
* 1,开发环境打包 gradlew clean assembleVersionDevDebug或者gradlew clean assembleVersionDevRealease
* 2,测试环境打包 gradlew clean assembleVersionTestDebug或者gradlew clean assembleVersionTestRealease
* 3,正式环境打包 gradlew clean assembleVersionOnlineRelease
复制代码
代码配置:
object HttpUrlConstants {
//开发环境
private const val DEV_BASE_URL = "https://wanandroid.com/dev"
//测试环境
private const val TEST_BASE_URL = "https://wanandroid.com/test"
//正式环境
private const val RELIASE_BASE_URL = "https://wanandroid.com/online"
//获取bse_url
fun getBaseUrl(): String = if (BuildConfig.VERSION_ONLINE) RELIASE_BASE_URL else{
if (BuildConfig.VERSION_TEST) TEST_BASE_URL else DEV_BASE_URL
}
复制代码
gradle配置
每一个模块的gradle文件须要引入公共的component_build.gradle,而且须要配置productFlavors,另外还须要统一资源前缀,否则打包容易出现资源冲突
例如login模块的gradle文件
apply from: "../component_build.gradle"
android {
resourcePrefix "login_" //给 Module 内的资源名增长前缀, 避免资源名冲突
flavorDimensions "verison"
productFlavors {
versionDev{
dimension "verison"
}
versionTest {
dimension "verison"
}
versionOnline{
dimension "verison"
}
}
}
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
}
复制代码
manifest配置
组件在本身的AndroidManifest.xml各自配置,application标签无需添加属性,也不须要指定activity的intent-filter。当合并打包时,gradle会将每一个组件的AndroidManifest合并到宿主App中。
文件位置:src/main/release/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mou.login">
<application>
<meta-data
android:name="com.mou.login.core.GlobalConfiguration"
android:value="ConfigModule"/>
<activity android:name=".mvvm.view.LoginActivity"
android:label="登陆"
android:screenOrientation="portrait"/>
</application>
</manifest>
复制代码
组件独立运行时,就须要单独的一个AndroidManifest.xml做为调试用。
文件位置:src/main/debug/AndroidManifest.xml
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mou.login">
<application
android:name=".core.App">
<!-- 每一个业务组件须要声明两个 ConfigModule, CommonSDK 的 ConfigModule 和 业务组件本身的 ConfigModule
CommonSDK 的 ConfigModule 含有有每一个组件均可共用的配置信息, 业务组件本身的 ConfigModule 含有本身独有的配置
信息, 这样便可重用代码, 又能够容许每一个组件可自行管理本身独有的配置信息, 若是业务组件没有独有的配置信息则只须要
声明 CommonSDK 的 ConfigModule -->
<meta-data
android:name="com.fortunes.commonsdk.core.GlobalConfiguration"
android:value="ConfigModule" />
<meta-data
android:name="com.mou.login.core.GlobalConfiguration"
android:value="ConfigModule" />
<meta-data
android:name="design_width_in_dp"
android:value="360"/>
<meta-data
android:name="design_height_in_dp"
android:value="640"/>
<activity android:name=".mvvm.view.LoginActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
复制代码
每一个模块独立运行后如图:
ARouter是阿里巴巴出品的路由框架,能够实现各组件之间的通讯
编写一个工具类进行路由跳转
object NavigationUtils {
/**
* 去往登陆页面
*/
fun goLoginActivity() {
ARouter.getInstance().build(RouterConstants.LOGIN_ACTIVITY).navigation()
}
/**
* 去往首页
*/
fun goMainActivity() {
ARouter.getInstance().build(RouterConstants.MAIN_ACTIVITY).navigation()
}
/**
* 去往WebView页面
* url:加载的网址
* title:加载的标题
*/
const val WEB_URL = "url"
const val WEB_TITLE = "title"
fun goWebActivity(url: String, title: String) {
ARouter.getInstance().build(RouterConstants.WEB_ACTIVITY)
.withString(WEB_URL, url)
.withString(WEB_TITLE, title)
.navigation()
}
}
复制代码
以MainActivity为例 1,建立MainActivity
@Route(path = RouterConstants.MAIN_ACTIVITY)
class MainActivity : BaseActivity<ActivityMainBinding>() {
override fun getLayoutId() = R.layout.activity_main
private val mViewModel by lazy {
createVM(MainViewModel::class.java)
}
override fun initView() {
//设置viewModel
mBinding.apply {
vm=mViewModel
}
btn.setOnClickListener {
mViewModel
.getArticle()
.bindDialogOrLifeCycle(this)
.onHttpSubscribeNoToast(this) {
toast("成功")
}
}
btn_login.setOnClickListener {
NavigationUtils.goLoginActivity()
}
btn_mine.setOnClickListener {
NavigationUtils.goMineActivity()
}
}
override fun initData() {
}
}
复制代码
2,编写xml文件
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable name="vm" type="com.mou.mvvmmodule.di.mvvm.viewmodel.MainViewModel"/>
</data>
<com.fortunes.commonsdk.view.toolbar.MyToolBarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
app:public_toolbar_img="false"
app:public_toolbar_title="首页">
<Button
android:id="@+id/btn"
android:text="请求"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_login"
android:text="去登陆页"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="@+id/btn_mine"
android:text="去我的中心"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:textColor="@color/black"
android:text="@{vm.chapterName}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<TextView
android:textColor="@color/black"
android:text="@{vm.link}"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
</com.fortunes.commonsdk.view.toolbar.MyToolBarLayout>
</layout>
复制代码
3,在ActivityModule中提供dagger须要Activity的注入的实例
@Module
abstract class ActivityModule {
@ContributesAndroidInjector
abstract fun contributeMainActivity(): MainActivity
}
复制代码
4,建立viewModel
class MainViewModel @Inject constructor(private val apiService: ApiService) : BaseViewModel() {
val chapterName = ObservableItemField<String>()
val link = ObservableItemField<String>()
fun getArticle(): Single<BaseBean<ArticleBean>> {
return apiService
.getArticle()
.async()
.doOnSuccess {
chapterName.set(it.data.datas[0].chapterName)
link.set(it.data.datas[0].link)
}
.doOnError {
Timber.d("doOnError")
}
}
复制代码
}
4,而后在ViewModelModule中提供dagger须要ViewModel的注入的实例
@Module
abstract class ViewModelModule {
@Binds
@IntoMap
@ViewModelKey(MainViewModel::class)
abstract fun bindMainViewModel(viewModel: MainViewModel): ViewModel
}
复制代码
这样就基本完成了一个activity的配置,就能够进行api调用和后续的数据绑定了
具体的sample地址以下: