├── app ├── .gitignore ├── multidexKeep.pro ├── src │ └── main │ │ ├── res │ │ ├── values │ │ │ └── strings.xml │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ ├── xml │ │ │ └── network_security_config.xml │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ └── drawable │ │ │ └── ic_launcher_background.xml │ │ ├── java │ │ └── com │ │ │ └── quyunshuo │ │ │ └── androidbaseframemvvm │ │ │ └── app │ │ │ └── AppApplication.kt │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── lib_base ├── .gitignore ├── consumer-rules.pro ├── src │ └── main │ │ ├── AndroidManifest.xml │ │ ├── res │ │ └── values │ │ │ ├── strings.xml │ │ │ ├── colors.xml │ │ │ └── styles.xml │ │ └── java │ │ └── com │ │ └── quyunshuo │ │ └── androidbaseframemvvm │ │ └── base │ │ ├── utils │ │ ├── ResourcesFun.kt │ │ ├── StateLayoutEnum.kt │ │ ├── network │ │ │ ├── NetworkStateChangeListener.kt │ │ │ ├── NetworkTypeEnum.kt │ │ │ ├── AutoRegisterNetListener.kt │ │ │ ├── NetworkStateClient.kt │ │ │ └── NetworkCallbackImpl.kt │ │ ├── ThreadUtils.kt │ │ ├── status │ │ │ ├── ViewStatusHelper.kt │ │ │ └── imp │ │ │ │ └── BaseFrameViewStatusHelperImp.kt │ │ ├── CoilGIFImageLoader.kt │ │ ├── ClipboardUtils.kt │ │ ├── AppUtils.kt │ │ ├── Utils.kt │ │ ├── ToastUtils.kt │ │ ├── EventBusUtils.kt │ │ ├── RegisterEventBus.kt │ │ ├── AndroidBugFixUtils.kt │ │ ├── ForegroundBackgroundHelper.kt │ │ ├── SpUtils.kt │ │ ├── ProcessUtils.kt │ │ ├── ActivityStackManager.kt │ │ ├── DateUtils.kt │ │ └── BarUtils.java │ │ ├── constant │ │ └── VersionStatus.kt │ │ ├── mvvm │ │ ├── vm │ │ │ ├── EmptyViewModel.kt │ │ │ └── BaseViewModel.kt │ │ ├── v │ │ │ ├── FrameView.kt │ │ │ ├── BaseFrameFragment.kt │ │ │ └── BaseFrameActivity.kt │ │ └── m │ │ │ └── BaseRepository.kt │ │ ├── ktx │ │ ├── EditTextKtx.kt │ │ ├── PopupWindowKtx.kt │ │ ├── NetUtils.kt │ │ ├── ActivityKtx.kt │ │ ├── LifecycleOwnerKtx.kt │ │ ├── ViewPager2Ktx.kt │ │ ├── VideoViewKtx.kt │ │ ├── SizeUnitKtx.kt │ │ ├── ViewModelKtx.kt │ │ └── ViewKtx.kt │ │ ├── view │ │ └── OnSingleClickListener.kt │ │ ├── app │ │ ├── ApplicationLifecycle.kt │ │ ├── LoadModuleProxy.kt │ │ └── ActivityLifecycleCallbacksImpl.kt │ │ └── BaseApplication.kt ├── proguard-rules.pro └── build.gradle ├── lib_common ├── .gitignore ├── consumer-rules.pro ├── src │ └── main │ │ ├── res │ │ └── values │ │ │ └── colors.xml │ │ ├── java │ │ └── com │ │ │ └── quyunshuo │ │ │ └── androidbaseframemvvm │ │ │ └── common │ │ │ ├── constant │ │ │ ├── RouteUrl.kt │ │ │ ├── SpKey.kt │ │ │ ├── RouteKey.kt │ │ │ └── NetBaseUrlConstant.kt │ │ │ ├── ui │ │ │ ├── BaseFragment.kt │ │ │ ├── BaseActivity.kt │ │ │ └── BaseFragmentStateAdapter.kt │ │ │ ├── helper │ │ │ ├── ResponseException.kt │ │ │ ├── ResponseCodeEnum.kt │ │ │ └── ExceptionHandler.kt │ │ │ ├── di │ │ │ └── DINetworkModule.kt │ │ │ └── CommonApplication.kt │ │ └── AndroidManifest.xml ├── proguard-rules.pro └── build.gradle ├── module_home ├── .gitignore ├── consumer-rules.pro ├── src │ ├── main │ │ ├── res │ │ │ ├── values │ │ │ │ └── strings.xml │ │ │ └── layout │ │ │ │ ├── home_activity_main.xml │ │ │ │ ├── home_activity_internal_layout.xml │ │ │ │ └── home_fragment_internal_layout.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── quyunshuo │ │ │ │ └── module │ │ │ │ └── home │ │ │ │ ├── net │ │ │ │ └── HomeApiService.kt │ │ │ │ ├── fragment │ │ │ │ ├── InternalRepository.kt │ │ │ │ ├── InternalViewModel.kt │ │ │ │ └── InternalFragment.kt │ │ │ │ ├── activity │ │ │ │ ├── HomeRepository.kt │ │ │ │ ├── HomeViewModel.kt │ │ │ │ ├── MainActivity.kt │ │ │ │ └── InternalPagerActivity.kt │ │ │ │ └── di │ │ │ │ └── DIHomeNetServiceModule.kt │ │ └── AndroidManifest.xml │ ├── test │ │ └── java │ │ │ └── com │ │ │ └── quyunshuo │ │ │ └── module │ │ │ └── home │ │ │ └── ExampleUnitTest.kt │ └── androidTest │ │ └── java │ │ └── com │ │ └── quyunshuo │ │ └── module │ │ └── home │ │ └── ExampleInstrumentedTest.kt ├── build.gradle └── proguard-rules.pro ├── img └── img2.png ├── gradle ├── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties └── libs.versions.toml ├── settings.gradle.kts ├── .gitignore ├── gradle.properties ├── base_lib.gradle ├── gradlew.bat ├── base_module.gradle ├── gradlew └── README.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /lib_base/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /lib_base/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib_common/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /module_home/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /lib_common/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /module_home/consumer-rules.pro: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib_base/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/multidexKeep.pro: -------------------------------------------------------------------------------- 1 | -keep public class com.tencent.bugly.**{*;} -------------------------------------------------------------------------------- /module_home/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /img/img2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quyunshuo/AndroidBaseFrameMVVM/HEAD/img/img2.png -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidBaseFrameMVVM 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quyunshuo/AndroidBaseFrameMVVM/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quyunshuo/AndroidBaseFrameMVVM/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Quyunshuo/AndroidBaseFrameMVVM/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /lib_common/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #00BCD4 5 | -------------------------------------------------------------------------------- /lib_base/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 网路已断开 3 | 网络已连接 4 | -------------------------------------------------------------------------------- /module_home/src/main/java/com/quyunshuo/module/home/net/HomeApiService.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.module.home.net 2 | 3 | /** 4 | * Home模块的接口 5 | * 6 | * @author Qu Yunshuo 7 | * @since 6/4/21 5:51 PM 8 | */ 9 | interface HomeApiService -------------------------------------------------------------------------------- /lib_base/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #6200EE 4 | #3700B3 5 | #03DAC5 6 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/quyunshuo/androidbaseframemvvm/common/constant/RouteUrl.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.common.constant 2 | 3 | /** 4 | * @Author: QuYunShuo 5 | * @Time: 2020/8/28 6 | * @Class: RoutePath 7 | * @Remark: 路由地址 8 | */ 9 | object RouteUrl -------------------------------------------------------------------------------- /lib_common/src/main/java/com/quyunshuo/androidbaseframemvvm/common/constant/SpKey.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.common.constant 2 | 3 | /** 4 | * @Author: QuYunShuo 5 | * @Time: 2020/8/29 6 | * @Class: SpKey 7 | * @Remark: 本地存储的键 放在此类中 8 | */ 9 | object SpKey -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat May 22 08:57:40 CST 2021 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip 7 | -------------------------------------------------------------------------------- /lib_common/src/main/java/com/quyunshuo/androidbaseframemvvm/common/constant/RouteKey.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.common.constant 2 | 3 | /** 4 | * @Author: QuYunShuo 5 | * @Time: 2020/8/28 6 | * @Class: RouteKey 7 | * @Remark: 路由使用中 用到的Key 统一写在此类中 8 | */ 9 | object RouteKey -------------------------------------------------------------------------------- /app/src/main/res/xml/network_security_config.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | android.bugly.qq.com 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/ResourcesFun.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils 2 | 3 | import androidx.annotation.StringRes 4 | import com.quyunshuo.androidbaseframemvvm.base.BaseApplication.Companion.application as app 5 | 6 | fun getString(@StringRes stringRes: Int): String { 7 | return app.getString(stringRes) 8 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/StateLayoutEnum.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils 2 | 3 | /** 4 | * 状态视图的状态枚举 5 | * 6 | * @author Qu Yunshuo 7 | * @since 2021/7/10 9:16 上午 8 | */ 9 | enum class StateLayoutEnum { 10 | HIDE, // 隐藏 11 | LOADING, // 加载中 12 | ERROR, // 错误 13 | NO_DATA // 没有数据 14 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/constant/VersionStatus.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.constant 2 | 3 | /** 4 | * 版本状态 5 | * 6 | * @author Qu Yunshuo 7 | * @since 4/20/21 9:05 AM 8 | */ 9 | object VersionStatus { 10 | 11 | const val RELEASE = "VERSION_STATUS_RELEASE" 12 | 13 | const val ALPHA = "VERSION_STATUS_ALPHA" 14 | 15 | const val BETA = "VERSION_STATUS_BETA" 16 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/mvvm/vm/EmptyViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.mvvm.vm 2 | 3 | import dagger.hilt.android.lifecycle.HiltViewModel 4 | import javax.inject.Inject 5 | 6 | /** 7 | * 空的ViewModel 主要给现阶段不需要ViewModel的界面使用 8 | * 9 | * @author Qu Yunshuo 10 | * @since 2021/7/10 11:04 上午 11 | */ 12 | @HiltViewModel 13 | class EmptyViewModel @Inject constructor() : BaseViewModel() -------------------------------------------------------------------------------- /lib_base/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /module_home/src/test/java/com/quyunshuo/module/home/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.module.home 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | } 17 | } -------------------------------------------------------------------------------- /gradle/libs.versions.toml: -------------------------------------------------------------------------------- 1 | [versions] 2 | # plugin 3 | agp = "8.1.0" 4 | kotlin-android = "1.8.0" 5 | hilt = "2.44" 6 | 7 | #lib 8 | 9 | 10 | [libraries] 11 | 12 | 13 | [plugins] 14 | application = { id = "com.android.application", version.ref = "agp" } 15 | library = { id = "com.android.library", version.ref = "agp" } 16 | kotlin = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin-android" } 17 | hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } 18 | 19 | 20 | [bundles] -------------------------------------------------------------------------------- /lib_common/src/main/java/com/quyunshuo/androidbaseframemvvm/common/constant/NetBaseUrlConstant.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.common.constant 2 | 3 | /** 4 | * 接口公共地址 5 | * 6 | * @author Qu Yunshuo 7 | * @since 4/17/21 3:27 PM 8 | */ 9 | internal object NetBaseUrlConstant { 10 | 11 | val MAIN_URL = "http://www.baidu.com" 12 | get() { 13 | if (field.isEmpty()){ 14 | throw NotImplementedError("请求改你的 MAIN_URL 的值为自己的请求地址") 15 | } 16 | return field 17 | } 18 | } -------------------------------------------------------------------------------- /lib_common/src/main/java/com/quyunshuo/androidbaseframemvvm/common/ui/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.common.ui 2 | 3 | import androidx.viewbinding.ViewBinding 4 | import com.quyunshuo.androidbaseframemvvm.base.mvvm.v.BaseFrameFragment 5 | import com.quyunshuo.androidbaseframemvvm.base.mvvm.vm.BaseViewModel 6 | 7 | /** 8 | * Fragment基类 9 | * 10 | * @author Qu Yunshuo 11 | * @since 8/27/20 12 | */ 13 | abstract class BaseFragment : BaseFrameFragment() -------------------------------------------------------------------------------- /module_home/src/main/java/com/quyunshuo/module/home/fragment/InternalRepository.kt: -------------------------------------------------------------------------------- 1 | //package com.quyunshuo.module.home.fragment 2 | // 3 | //import com.quyunshuo.androidbaseframemvvm.base.mvvm.m.BaseRepository 4 | //import kotlinx.coroutines.delay 5 | //import javax.inject.Inject 6 | // 7 | ///** 8 | // * @author DBoy 2021/7/6

9 | // * - 文件描述 : 10 | // */ 11 | //class InternalRepository @Inject constructor() : BaseRepository() { 12 | // 13 | // suspend fun getData() = request { 14 | // delay(1000) 15 | // emit("数据加载成功") 16 | // } 17 | //} -------------------------------------------------------------------------------- /module_home/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/ktx/EditTextKtx.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.ktx 2 | 3 | import android.text.InputFilter 4 | import android.widget.EditText 5 | 6 | /** 7 | * EditText相关扩展方法 8 | * 9 | * @author Qu Yunshuo 10 | * @since 2020/9/17 11 | */ 12 | 13 | /** 14 | * 过滤掉空格和回车 15 | */ 16 | fun EditText.filterBlankAndCarriageReturn() { 17 | val filterList = mutableListOf() 18 | filterList.addAll(filters) 19 | filterList.add(InputFilter { source, _, _, _, _, _ -> if (source == " " || source == "\n") "" else null }) 20 | filters = filterList.toTypedArray() 21 | } -------------------------------------------------------------------------------- /module_home/build.gradle: -------------------------------------------------------------------------------- 1 | import com.quyunshuo.androidbaseframemvvm.buildsrc.ProjectBuildConfig 2 | 3 | //**************************************** 4 | //******** module_home 的配置文件 ********* 5 | //**************************************** 6 | 7 | plugins { 8 | alias(libs.plugins.kotlin) 9 | alias(libs.plugins.hilt) 10 | id "kotlin-kapt" 11 | } 12 | if (ProjectBuildConfig.isAppMode) { 13 | apply plugin: 'com.android.application' 14 | } else { 15 | apply plugin: 'com.android.library' 16 | } 17 | 18 | apply from: '../base_module.gradle' 19 | 20 | android { 21 | resourcePrefix "home_" 22 | namespace 'com.quyunshuo.module.home' 23 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/network/NetworkStateChangeListener.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils.network 2 | 3 | /** 4 | * 网络状态改变监听起 5 | * 6 | * @author Qu Yunshuo 7 | * @since 2021/7/11 4:56 下午 8 | */ 9 | interface NetworkStateChangeListener { 10 | 11 | /** 12 | * 网络类型更改回调 13 | * @param type NetworkTypeEnum 网络类型 14 | * @return Unit 15 | */ 16 | fun networkTypeChange(type: NetworkTypeEnum) 17 | 18 | /** 19 | * 网络连接状态更改回调 20 | * @param isConnected Boolean 是否已连接 21 | * @return Unit 22 | */ 23 | fun networkConnectChange(isConnected: Boolean) 24 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/ThreadUtils.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils 2 | 3 | import android.os.Build 4 | import android.os.Looper 5 | 6 | /** 7 | * 线程相关工具类 8 | * 9 | * @author Qu Yunshuo 10 | * @since 2023/3/12 19:29 11 | */ 12 | object ThreadUtils { 13 | 14 | /** 15 | * 判断当前是否是主线程 16 | * 在 [Build.VERSION.SDK_INT] >= [Build.VERSION_CODES.M] 有一个简化方法来判断当前线程是否是主线程 17 | * ``` 18 | * Looper.getMainLooper().isCurrentThread() 19 | * ``` 20 | * 21 | * @return Boolean 22 | */ 23 | fun isMainThread(): Boolean = Looper.getMainLooper().thread == Thread.currentThread() 24 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/status/ViewStatusHelper.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils.status 2 | 3 | import android.os.Bundle 4 | 5 | /** 6 | * @author DBoy 2021/8/5

7 | * - 文件描述 : 采用了一种链式调用,所有对象持有自己父级帮助类,进行场景回复时先恢复链头的数据 8 | */ 9 | abstract class ViewStatusHelper(val parentViewStatusHelper: ViewStatusHelper?) { 10 | 11 | open fun onRestoreInstanceStatus(savedInstanceState: Bundle?) { 12 | parentViewStatusHelper?.onRestoreInstanceStatus(savedInstanceState) 13 | } 14 | 15 | open fun onSaveInstanceState(bundle: Bundle) { 16 | parentViewStatusHelper?.onSaveInstanceState(bundle) 17 | } 18 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/ktx/PopupWindowKtx.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.ktx 2 | 3 | import android.view.View 4 | import android.view.ViewGroup 5 | import android.widget.PopupWindow 6 | 7 | /** 8 | * PopupWindow相关扩展 9 | * 10 | * @author Qu Yunshuo 11 | * @since 1/4/21 10:48 AM 12 | */ 13 | 14 | /** 15 | * 测量view宽高 16 | */ 17 | fun PopupWindow.makeDropDownMeasureSpec(measureSpec: Int): Int { 18 | val mode = 19 | if (measureSpec == ViewGroup.LayoutParams.WRAP_CONTENT) View.MeasureSpec.UNSPECIFIED else View.MeasureSpec.EXACTLY 20 | return View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(measureSpec), mode) 21 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/mvvm/v/FrameView.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.mvvm.v 2 | 3 | import androidx.lifecycle.LiveData 4 | import androidx.viewbinding.ViewBinding 5 | 6 | /** 7 | * View层基类抽象 8 | * 9 | * @author Qu Yunshuo 10 | * @since 10/13/20 11 | */ 12 | interface FrameView { 13 | 14 | /** 15 | * 创建 [ViewBinding] 实例 16 | */ 17 | fun createVB(): VB 18 | 19 | /** 20 | * 初始化 View 21 | */ 22 | fun VB.initView() 23 | 24 | /** 25 | * 订阅 [LiveData] 26 | */ 27 | fun initObserve() 28 | 29 | /** 30 | * 用于在页面创建时进行请求接口 31 | */ 32 | fun initRequestData() 33 | } -------------------------------------------------------------------------------- /settings.gradle.kts: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | google() 4 | mavenCentral() 5 | gradlePluginPortal() 6 | maven { url = java.net.URI.create("https://maven.google.com") } 7 | } 8 | } 9 | 10 | dependencyResolutionManagement { 11 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 12 | repositories { 13 | google() 14 | mavenCentral() 15 | gradlePluginPortal() 16 | maven { url = java.net.URI.create("https://maven.google.com") } 17 | } 18 | } 19 | 20 | rootProject.name = "AndroidBaseFrameMVVM" 21 | 22 | include( 23 | ":app", 24 | ":lib_base", 25 | ":lib_common", 26 | ":module_home" 27 | ) -------------------------------------------------------------------------------- /module_home/src/main/java/com/quyunshuo/module/home/activity/HomeRepository.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.module.home.activity 2 | 3 | import com.quyunshuo.androidbaseframemvvm.base.mvvm.m.BaseRepository 4 | import com.quyunshuo.module.home.net.HomeApiService 5 | import kotlinx.coroutines.delay 6 | import javax.inject.Inject 7 | 8 | /** 9 | * 首页M层 10 | * 11 | * @author Qu Yunshuo 12 | * @since 5/25/21 5:42 PM 13 | */ 14 | class HomeRepository @Inject constructor() : BaseRepository() { 15 | 16 | @Inject 17 | lateinit var mApi: HomeApiService 18 | 19 | /** 20 | * 模拟获取数据 21 | */ 22 | suspend fun getData() = request { 23 | delay(1000L) 24 | emit("Hello Hilt") 25 | } 26 | } -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # AGP 8.x 警告生成 2 | # Please add these rules to your existing keep rules in order to suppress warnings. 3 | # This is generated automatically by the Android Gradle plugin. 4 | -dontwarn dalvik.system.VMStack 5 | -dontwarn javax.lang.model.element.Element 6 | -dontwarn org.bouncycastle.jsse.BCSSLParameters 7 | -dontwarn org.bouncycastle.jsse.BCSSLSocket 8 | -dontwarn org.bouncycastle.jsse.provider.BouncyCastleJsseProvider 9 | -dontwarn org.conscrypt.Conscrypt$Version 10 | -dontwarn org.conscrypt.Conscrypt 11 | -dontwarn org.conscrypt.ConscryptHostnameVerifier 12 | -dontwarn org.openjsse.javax.net.ssl.SSLParameters 13 | -dontwarn org.openjsse.javax.net.ssl.SSLSocket 14 | -dontwarn org.openjsse.net.ssl.OpenJSSE -------------------------------------------------------------------------------- /app/src/main/java/com/quyunshuo/androidbaseframemvvm/app/AppApplication.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.app 2 | 3 | import com.quyunshuo.androidbaseframemvvm.base.BaseApplication 4 | import dagger.hilt.android.HiltAndroidApp 5 | import org.greenrobot.eventbus.EventBus 6 | 7 | /** 8 | * App壳 9 | * 10 | * @author Qu Yunshuo 11 | * @since 4/23/21 6:08 PM 12 | */ 13 | @HiltAndroidApp 14 | class AppApplication : BaseApplication() { 15 | 16 | override fun onCreate() { 17 | // 开启EventBusAPT,优化反射效率 当组件作为App运行时需要将添加的Index注释掉 因为找不到对应的类了 18 | EventBus 19 | .builder() 20 | // .addIndex(MainEventIndex()) 21 | .installDefaultEventBus() 22 | super.onCreate() 23 | } 24 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/ktx/NetUtils.kt: -------------------------------------------------------------------------------- 1 | package com.ad.newad 2 | 3 | import android.content.Context 4 | import android.net.ConnectivityManager 5 | import android.net.NetworkCapabilities 6 | 7 | /** 8 | * 网络相关工具类 9 | */ 10 | object NetUtils { 11 | 12 | /** 13 | * 当前网络是否是 Wi-Fi 14 | */ 15 | fun currentNetIsWiFi(context: Context): Boolean { 16 | val connectivityManager = 17 | context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager 18 | val networkCapabilities = 19 | connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork) 20 | return networkCapabilities?.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) ?: false 21 | } 22 | } -------------------------------------------------------------------------------- /lib_common/src/main/java/com/quyunshuo/androidbaseframemvvm/common/helper/ResponseException.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.common.helper 2 | 3 | /** 4 | * 自定义响应异常的抽象类型 5 | * 6 | * @author Qu Yunshuo 7 | * @since 2021/8/27 9:50 上午 8 | */ 9 | interface IResponseException 10 | 11 | /** 12 | * 请求响应异常,主要为各种code码专门定义的异常 13 | * 14 | * @param type IResponseCode 异常类型枚举,用于标记该异常的类型 15 | * @param msg String 异常信息 16 | * 17 | * @author Qu Yunshuo 18 | * @since 2021/7/9 2:57 下午 19 | */ 20 | class ResponseException(val type: IResponseCode, val msg: String) : Exception(), IResponseException 21 | 22 | /** 23 | * 空异常,表示该异常已经被处理过了,不需要再做额外处理了 24 | * 25 | * @author Qu Yunshuo 26 | * @since 2021/7/9 3:11 下午 27 | */ 28 | class ResponseEmptyException : Exception(), IResponseException -------------------------------------------------------------------------------- /lib_base/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/ktx/ActivityKtx.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.ktx 2 | 3 | import android.app.Activity 4 | import android.view.WindowManager 5 | import androidx.appcompat.app.AppCompatActivity 6 | import androidx.lifecycle.Lifecycle 7 | 8 | /** 9 | * 设置当前 [Activity] 是否允许截屏操作 10 | * 11 | * @receiver [Activity] 12 | * @param isAllow Boolean 是否允许截屏 13 | */ 14 | fun Activity.isAllowScreenCapture(isAllow: Boolean) { 15 | if (isAllow) { 16 | window?.addFlags(WindowManager.LayoutParams.FLAG_SECURE) 17 | } else { 18 | window?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE) 19 | } 20 | } 21 | 22 | /** 23 | * 判断当前 Activity 是否是 [Lifecycle.State.RESUMED] 24 | */ 25 | fun AppCompatActivity.isResumed(): Boolean = lifecycle.currentState == Lifecycle.State.RESUMED 26 | -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/CoilGIFImageLoader.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils 2 | 3 | import android.os.Build.VERSION.SDK_INT 4 | import coil.ImageLoader 5 | import coil.decode.GifDecoder 6 | import coil.decode.ImageDecoderDecoder 7 | import com.quyunshuo.androidbaseframemvvm.base.BaseApplication 8 | 9 | /** 10 | * 用于加载 Gif 的 Coil ImageLoader 11 | * 12 | * @author Qu Yunshuo 13 | * @since 2021/9/6 4:26 下午 14 | */ 15 | object CoilGIFImageLoader { 16 | 17 | val imageLoader = ImageLoader.Builder(BaseApplication.context) 18 | .componentRegistry { 19 | if (SDK_INT >= 28) { 20 | add(ImageDecoderDecoder(BaseApplication.context)) 21 | } else { 22 | add(GifDecoder()) 23 | } 24 | } 25 | .build() 26 | } -------------------------------------------------------------------------------- /lib_common/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /module_home/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /module_home/src/androidTest/java/com/quyunshuo/module/home/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.module.home 2 | 3 | import androidx.test.platform.app.InstrumentationRegistry 4 | import androidx.test.ext.junit.runners.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext 22 | assertEquals("com.quyunshuo.module.home.test", appContext.packageName) 23 | } 24 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/mvvm/m/BaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.mvvm.m 2 | 3 | import kotlinx.coroutines.Dispatchers 4 | import kotlinx.coroutines.flow.Flow 5 | import kotlinx.coroutines.flow.FlowCollector 6 | import kotlinx.coroutines.flow.flow 7 | import kotlinx.coroutines.flow.flowOn 8 | 9 | /** 10 | * 仓库层 Repository 基类 11 | * 12 | * @author Qu Yunshuo 13 | * @since 8/27/20 14 | */ 15 | open class BaseRepository { 16 | 17 | /** 18 | * 发起请求封装 19 | * 该方法将flow的执行切换至IO线程 20 | * 21 | * @param requestBlock 请求的整体逻辑 22 | * @return Flow @BuilderInference block: suspend FlowCollector.() -> Unit 23 | */ 24 | protected fun request(requestBlock: suspend FlowCollector.() -> Unit): Flow { 25 | return flow(block = requestBlock).flowOn(Dispatchers.IO) 26 | } 27 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/ClipboardUtils.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils 2 | 3 | import android.content.ClipData 4 | import android.content.ClipboardManager 5 | import android.content.Context 6 | import com.quyunshuo.androidbaseframemvvm.base.BaseApplication 7 | 8 | /** 9 | * 剪切板工具类 10 | * 11 | * @author Qu Yunshuo 12 | * @since 2023/5/31 10:27 13 | */ 14 | object ClipboardUtils { 15 | 16 | /** 17 | * 复制内容到剪切板 18 | * 19 | * @param text String 内容 20 | * @param label String 标签,用于区分内容 21 | */ 22 | fun copyToClipboard(text: String, label: String = "") { 23 | val clipboard = 24 | BaseApplication.context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager 25 | val clip = ClipData.newPlainText(label, text) 26 | clipboard.setPrimaryClip(clip) 27 | } 28 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/ktx/LifecycleOwnerKtx.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.ktx 2 | 3 | import androidx.lifecycle.LifecycleOwner 4 | import androidx.lifecycle.LiveData 5 | 6 | /** 7 | * 对LiveData订阅的简化封装 8 | * 9 | * 使用示例 10 | * ``` 11 | * override fun initObserve() { 12 | * observeLiveData(mViewModel.stateViewLD, ::processStateViewLivaData) 13 | * } 14 | * 15 | * private fun processStateViewLivaData(data: StateLayoutEnum) { 16 | * ... 17 | * } 18 | * ``` 19 | * 20 | * @receiver LifecycleOwner 21 | * @param liveData LiveData 需要进行订阅的LiveData 22 | * @param action action: (t: T) -> Unit 处理订阅内容的方法 23 | * @return Unit 24 | */ 25 | inline fun LifecycleOwner.observeLiveData( 26 | liveData: LiveData, 27 | crossinline action: (t: T) -> Unit 28 | ) { 29 | liveData.observe(this) { it?.let { t -> action(t) } } 30 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/network/NetworkTypeEnum.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils.network 2 | 3 | /** 4 | * 网络类型的枚举 5 | * 6 | * @author Qu Yunshuo 7 | * @since 2021/8/22 10:35 下午 8 | */ 9 | enum class NetworkTypeEnum { 10 | 11 | /** 12 | * 使用蜂窝移动网络传输 13 | */ 14 | TRANSPORT_CELLULAR, 15 | 16 | /** 17 | * 使用Wi-Fi传输 18 | */ 19 | TRANSPORT_WIFI, 20 | 21 | /** 22 | * 使用蓝牙传输 23 | */ 24 | TRANSPORT_BLUETOOTH, 25 | 26 | /** 27 | * 使用以太网传输 28 | */ 29 | TRANSPORT_ETHERNET, 30 | 31 | /** 32 | * 使用 VPN 传输 33 | */ 34 | TRANSPORT_VPN, 35 | 36 | /** 37 | * 使用 Wi-Fi Aware 传输 38 | */ 39 | TRANSPORT_WIFI_AWARE, 40 | 41 | /** 42 | * 使用 LoWPAN 传输 43 | */ 44 | TRANSPORT_LOWPAN, 45 | 46 | /** 47 | * 其他 48 | */ 49 | OTHER 50 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/view/OnSingleClickListener.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.view 2 | 3 | import android.view.View 4 | 5 | /** 6 | * 带有防抖效果的单击监听 7 | * 8 | * @param mDelayTime Int 防抖间隔时间,单位是毫秒,默认值 500ms 9 | * @param mListener (v: View) -> Unit 具体的点击事件 10 | * 11 | * @author Qu Yunshuo 12 | * @since 2023/3/15 23:39 13 | */ 14 | class OnSingleClickListener( 15 | private val mDelayTime: Int = 500, 16 | private val mListener: (v: View) -> Unit 17 | ) : View.OnClickListener { 18 | 19 | /** 20 | * 上次有效点击的时间 21 | */ 22 | private var mLastClickTime = 0L 23 | override fun onClick(v: View) { 24 | val currentTimeMillis = System.currentTimeMillis() 25 | if (currentTimeMillis - mLastClickTime >= mDelayTime) { 26 | mLastClickTime = currentTimeMillis 27 | mListener.invoke(v) 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /module_home/src/main/java/com/quyunshuo/module/home/di/DIHomeNetServiceModule.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.module.home.di 2 | 3 | import com.quyunshuo.module.home.net.HomeApiService 4 | import dagger.Module 5 | import dagger.Provides 6 | import dagger.hilt.InstallIn 7 | import dagger.hilt.components.SingletonComponent 8 | import retrofit2.Retrofit 9 | import javax.inject.Singleton 10 | 11 | /** 12 | * 全局作用域的Home组件网络接口代理依赖注入模块 13 | * 14 | * @author Qu Yunshuo 15 | * @since 6/4/21 5:51 PM 16 | */ 17 | @Module 18 | @InstallIn(SingletonComponent::class) 19 | class DIHomeNetServiceModule { 20 | 21 | /** 22 | * Home模块的[HomeApiService]依赖提供方法 23 | * 24 | * @param retrofit Retrofit 25 | * @return HomeApiService 26 | */ 27 | @Singleton 28 | @Provides 29 | fun provideHomeApiService(retrofit: Retrofit): HomeApiService { 30 | return retrofit.create(HomeApiService::class.java) 31 | } 32 | } -------------------------------------------------------------------------------- /lib_common/src/main/java/com/quyunshuo/androidbaseframemvvm/common/helper/ResponseCodeEnum.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.common.helper 2 | 3 | /** 4 | * 请求响应code枚举抽象 5 | * 6 | * @author Qu Yunshuo 7 | * @since 2021/7/9 2:56 下午 8 | */ 9 | interface IResponseCode { 10 | 11 | /** 12 | * 获取该枚举的code码 13 | * @return Int 14 | */ 15 | fun getCode(): Int 16 | 17 | /** 18 | * 获取该枚举的描述 19 | * @return String 20 | */ 21 | fun getMessage(): String 22 | } 23 | 24 | /** 25 | * 请求响应code的枚举 26 | * 27 | * @author Qu Yunshuo 28 | * @since 2021/7/9 2:55 下午 29 | */ 30 | enum class ResponseCodeEnum : IResponseCode { 31 | 32 | // 通用异常 33 | ERROR { 34 | override fun getCode() = 100 35 | override fun getMessage() = "处理失败" 36 | }, 37 | 38 | // 成功 39 | SUCCESS { 40 | override fun getCode() = 200 41 | override fun getMessage() = "成功" 42 | } 43 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/app/ApplicationLifecycle.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.app 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | 6 | /** 7 | * Application 生命周期 用于初始化各个组件 8 | * 9 | * @author Qu Yunshuo 10 | * @since 4/23/21 5:22 PM 11 | */ 12 | interface ApplicationLifecycle { 13 | 14 | /** 15 | * 同[Application.attachBaseContext] 16 | * @param context Context 17 | */ 18 | fun onAttachBaseContext(context: Context) 19 | 20 | /** 21 | * 同[Application.onCreate] 22 | * @param application Application 23 | */ 24 | fun onCreate(application: Application) 25 | 26 | /** 27 | * 同[Application.onTerminate] 28 | * @param application Application 29 | */ 30 | fun onTerminate(application: Application) 31 | 32 | /** 33 | * 主线程前台初始化 34 | * @return MutableList<() -> String> 初始化方法集合 35 | */ 36 | fun initByFrontDesk(): MutableList<() -> String> 37 | 38 | /** 39 | * 不需要立即初始化的放在这里进行后台初始化 40 | */ 41 | fun initByBackstage() 42 | } -------------------------------------------------------------------------------- /module_home/src/main/res/layout/home_activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 22 | 23 | -------------------------------------------------------------------------------- /module_home/src/main/java/com/quyunshuo/module/home/activity/HomeViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.module.home.activity 2 | 3 | import android.util.Log 4 | import androidx.lifecycle.MutableLiveData 5 | import com.quyunshuo.androidbaseframemvvm.base.ktx.launchIO 6 | import com.quyunshuo.androidbaseframemvvm.base.mvvm.vm.BaseViewModel 7 | import dagger.hilt.android.lifecycle.HiltViewModel 8 | import kotlinx.coroutines.flow.catch 9 | import kotlinx.coroutines.flow.collect 10 | import javax.inject.Inject 11 | 12 | /** 13 | * 首页的VM层 14 | * 15 | * @property mRepository HomeRepository 仓库层 通过Hilt注入 16 | * 17 | * @author Qu Yunshuo 18 | * @since 5/25/21 5:41 PM 19 | */ 20 | @HiltViewModel 21 | class HomeViewModel @Inject constructor(private val mRepository: HomeRepository) : BaseViewModel() { 22 | 23 | val data = MutableLiveData() 24 | 25 | /** 26 | * 模拟获取数据 27 | */ 28 | fun getData() { 29 | launchIO { 30 | mRepository.getData() 31 | .catch { Log.d("qqq", "getData: $it") } 32 | .collect { data.postValue(it) } 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /lib_common/src/main/java/com/quyunshuo/androidbaseframemvvm/common/helper/ExceptionHandler.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.common.helper 2 | 3 | import com.quyunshuo.androidbaseframemvvm.base.utils.toast 4 | 5 | /** 6 | * 响应code异常统一处理 7 | * 8 | * 该方法主要做两件事: 9 | * 10 | * - 1.做统一的code码处理 11 | * - 2.未进行统一处理的code码会被转换为自定义异常[ResponseException]抛出 12 | * 13 | * 使用方式为:进行统一处理的异常进行抛出[ResponseEmptyException],未进行处理的code抛出[ResponseException],成功状态下执行[successBlock] 14 | * 15 | * @param code Int code码 16 | * @param msg String? 错误信息 17 | * @param successBlock suspend () -> Unit 没有异常的情况下执行的方法体 可以在此处进行数据的发射 18 | * @throws ResponseException 未进行处理的异常会进行抛出,让ViewModel去做进一步处理 19 | */ 20 | @Throws(ResponseException::class) 21 | suspend fun responseCodeExceptionHandler( 22 | code: Int, 23 | msg: String?, 24 | successBlock: suspend () -> Unit 25 | ) { 26 | // 进行异常的处理 27 | when (code) { 28 | ResponseCodeEnum.ERROR.getCode() -> { 29 | toast(ResponseCodeEnum.ERROR.getMessage()) 30 | throw ResponseEmptyException() 31 | } 32 | ResponseCodeEnum.SUCCESS.getCode() -> successBlock.invoke() 33 | } 34 | } -------------------------------------------------------------------------------- /lib_common/build.gradle: -------------------------------------------------------------------------------- 1 | //**************************************** 2 | //********* lib_common 的配置文件 ********** 3 | //**************************************** 4 | 5 | plugins { 6 | alias(libs.plugins.library) 7 | alias(libs.plugins.kotlin) 8 | alias(libs.plugins.hilt) 9 | id "kotlin-kapt" 10 | } 11 | 12 | apply from: '../base_lib.gradle' 13 | 14 | import com.quyunshuo.androidbaseframemvvm.buildsrc.* 15 | 16 | android { 17 | 18 | defaultConfig { 19 | resValue "string", "BUGLY_APP_ID", SDKKeyConfig.BUGLY_APP_ID 20 | } 21 | 22 | buildFeatures { 23 | viewBinding = true 24 | } 25 | 26 | resourcePrefix "common_" 27 | namespace 'com.quyunshuo.androidbaseframemvvm.common' 28 | } 29 | 30 | dependencies { 31 | implementation fileTree(dir: "libs", include: ["*.jar"]) 32 | 33 | api project(path: ':lib_base') 34 | 35 | api DependencyConfig.JetPack.HiltCore 36 | 37 | kapt DependencyConfig.GitHub.ARouteCompiler 38 | kapt DependencyConfig.GitHub.EventBusAPT 39 | kapt DependencyConfig.GitHub.AutoServiceAnnotations 40 | kapt DependencyConfig.JetPack.HiltApt 41 | kapt DependencyConfig.JetPack.LifecycleCompilerAPT 42 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/AppUtils.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils 2 | 3 | import android.content.pm.PackageInfo 4 | import android.os.Build 5 | import com.quyunshuo.androidbaseframemvvm.base.BaseApplication 6 | 7 | /** 8 | * App 相关工具类 9 | * 10 | * @author Qu Yunshuo 11 | * @sine 2023/2/13 23:15 12 | */ 13 | class AppUtils { 14 | 15 | /** 16 | * 获取当前 App 版本号 17 | * 18 | * @return Long 19 | */ 20 | fun getAppVersionCode(): Long { 21 | return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { 22 | getAppPackageInfo().longVersionCode 23 | } else { 24 | getAppPackageInfo().versionCode.toLong() 25 | } 26 | } 27 | 28 | /** 29 | * 获取当前 App 版本名 30 | * 31 | * @return String 32 | */ 33 | fun getAppVersionName(): String = getAppPackageInfo().versionName 34 | 35 | /** 36 | * 获取当前 App 的 [PackageInfo] 37 | * 38 | * @return PackageInfo 39 | */ 40 | fun getAppPackageInfo(): PackageInfo { 41 | return BaseApplication.context 42 | .packageManager 43 | .getPackageInfo(BaseApplication.context.packageName, 0) 44 | } 45 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/network/AutoRegisterNetListener.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils.network 2 | 3 | import androidx.lifecycle.Lifecycle 4 | import androidx.lifecycle.LifecycleObserver 5 | import androidx.lifecycle.OnLifecycleEvent 6 | 7 | /** 8 | * 自动注册网络状态监听 9 | * 使用的是[androidx.lifecycle.LifecycleObserver]来同步生命周期 10 | * 11 | * @author Qu Yunshuo 12 | * @since 2021/7/11 4:56 下午 13 | */ 14 | class AutoRegisterNetListener constructor(listener: NetworkStateChangeListener) : 15 | LifecycleObserver { 16 | 17 | /** 18 | * 当前需要自动注册的监听器 19 | */ 20 | private var mListener: NetworkStateChangeListener? = null 21 | 22 | init { 23 | mListener = listener 24 | } 25 | 26 | @OnLifecycleEvent(Lifecycle.Event.ON_RESUME) 27 | fun register() { 28 | mListener?.run { NetworkStateClient.setListener(this) } 29 | } 30 | 31 | @OnLifecycleEvent(Lifecycle.Event.ON_PAUSE) 32 | fun unregister() { 33 | NetworkStateClient.removeListener() 34 | } 35 | 36 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) 37 | fun clean() { 38 | NetworkStateClient.removeListener() 39 | mListener = null 40 | } 41 | } -------------------------------------------------------------------------------- /lib_common/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 15 | 16 | 19 | 20 | 21 | 25 | 26 | 27 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/ktx/ViewPager2Ktx.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.ktx 2 | 3 | import android.view.View 4 | import androidx.recyclerview.widget.RecyclerView 5 | import androidx.viewpager2.widget.ViewPager2 6 | 7 | /** 8 | * 设置ViewPager2的过度滚动模式为绝不允许用户过度滚动此视图 9 | * @receiver ViewPager2 10 | */ 11 | fun ViewPager2.setOverScrollModeToNever() { 12 | val childView: View = this.getChildAt(0) 13 | if (childView is RecyclerView) { 14 | childView.overScrollMode = RecyclerView.OVER_SCROLL_NEVER 15 | } 16 | } 17 | 18 | /** 19 | * 设置ViewPager2的过度滚动模式为始终允许用户过度滚动此视图,前提是它是可以滚动的视图 20 | * @receiver ViewPager2 21 | */ 22 | fun ViewPager2.setOverScrollModeToAlways() { 23 | val childView: View = this.getChildAt(0) 24 | if (childView is RecyclerView) { 25 | childView.overScrollMode = RecyclerView.OVER_SCROLL_ALWAYS 26 | } 27 | } 28 | 29 | /** 30 | * 设置ViewPager2的过度滚动模式为仅当内容大到足以有意义地滚动时,才允许用户过度滚动此视图,前提是它是可以滚动的视图。 31 | * @receiver ViewPager2 32 | */ 33 | fun ViewPager2.setOverScrollModeToIfContentScrolls() { 34 | val childView: View = this.getChildAt(0) 35 | if (childView is RecyclerView) { 36 | childView.overScrollMode = RecyclerView.OVER_SCROLL_IF_CONTENT_SCROLLS 37 | } 38 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Proguard folder generated by Eclipse 24 | proguard/ 25 | 26 | # Log Files 27 | *.log 28 | 29 | # Android Studio Navigation editor temp files 30 | .navigation/ 31 | 32 | # Android Studio captures folder 33 | captures/ 34 | 35 | # IntelliJ 36 | *.iml 37 | .idea/workspace.xml 38 | .idea/tasks.xml 39 | .idea/gradle.xml 40 | .idea/assetWizardSettings.xml 41 | .idea/dictionaries 42 | .idea/libraries 43 | .idea/caches 44 | .idea/* 45 | 46 | # Keystore files 47 | # Uncomment the following line if you do not want to check your keystore files in. 48 | #*.jks 49 | 50 | # External native build folder generated in Android Studio 2.2 and later 51 | .externalNativeBuild 52 | 53 | # Google Services (e.g. APIs or Firebase) 54 | google-services.json 55 | 56 | # Freeline 57 | freeline.py 58 | freeline/ 59 | freeline_project_description.json 60 | 61 | # fastlane 62 | fastlane/report.xml 63 | fastlane/Preview.html 64 | fastlane/screenshots 65 | fastlane/test_output 66 | fastlane/readme.md 67 | -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/status/imp/BaseFrameViewStatusHelperImp.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils.status.imp 2 | 3 | import android.os.Bundle 4 | import com.quyunshuo.androidbaseframemvvm.base.utils.status.ViewStatusHelper 5 | 6 | /** 7 | * @author DBoy 2021/7/8 8 | * 9 | * - 文件描述 : 视图,activity,fragment重建帮助类 10 | */ 11 | open class BaseFrameViewStatusHelperImp(parentViewStatusHelper: ViewStatusHelper? = null) : ViewStatusHelper(parentViewStatusHelper) { 12 | /** 13 | * 重建标记key 以包名保存数据可以防止嵌套层级出现重复Key 14 | */ 15 | private val KEY_RECREATE = "com.quyunshuo.androidbaseframemvvm.base.utils.status.BaseFrameViewStatusHelperImp.Recreate" 16 | 17 | /** 18 | * 是否重建 19 | */ 20 | var isRecreate = false 21 | private set 22 | 23 | 24 | /** 25 | * 恢复状态 26 | */ 27 | override fun onRestoreInstanceStatus(savedInstanceState: Bundle?) { 28 | super.onRestoreInstanceStatus(savedInstanceState) 29 | isRecreate = savedInstanceState?.getBoolean(KEY_RECREATE) ?: false 30 | } 31 | 32 | /** 33 | * 保存状态 34 | */ 35 | override fun onSaveInstanceState(bundle: Bundle) { 36 | super.onSaveInstanceState(bundle) 37 | bundle.putBoolean(KEY_RECREATE, true) 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /module_home/src/main/java/com/quyunshuo/module/home/activity/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.module.home.activity 2 | 3 | import android.graphics.Color 4 | import androidx.activity.viewModels 5 | import com.quyunshuo.androidbaseframemvvm.base.ktx.observeLiveData 6 | import com.quyunshuo.androidbaseframemvvm.common.ui.BaseActivity 7 | import com.quyunshuo.module.home.databinding.HomeActivityMainBinding 8 | import dagger.hilt.android.AndroidEntryPoint 9 | 10 | /** 11 | * 首页 12 | * 13 | * @author Qu Yunshuo 14 | * @since 5/22/21 2:26 PM 15 | */ 16 | @AndroidEntryPoint 17 | class MainActivity : BaseActivity() { 18 | 19 | /** 20 | * 通过 viewModels() + Hilt 获取 ViewModel 实例 21 | */ 22 | override val mViewModel by viewModels() 23 | 24 | override fun createVB() = HomeActivityMainBinding.inflate(layoutInflater) 25 | 26 | override fun HomeActivityMainBinding.initView() {} 27 | 28 | override fun initObserve() { 29 | observeLiveData(mViewModel.data, ::processData) 30 | } 31 | 32 | private fun processData(data: String) { 33 | mBinding.vTvHello.text = data 34 | mBinding.vTvHello.setTextColor(Color.BLUE) 35 | } 36 | 37 | override fun initRequestData() { 38 | // 模拟获取数据 39 | mViewModel.getData() 40 | } 41 | } -------------------------------------------------------------------------------- /lib_common/src/main/java/com/quyunshuo/androidbaseframemvvm/common/ui/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.common.ui 2 | 3 | import android.util.Log 4 | import androidx.viewbinding.ViewBinding 5 | import com.quyunshuo.androidbaseframemvvm.base.mvvm.v.BaseFrameActivity 6 | import com.quyunshuo.androidbaseframemvvm.base.mvvm.vm.BaseViewModel 7 | import com.quyunshuo.androidbaseframemvvm.base.utils.ActivityStackManager 8 | import com.quyunshuo.androidbaseframemvvm.base.utils.AndroidBugFixUtils 9 | import com.quyunshuo.androidbaseframemvvm.base.utils.BarUtils 10 | 11 | /** 12 | * Activity基类 13 | * 14 | * @author Qu Yunshuo 15 | * @since 8/27/20 16 | */ 17 | abstract class BaseActivity : BaseFrameActivity() { 18 | 19 | /** 20 | * 设置状态栏 21 | * 子类需要自定义时重写该方法即可 22 | * @return Unit 23 | */ 24 | override fun setStatusBar() { 25 | BarUtils.transparentStatusBar(this) 26 | BarUtils.setStatusBarLightMode(this, true) 27 | } 28 | 29 | override fun onResume() { 30 | super.onResume() 31 | Log.d("ActivityLifecycle", "ActivityStack: ${ActivityStackManager.activityStack}") 32 | } 33 | 34 | override fun onDestroy() { 35 | super.onDestroy() 36 | // 解决某些特定机型会触发的Android本身的Bug 37 | AndroidBugFixUtils().fixSoftInputLeaks(this) 38 | } 39 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Automatically convert third-party libraries to use AndroidX 19 | android.enableJetifier=true 20 | # Kotlin code style for this project: "official" or "obsolete": 21 | kotlin.code.style=official 22 | android.defaults.buildfeatures.buildconfig=true 23 | android.nonTransitiveRClass=false 24 | android.nonFinalResIds=false 25 | android.enableR8.fullMode=false -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 12 | 13 | 23 | 24 | 25 | 28 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/Utils.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils 2 | 3 | import android.util.Log 4 | import com.alibaba.android.arouter.launcher.ARouter 5 | import kotlinx.coroutines.Dispatchers 6 | import kotlinx.coroutines.delay 7 | import kotlinx.coroutines.flow.catch 8 | import kotlinx.coroutines.flow.flow 9 | import kotlinx.coroutines.flow.flowOn 10 | 11 | /** 12 | * 使用 Flow 做的简单的轮询 13 | * 请使用单独的协程来进行管理该 Flow 14 | * Flow 仍有一些操作符是实验性的 使用时需添加 @InternalCoroutinesApi 注解 15 | * @param intervals 轮询间隔时间/毫秒 16 | * @param block 需要执行的代码块 17 | */ 18 | suspend fun startPolling(intervals: Long, block: () -> Unit) { 19 | flow { 20 | while (true) { 21 | delay(intervals) 22 | emit(0) 23 | } 24 | } 25 | .catch { Log.e("flow", "startPolling: $it") } 26 | .flowOn(Dispatchers.Main) 27 | .collect { block.invoke() } 28 | } 29 | /**************************************************************************************************/ 30 | 31 | /** 32 | * 发送普通EventBus事件 33 | */ 34 | fun sendEvent(event: Any) = EventBusUtils.postEvent(event) 35 | 36 | /**************************************************************************************************/ 37 | /** 38 | * 阿里路由不带参数跳转 39 | * @param routerUrl String 路由地址 40 | */ 41 | fun aRouterJump(routerUrl: String) { 42 | ARouter.getInstance().build(routerUrl).navigation() 43 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/ktx/VideoViewKtx.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.ktx 2 | 3 | import android.media.MediaPlayer 4 | import android.view.ViewGroup 5 | import android.widget.VideoView 6 | 7 | /** 8 | * 根据视频的尺寸与容器尺寸比例,动态调整 [VideoView] 的尺寸以适应视频的尺寸 9 | * 解决 [VideoView] 尺寸比例与视频尺寸比例不一致导致视频拉伸的问题 10 | * 11 | * 容器可以是屏幕或者 [VideoView] 12 | * 13 | * 使用方式: 14 | * 1. 通过 [VideoView.setOnPreparedListener] 向 [VideoView] 设置 [MediaPlayer.OnPreparedListener] 监听 15 | * 2. 通过 [MediaPlayer.OnPreparedListener] 回调获取到视频的真实宽高,调用该方法传入参数进行适配 16 | * 3. 如果需要考虑横竖屏切换,请在横竖屏改变监听回调中再次调用该方法进行适配 17 | * 18 | * @receiver [VideoView] 19 | * @param containerW Float 容器的真实宽 20 | * @param containerH Float 容器的真实高 21 | * @param videoW Float 视频的真实宽 22 | * @param videoH Float 视频的真实高 23 | */ 24 | fun VideoView.resetVideoViewDimensions( 25 | containerW: Float, 26 | containerH: Float, 27 | videoW: Float, 28 | videoH: Float, 29 | ) { 30 | // 计算宽高比进行调整宽高 31 | this.layoutParams = if (videoW / containerW < videoH / containerH) { 32 | this.layoutParams.apply { 33 | width = ViewGroup.LayoutParams.WRAP_CONTENT 34 | height = ViewGroup.LayoutParams.MATCH_PARENT 35 | } 36 | } else { 37 | this.layoutParams.apply { 38 | width = ViewGroup.LayoutParams.MATCH_PARENT 39 | height = ViewGroup.LayoutParams.WRAP_CONTENT 40 | } 41 | } 42 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/ToastUtils.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils 2 | 3 | import android.os.Build 4 | import android.os.Handler 5 | import android.os.Looper 6 | import android.widget.Toast 7 | import androidx.annotation.StringRes 8 | import com.quyunshuo.androidbaseframemvvm.base.BaseApplication 9 | 10 | private val mToastHandler by lazy { Handler(Looper.getMainLooper()) } 11 | 12 | private var mToast: Toast? = null 13 | 14 | @JvmOverloads 15 | fun toast(text: String, duration: Int = Toast.LENGTH_SHORT) { 16 | postToast(text, duration) 17 | } 18 | 19 | @JvmOverloads 20 | fun toast(@StringRes id: Int, duration: Int = Toast.LENGTH_SHORT) { 21 | postToast(getString(id), duration) 22 | } 23 | 24 | private fun postToast(text: String, duration: Int) { 25 | mToastHandler.post { 26 | setToast(text, duration) 27 | mToast?.show() 28 | } 29 | } 30 | 31 | private fun setToast(text: String, duration: Int) { 32 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.P) { 33 | if (mToast == null) { 34 | mToast = Toast.makeText(BaseApplication.context, text, duration) 35 | } else { 36 | mToast?.duration = duration 37 | mToast?.setText(text) 38 | } 39 | } else { 40 | if (mToast != null) { 41 | mToast?.cancel() 42 | mToast = null 43 | } 44 | mToast = Toast.makeText(BaseApplication.context, text, duration) 45 | } 46 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/EventBusUtils.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils 2 | 3 | import org.greenrobot.eventbus.EventBus 4 | 5 | /** 6 | * @Author: QuYunShuo 7 | * @Time: 2020/8/29 8 | * @Class: EventBusUtil 9 | * @Remark: EventBus工具类 10 | */ 11 | object EventBusUtils { 12 | 13 | /** 14 | * 订阅 15 | * @param subscriber 订阅者 16 | */ 17 | fun register(subscriber: Any) = EventBus.getDefault().register(subscriber) 18 | 19 | /** 20 | * 解除注册 21 | * @param subscriber 订阅者 22 | */ 23 | fun unRegister(subscriber: Any) = EventBus.getDefault().unregister(subscriber) 24 | 25 | /** 26 | * 发送普通事件 27 | * @param event 事件 28 | */ 29 | fun postEvent(event: Any) = EventBus.getDefault().post(event) 30 | 31 | /** 32 | * 发送粘性事件 33 | * @param stickyEvent 粘性事件 34 | */ 35 | fun postStickyEvent(stickyEvent: Any) = EventBus.getDefault().postSticky(stickyEvent) 36 | 37 | /** 38 | * 手动获取粘性事件 39 | * @param stickyEventType 粘性事件 40 | * @param 事件泛型 41 | * @return 返回给定事件类型的最近粘性事件 42 | */ 43 | fun getStickyEvent(stickyEventType: Class): T = 44 | EventBus.getDefault().getStickyEvent(stickyEventType) 45 | 46 | /** 47 | * 手动删除粘性事件 48 | * @param stickyEventType 粘性事件 49 | * @param 事件泛型 50 | * @return 返回给定事件类型的最近粘性事件 51 | */ 52 | fun removeStickyEvent(stickyEventType: Class): T = 53 | EventBus.getDefault().removeStickyEvent(stickyEventType) 54 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/RegisterEventBus.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils 2 | 3 | /** 4 | * 辅助注册 EventBus 的注解 5 | * 6 | * - **使用方式:** 7 | * 在基类中的 `onCreate()`、`onDestroy()` 生命周期回调中去判断当前 Class 对象是否使用了该注解, 8 | * 然后根据结果去注册或反注册 9 | * 10 | * - **为什么不统一注册:** 11 | * 统一注册会在 EventBus 内部集合中留存,每次发送事件时,会遍历集合,过多无用的注册会导致速度变慢, 12 | * 所以最好的方式就是根据需要进行注册,避免无意义的全部注册 13 | * 14 | * - **sample:** 15 | * ``` 16 | * abstract class BaseActivity : AppCompatActivity() { 17 | * 18 | * // 是否有 [RegisterEventBus] 注解 , 避免重复调用 [Class.isAnnotation] 19 | * private var mHaveRegisterEventBus = false 20 | * override fun onCreate(savedInstanceState: Bundle?) { 21 | * super.onCreate(savedInstanceState) 22 | * // 根据子类是否有 RegisterEventBus 注解決定是否进行注册 EventBus 23 | * if (javaClass.isAnnotationPresent(RegisterEventBus::class.java)) { 24 | * mHaveRegisterEventBus = true 25 | * EventBusUtils.register(this) 26 | * } 27 | * } 28 | * 29 | * override fun onDestroy() { 30 | * // 根据子类是否有 RegisterEventBus 注解决定是否进行注册 EventBus 31 | * if (mHaveRegisterEventBus) { 32 | * EventBusUtils.unRegister(this) 33 | * } 34 | * super.onDestroy() 35 | * } 36 | * } 37 | * 38 | * // 子类: 39 | * @RegisterEventBus 40 | * class SampleActivity : BaseActivity() 41 | * ``` 42 | * 43 | * @author Qu Yunshuo 44 | * @since 2020/8/29 45 | */ 46 | @Target(AnnotationTarget.CLASS) 47 | @Retention(AnnotationRetention.RUNTIME) 48 | annotation class RegisterEventBus -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/utils/AndroidBugFixUtils.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.utils 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.view.View 6 | import android.view.inputmethod.InputMethodManager 7 | import com.quyunshuo.androidbaseframemvvm.base.BaseApplication 8 | import java.lang.reflect.Field 9 | 10 | /** 11 | * 解决 Android 自身的 Bug 12 | * 13 | * @author Qu Yunshuo 14 | * @since 2020/10/22 15 | */ 16 | class AndroidBugFixUtils { 17 | 18 | /** 19 | * 解决 InputMethodManager 造成的内存泄露 20 | * 21 | * 使用方式: 22 | * ``` 23 | * override fun onDestroy() { 24 | * AndroidBugFixUtils().fixSoftInputLeaks(this) 25 | * super.onDestroy() 26 | * } 27 | * ``` 28 | */ 29 | fun fixSoftInputLeaks(activity: Activity) { 30 | val imm = 31 | BaseApplication.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager 32 | val leakViews = arrayOf("mLastSrvView", "mCurRootView", "mServedView", "mNextServedView") 33 | for (leakView in leakViews) { 34 | try { 35 | val leakViewField: Field = 36 | InputMethodManager::class.java.getDeclaredField(leakView) ?: continue 37 | if (!leakViewField.isAccessible) leakViewField.isAccessible = true 38 | val view: Any? = leakViewField.get(imm) 39 | if (view !is View) continue 40 | if (view.rootView == activity.window.decorView.rootView) { 41 | leakViewField.set(imm, null) 42 | } 43 | } catch (t: Throwable) { 44 | } 45 | } 46 | } 47 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/app/LoadModuleProxy.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.app 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.util.Log 6 | import java.util.* 7 | 8 | /** 9 | * 加载组件代理类 10 | * 组件初始化的工作将由该代理类代理实现 11 | * 12 | * @author Qu Yunshuo 13 | * @since 4/23/21 5:37 PM 14 | */ 15 | class LoadModuleProxy : ApplicationLifecycle { 16 | 17 | private var mLoader: ServiceLoader = 18 | ServiceLoader.load(ApplicationLifecycle::class.java) 19 | 20 | /** 21 | * 同[Application.attachBaseContext] 22 | * @param context Context 23 | */ 24 | override fun onAttachBaseContext(context: Context) { 25 | mLoader.forEach { 26 | Log.d("ApplicationInit", it.toString()) 27 | it.onAttachBaseContext(context) 28 | } 29 | } 30 | 31 | /** 32 | * 同[Application.onCreate] 33 | * @param application Application 34 | */ 35 | override fun onCreate(application: Application) { 36 | mLoader.forEach { it.onCreate(application) } 37 | } 38 | 39 | /** 40 | * 同[Application.onTerminate] 41 | * @param application Application 42 | */ 43 | override fun onTerminate(application: Application) { 44 | mLoader.forEach { it.onTerminate(application) } 45 | } 46 | 47 | /** 48 | * 主线程前台初始化 49 | * @return MutableList<() -> String> 初始化方法集合 50 | */ 51 | override fun initByFrontDesk(): MutableList<() -> String> { 52 | val list: MutableList<() -> String> = mutableListOf() 53 | mLoader.forEach { list.addAll(it.initByFrontDesk()) } 54 | return list 55 | } 56 | 57 | /** 58 | * 不需要立即初始化的放在这里进行后台初始化 59 | */ 60 | override fun initByBackstage() { 61 | mLoader.forEach { it.initByBackstage() } 62 | } 63 | } -------------------------------------------------------------------------------- /lib_base/src/main/java/com/quyunshuo/androidbaseframemvvm/base/ktx/SizeUnitKtx.kt: -------------------------------------------------------------------------------- 1 | package com.quyunshuo.androidbaseframemvvm.base.ktx 2 | 3 | import android.content.Context 4 | import androidx.fragment.app.Fragment 5 | 6 | /** 7 | * @Author: QuYunShuo 8 | * @Time: 2020/9/17 9 | * @Class: SizeUnitKtx 10 | * @Remark: 尺寸单位换算相关扩展属性 11 | */ 12 | 13 | /** 14 | * dp 转 px 15 | */ 16 | fun Context.dp2px(dpValue: Float): Int { 17 | val scale = resources.displayMetrics.density 18 | return (dpValue * scale + 0.5f).toInt() 19 | } 20 | 21 | /** 22 | * px 转 dp 23 | */ 24 | fun Context.px2dp(pxValue: Float): Int { 25 | val scale = resources.displayMetrics.density 26 | return (pxValue / scale + 0.5f).toInt() 27 | } 28 | 29 | /** 30 | * sp 转 px 31 | */ 32 | fun Context.sp2px(spValue: Float): Int { 33 | val scale = resources.displayMetrics.scaledDensity 34 | return (spValue * scale + 0.5f).toInt() 35 | } 36 | 37 | /** 38 | * px 转 sp 39 | */ 40 | fun Context.px2sp(pxValue: Float): Int { 41 | val scale = resources.displayMetrics.scaledDensity 42 | return (pxValue / scale + 0.5f).toInt() 43 | } 44 | 45 | /** 46 | * dp 转 px 47 | */ 48 | fun Fragment.dp2px(dpValue: Float): Int { 49 | val scale = resources.displayMetrics.density 50 | return (dpValue * scale + 0.5f).toInt() 51 | } 52 | 53 | /** 54 | * px 转 dp 55 | */ 56 | fun Fragment.px2dp(pxValue: Float): Int { 57 | val scale = resources.displayMetrics.density 58 | return (pxValue / scale + 0.5f).toInt() 59 | } 60 | 61 | /** 62 | * sp 转 px 63 | */ 64 | fun Fragment.sp2px(spValue: Float): Int { 65 | val scale = resources.displayMetrics.scaledDensity 66 | return (spValue * scale + 0.5f).toInt() 67 | } 68 | 69 | /** 70 | * px 转 sp 71 | */ 72 | fun Fragment.px2sp(pxValue: Float): Int { 73 | val scale = resources.displayMetrics.scaledDensity 74 | return (pxValue / scale + 0.5f).toInt() 75 | } -------------------------------------------------------------------------------- /module_home/src/main/res/layout/home_activity_internal_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 14 | 15 | 16 | 22 | 23 |