doc: 更新文档
This commit is contained in:
515
README.md
515
README.md
@ -1,26 +1,101 @@
|
||||
# <p align="center"> AndroidBaseFrameMVVM 🐽</p>
|
||||
|
||||
## 框架已进行部分重构,暂未编写文档及示例demo,等有时间后会根据当前框架编写新的文档及示例demo,本文档目前不是最新的
|
||||
>   **AndroidBaseFrameMVVM** 是一个Android工程框架,所使用技术栈为:**组件化、Kotlin、MVVM、Jetpack、Repository、Kotlin-Coroutine-Flow**,本框架既是一个可以开箱即用的工程框架基础层,也是一个很好的学习资源,文档下面会对框架中所使用的一些核心技术进行阐述。该框架作为个人技术积累的产物,会一直更新维护,如果有技术方面的谈论或者框架中的错误点,可以在 **GitHub** 上提 **Issues**,我会及时进行回应。希望这个框架项目能给大家带来帮助,喜欢可以Start🌟。
|
||||
>
|
||||
>   项目地址:[AndroidBaseFrameMVVM](https://github.com/Quyunshuo/AndroidBaseFrameMVVM)
|
||||
|
||||
<p align="center"> AndroidBaseFrameMVVM 是一个 Android 项目 MVVM 架构 开箱即用的框架 </p>
|
||||
## Demo
|
||||
|
||||
<p align="center"> 该框架基于 Kotlin + Flow + Jetpack + MVVM + 组件化 + Repository 模式实现</p>
|
||||
  demo是在另一个demo分支,但是目前的demo分支的代码是以前那一版的demo,目前版本的demo还没有来得及编写,敬请期待吧。
|
||||
|
||||
<p align="center"> 该框架存在的意义一方面是秉承着我和大部分程序猿/媛"懒"的天性,实现可复用、不用重复搭项目架构、开箱微小修改即可上手新开项目,另一方面也是想把自己会的东西写出来,供其他学习这方面知识的同学借鉴和参考 </p>
|
||||
## 框架图示
|
||||
|
||||
<p align="center"> 下面展示该框架的架构图 👾</p>
|
||||
|
||||
<p align="center"><img src="https://github.com/Quyunshuo/AndroidBaseFrameMVVM/blob/master/img/img1.jpg"/> </p>
|
||||
|
||||
<p align="center"> 谷歌 Android 团队 Jetpack 视图模型 👾</p>
|
||||
**谷歌 Android 团队 Jetpack 视图模型:**
|
||||
|
||||
<p align="center"><img src="https://github.com/Quyunshuo/AndroidBaseFrameMVVM/blob/master/img/img2.png"/> </p>
|
||||
|
||||
## 框架技术栈
|
||||
## 模块
|
||||
|
||||
- **app:**
|
||||
|
||||
**app壳** 工程,是依赖所有组件的壳,该模块不应该包含任何代码,它只作为一个空壳存在,由于项目中使用了EventBusAPT技术,需要索引到各业务组件的对应的APT生成类,所以在 **app壳** 内有这一部分的代码。
|
||||
|
||||
- **buildGradleScript:**
|
||||
|
||||
脚本模块,该模块下存放的都是各个组件及封装的一些 **Gradle** 脚本文件。初衷是将所有的脚本统一管理,事实上我在组件内查找脚本的习惯还是没有改掉。
|
||||
|
||||
- **buildSrc:**
|
||||
|
||||
这是一个特殊的文件夹,负责项目的构建,里面存放着一些项目构建时用到的东西,比如项目配置,依赖。这里面还是存放 **Gradle** 插件的地方,一些自定义的 **Gradle** 的插件都需要放在此处。
|
||||
|
||||
- **lib_base:**
|
||||
|
||||
项目的基础公共模块,存放着各种基类封装、对远程库的依赖、以及工具类、三方库封装,该组件是和项目业务无关的,和项目业务相关的公共部分需要放在 **lib_common** 中。
|
||||
|
||||
- **lib_common:**
|
||||
|
||||
项目的业务公共模块,这里面存放着项目里各个业务组件的公共部分,还有一些项目特定需要的一些文件等,该组件是和项目业务有关系的。
|
||||
|
||||
- **lib_net:**
|
||||
|
||||
网络模块,网络模块的配置、封装等,专门设立了一个组件来负责网路模块部分。
|
||||
|
||||
## 组件化相关
|
||||
|
||||
### 组件初始化
|
||||
|
||||
>   为了更好的代码隔离与解耦,在特定组件内使用的SDK及三方库,应该只在该组件内依赖,不应该让该组件的特定SDK及三方库的API暴露给其他不需要用的组件。有一个问题就出现了,SDK及三方库常常需要手动去初始化,而且一般都需要在项目一启动(即 **Application** 中)初始化,但是一个项目肯定只能有一个自定义的 **Application**,该项目中的自定义 **Application** 在 **lib_base** 模块中,并且也是在 **lib_base** 模块中的清单文件中声明的,那其他组件该如何初始化呢?带着这个问题我们一起来深入研究下。
|
||||
|
||||
**常见的组件初始化解决方案:**
|
||||
|
||||
在我的了解范围内,目前有两种最为常见的解决方案:
|
||||
|
||||
- **面向接口编程 + 反射扫描实现类:**
|
||||
|
||||
  该方案是基于接口编程,自定义 **Application** 去实现一个自定义的接口(**interface**),这个接口中定一些和 **Application** 生命周期相对应的抽象方法及其他自定义的抽象方法,每个组件去编写一个实现类,该实现类就类似于一个假的自定义 **Application**,然后在真正的自定义 **Application** 中去通过反射去动态查找当前运行时环境中所有该接口的实现类,并且去进行实例化,然后将这些实现类收集到一个集合中,在 **Application** 的对应声明周期方法中去逐一调用对应方法,以实现各实现类能够和 **Application** 生命周期相同步,并且持有 **Application** 的引用及 **context** 上下文对象,这样我们就可以在组件内模拟 **Application** 的生命周期并初始化SDK和三方库。使用反射还需要做一些异常的处理。该方案是我见过的最常见的方案,在一些商业项目中也见到过。
|
||||
|
||||
- **面向接口编程 + meta-data + 反射:**
|
||||
|
||||
  该方案的后半部分也是和第一种方法一样,通过接口编程实现 **Application** 的生命周期同步,其实这一步是避免不了的,在我的方案中,后半部分也是这样实现的。不同的是前半部分,也就是如何找到接口的实现类,该方案使用的是 **AndroidManifest** 的 **meta-data** 标签,通过每个组件内的 **AndroidManifest** 内去声明一个 **meta-data** 标签,包含该组件实现类的信息,然后在 **Application** 中去找到这些配置信息,然后通过反射去创建这些实现类的实例,再将它们收集到一个集合中,剩下的操作基本相同了。该方案和第一种方案一样都需要处理很多的异常。这种方案我在一些开源项目中见到过,个人认为过于繁琐,还要处理很多的异常。
|
||||
|
||||
**本项目中所使用的方案:**
|
||||
|
||||
- **面向接口编程 + Java的SPI机制(ServiceLoader)+AutoService:**
|
||||
|
||||
  先来认识下 **Java** 的 **SPI** 机制:面向的对象的设计里,我们一般推荐模块之间基于接口编程,模块之间不对实现类进行硬编码。一旦代码里涉及具体的实现类,就违反了可拔插的原则,如果需要替换一种实现,就需要修改代码。为了实现在模块装配的时候不用在程序里动态指明,这就需要一种服务发现机制。**JavaSPI** 就是提供这样的一个机制:为某个接口寻找服务实现的机制。这有点类似 **IOC** 的思想,将装配的控制权移到了程序之外。这段话也是我复制的别人的,听起来很懵逼,大致意思就是我们可以通过 **SPI** 机制将实现类暴露出去。关于如何使用 **SPI**,这里不在陈述,总之是我们在各组件内通过 **SPI** 去将实现类暴露出去,在 **Application** 中我们通过 **Java** 提供的 **SPI** **API** 去获取这些暴露的服务,这样我们就拿到了这些类的实例,剩下的步骤就和上面的方案一样了,通过一个集合遍历实现类调用其相应的方法完成初始化的工作。由于使用 **SPI** 需要在每个模块创建对应的文件配置,这比较麻烦,所以我们使用 **Google** 的 **AutoService** 库来帮助我们自动创建这些配置文件,使用方式也非常的简单,就是在实现类添加一个 **AutoService** 注解。本框架中的核心类是这几个:**lib_base-LoadModuleProxy**、**lib_base-ApplicationLifecycle**。这种方案是我请教的一个米哈游的大佬,这位大佬告诉我在组件化中组件的初始化可以使用 **ServiceLoader** 来做,于是我就去研究了下,最后发现这种方案还不错,比前面提到的两种方案都要简单、安全。
|
||||
|
||||
### 资源命名冲突
|
||||
|
||||
  在组件化方案中,资源命名冲突是一个比较严重的问题,由于在打包时会进行资源的合并,如果两个模块中有两个相同名字的文件,那么最后只会保留一份,如果不知道这个问题的小伙伴,在遇到这个问题时肯定是一脸懵逼的状态。问题既然已经出现,那我们就要去解决,解决办法就是每个组件都用固定的命名前缀,这样就不会出现两个相同的文件的现象了,我们可以在 **build.gradle** 配置文件中去配置前缀限定,如果不按该前缀进行命名,**AS** 就会进行警告提示,配置如下:
|
||||
|
||||
```Groovy
|
||||
android {
|
||||
resourcePrefix "前缀_"
|
||||
}
|
||||
```
|
||||
|
||||
### 组件划分
|
||||
|
||||
  其实组件的划分一直是一个比较难的部分,这里其实也给不到一些非常适合的建议,看是看具体项目而定。
|
||||
|
||||
  关于基础组件通常要以独立可直接复用的角度出现,比如网络模块、二维码识别模块等。
|
||||
|
||||
  关于业务组件,业务组件一般可以进行单独调试,也就是可以作为 **app** 运行,这样才能发挥组件化的一大用处,当项目越来越大,业务组件越来越多时,编译耗时将会是一个非常棘手的问题,但是如果每个业务模块都可以进行的单独调试,那就大大减少了编译时间,同时,开发人员也不需要关注其他组件。
|
||||
|
||||
  关于公共模块,**lib_base** 放一些基础性代码,属于框架基础层,不应该和项目业务有牵扯,而和项目业务相关的公共部分则应该放在 **lib_common** 中,不要污染 **lib_base**。
|
||||
|
||||
### 依赖版本控制
|
||||
|
||||
  组件化常见的一个问题就是依赖版本,每个组件都有可能自己的依赖库,那我们应该统一管理各种依赖库及其版本,使项目所有使用的依赖都是同一个版本,而不是不同版本。本项目中使用 **buildSrc** 中的几个kt文件进行依赖版本统一性的管理,及其项目的一些配置。
|
||||
|
||||
## **MVVM相关**
|
||||
|
||||
* **MVVM** 采用 **Jetpack** 组件 + **Repository** 设计模式 实现,所使用的 **Jetpack** 并不是很多,像 **DataBinding**、**Hilt**、**Room** 等并没有使用,如果需要可以添加。采用架构模式目的就是为了解偶代码,对代码进行分层,各模块各司其职,所以既然使用了架构模式那就要遵守好规范。
|
||||
* **Repository** 仓库层负责数据的提供,**ViewModel** 无需关心数据的来源,**Repository** 内避免使用 **LiveData**,框架里使用了 **Kotlin** 协程的 **Flow** 进行处理请求或访问数据库,**Repository** 的函数会返回一个 **Flow** 给 **ViewModel** 的调用函数,**Flow** 上游负责提供数据,下游也就是 **ViewModel** 获取到数据使用 **LiveData** 进行存储,**View** 层订阅 **LiveData**,实现数据驱动视图
|
||||
* 三者的依赖都是单向依赖,**View** -> **ViewModel** -> **Repository**
|
||||
|
||||
## 项目使用的三方库及其简单示例和资料
|
||||
|
||||
* 组件化 架构
|
||||
* MVVM 架构
|
||||
* Repository 设计模式
|
||||
* [Kotlin](https://github.com/JetBrains/kotlin)
|
||||
* [Kotlin-Coroutines-Flow](https://github.com/JetBrains/kotlin)
|
||||
* [Lifecycle](https://developer.android.com/jetpack/androidx/releases/lifecycle)
|
||||
@ -28,145 +103,111 @@
|
||||
* [LiveData](https://developer.android.com/topic/libraries/architecture/livedata)
|
||||
* [ViewBinding](https://developer.android.com/topic/libraries/view-binding)
|
||||
* [Android KTX](https://developer.android.com/kotlin/ktx)
|
||||
* [OkHttp](https://github.com/square/okhttp):网络请求
|
||||
* [Retrofit](https://github.com/square/retrofit):网络请求
|
||||
* [MMKV](https://github.com/Tencent/MMKV):腾讯基于 mmap 内存映射的 key-value 本地存储组件
|
||||
* [Glide](https://github.com/bumptech/glide):快速高效的Android图片加载库
|
||||
* [ARoute](https://github.com/alibaba/ARouter):阿里用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
|
||||
* [BaseRecyclerViewAdapterHelper](https://github.com/CymChad/BaseRecyclerViewAdapterHelper):一个强大并且灵活的RecyclerViewAdapter
|
||||
* [StatusBarUtil](https://github.com/laobie/StatusBarUtil):状态栏
|
||||
* [EventBus](https://github.com/greenrobot/EventBus):适用于Android和Java的发布/订阅事件总线
|
||||
* [Bugly](https://bugly.qq.com/v2/index):腾讯异常上报及热更新(只集成了异常上报)
|
||||
* [PermissionX](https://github.com/guolindev/PermissionX):郭霖权限请求框架
|
||||
* [Chuck](https://github.com/jgilfelt/chuck):适用于Android OkHttp 客户端的应用内HTTP拦截器 显示请求信息
|
||||
* [LeakCanary](https://github.com/square/leakcanary):Android的内存泄漏检测库
|
||||
* [OkHttp](https://github.com/square/okhttp):网络请求
|
||||
* [Retrofit](https://github.com/square/retrofit):网络请求
|
||||
* [MMKV](https://github.com/Tencent/MMKV):腾讯基于 **mmap** 内存映射的 **key-value** 本地存储组件
|
||||
* [Glide](https://github.com/bumptech/glide):快速高效的 **Android** 图片加载库
|
||||
* [ARoute](https://github.com/alibaba/ARouter):阿里用于帮助 **Android App** 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
|
||||
* [BaseRecyclerViewAdapterHelper](https://github.com/CymChad/BaseRecyclerViewAdapterHelper):一个强大并且灵活的 **RecyclerViewAdapter**
|
||||
* [StatusBarUtil](https://github.com/laobie/StatusBarUtil):状态栏
|
||||
* [EventBus](https://github.com/greenrobot/EventBus):适用于 **Android** 和 **Java** 的发布/订阅事件总线
|
||||
* [Bugly](https://bugly.qq.com/v2/index):腾讯异常上报及热更新(只集成了异常上报)
|
||||
* [PermissionX](https://github.com/guolindev/PermissionX):郭霖权限请求框架
|
||||
* [LeakCanary](https://github.com/square/leakcanary):**Android** 的内存泄漏检测库
|
||||
* [AndroidAutoSize](https://github.com/JessYanCoding/AndroidAutoSize):**JessYan** 大佬的 今日头条屏幕适配方案终极版
|
||||
|
||||
## 使用方式
|
||||
* 1.下载本项目删除无用的文件
|
||||
* 2.修改项目包名及各组件包结构,修改 AppName
|
||||
* 3.填写自己的 Bugly key 在 BaseApplication initialize() 方法中
|
||||
* 这样就可以使用了,当然可以删除不用的第三方,或者添加相应要使用的第三方,具体规范看下面的框架解读
|
||||
### **Kotlin协程**
|
||||
|
||||
## Demo
|
||||
* 项目里有一个demo分支,这是我写的一个小例子,可以结合Demo去熟悉这个框架
|
||||
关于 **Kotlin 协程**,是真的香,具体教程可以看我的一篇文章:
|
||||
|
||||
## 框架解读
|
||||
- [万字长文 - Kotlin 协程进阶](https://juejin.cn/post/6950616789390721037)
|
||||
|
||||
**组件化相关**
|
||||
* 本框架采用的是组件化架构,核心组件就是 Base 和 Common ,这两个组件都属于公共组件,负责为功能业务组件提供支持
|
||||
* Base 组件主要集成了各种需要使用的第三方库和依赖或者公用的 aar/jar,并将依赖向依赖该组件的组件传递,需要集成的依赖,全部集成在 Base 组件内,Base 组件也提供了各种基类封装以及工具类、扩展函数、顶层函数,这些都应该是项目无关性的,可以达到 Base 模块直接拷贝复用的效果
|
||||
* Common 组件主要是与项目有关的公用库,比如网络接口,全局常量, bean 类等,和项目有关的东西因该放在 Common 组件内,不要侵入 Base 组件,因为和项目有关的东西一旦放在了 Base 组件内,想要直接拷贝复用 Base 组件就不可能了,肯定会有一堆和项目相关的东西,项目的资源文件或者公用的资源文件最好统一放在 Common 组件内,方便公用,方便管理
|
||||
* Base 和 Common 都属于公共组件,区别就在于 Base 比 Common 更底层,偏于与项目无关性,而 Common 是与项目有关性
|
||||
* 项目的依赖版本管理和项目参数等配置统一写在了 buildSrc 文件夹内,内部维护了几个 kt 文件进行对依赖库版本及项目参数统一存放管理
|
||||
* 功能组件应该依赖 Common 组件,壳 App 依赖所有的功能组件,要尽量避免各组件互相依赖,壳 App 内不要写东西,只当一个壳负责集成各个组件,每个组件都应该在 build.gradle 文件内设置资源命名规范,目的是为了避免资源冲突
|
||||
```
|
||||
android {
|
||||
resourcePrefix "资源名前缀"
|
||||
}
|
||||
```
|
||||
* 各个功能业务组件可以单独运行,通过 buildSrc/BuildConfig.kt 中的 isAppMode 参数控制,项目业务复杂起来后,就需要为每个组件单独编写供其正常单独运行的逻辑代码
|
||||
**Flow** 类似于 **RxJava**,它也有一系列的操作符,资料:
|
||||
|
||||
**MVVM相关**
|
||||
* MVVM 采用 Jetpack 组件 + Repository 设计模式 实现,所使用的 Jetpack 并不是很多,像 DataBinding、Hilt、Room 等并没有使用,如果需要可以添加。采用架构模式目的就是为了解偶代码,对代码进行分层,各模块各司其职,所以既然使用了架构模式那就要遵守好规范
|
||||
* Repository 仓库层负责数据的提供,ViewModel 无需关心数据的来源,Repository 内避免使用 LiveData,框架里使用了 Kotlin 协程的 Flow 进行处理请求或访问数据库,最后将数据发射到 ViewModel 调用者,Flow上游负责提供数据,下游也就是ViewModel获取到数据使用LiveData进行存储,View层订阅LiveData,实现数据驱动视图
|
||||
* 三者的依赖都是单向依赖,View -> ViewModel -> Repository
|
||||
- [Google 推荐在 MVVM 架构中使用 Kotlin Flow: ](https://juejin.im/post/6854573211930066951)
|
||||
- [即学即用Kotlin - 协程:](https://juejin.im/post/6854573211418361864)
|
||||
- [Kotlin Coroutines Flow 系列(1-5):](https://juejin.im/post/6844904057530908679)
|
||||
|
||||
# <p align="center"> 示例代码及注意事项🐽</p>
|
||||
### **PermissionX**
|
||||
|
||||
## ViewModel
|
||||
`ViewModel` 类旨在以注重生命周期的方式存储和管理界面相关的数据。`ViewModel` 类让数据可在发生屏幕旋转等配置更改后继续留存。
|
||||
**PermissionX** 是郭霖的一个权限申请框架
|
||||
**使用方式:**
|
||||
```
|
||||
class MainViewModel : ViewModel(){
|
||||
}
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
// 获取无参构造的ViewModel实例
|
||||
val mViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
||||
}
|
||||
```
|
||||
**资料:**
|
||||
官方文档: [https://developer.android.com/topic/libraries/architecture/viewmodel](https://developer.android.com/topic/libraries/architecture/viewmodel)
|
||||
Android ViewModel,再学不会你砍我: [https://juejin.im/post/6844903919064186888](https://juejin.im/post/6844903919064186888)
|
||||
## LiveData
|
||||
`LiveData`是一种可观察的数据存储器类。与常规的可观察类不同,`LiveData` 具有生命周期感知能力,意指它遵循其他应用组件(如 `Activity`、`Fragment` 或 `Service`)的生命周期。这种感知能力可确保 `LiveData` 仅更新处于活跃生命周期状态的应用组件观察者
|
||||
LiveData 分为可变值的`MutableLiveData`和不可变值的`LiveData`
|
||||
**常用方法:**
|
||||
PermissionX.init(this)
|
||||
.permissions("需要申请的权限")
|
||||
.request { allGranted, grantedList, deniedList -> }
|
||||
```
|
||||
fun test() {
|
||||
val liveData = MutableLiveData<String>()
|
||||
// 设置更新数据源
|
||||
liveData.value = "LiveData"
|
||||
// 将任务发布到主线程以设置给定值
|
||||
liveData.postValue("LiveData")
|
||||
// 获取值
|
||||
val value = liveData.value
|
||||
// 观察数据源更改(第一个参数应是owner:LifecycleOwner 比如实现了LifecycleOwner接口的Activity)
|
||||
liveData.observe(this, {
|
||||
// 数据源更改后触发的逻辑
|
||||
})
|
||||
}
|
||||
```
|
||||
**资料:**
|
||||
官方文档: [https://developer.android.com/topic/libraries/architecture/livedata](https://developer.android.com/topic/libraries/architecture/livedata)
|
||||
|
||||
## Lifecycle
|
||||
`Lifecycle` 是一个类,用于存储有关组件(如 `Activity` 或 `Fragment`)的生命周期状态的信息,并允许其他对象观察此状态。
|
||||
`LifecycleOwner` 是单一方法接口,表示类具有 `Lifecycle`。它具有一种方法(即 `getLifecycle()`),该方法必须由类实现。
|
||||
实现 `LifecycleObserver` 的组件可与实现 `LifecycleOwner` 的组件无缝协同工作,因为所有者可以提供生命周期,而观察者可以注册以观察生命周期。
|
||||
|
||||
**资料:**
|
||||
官方文档: [https://developer.android.com/topic/libraries/architecture/lifecycle](https://developer.android.com/topic/libraries/architecture/lifecycle)
|
||||
|
||||
## ViewBinding
|
||||
通过视图绑定功能,您可以更轻松地编写可与视图交互的代码。在模块中启用视图绑定之后,系统会为该模块中的每个 XML 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 ID 的所有视图的直接引用。
|
||||
在大多数情况下,视图绑定会替代 `findViewById`
|
||||
**使用方式:**
|
||||
按模块启用`ViewBinding`
|
||||
```
|
||||
// 模块下的build.gradle文件
|
||||
android {
|
||||
// 开启ViewBinding
|
||||
// 高版本AS
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
}
|
||||
// 低版本AS 最低3.6
|
||||
viewBinding {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
```
|
||||
`Activity` 中 `ViewBinding` 的使用
|
||||
```
|
||||
// 之前设置视图的方法
|
||||
setContentView(R.layout.activity_main)
|
||||
GitHub: [https://github.com/guolindev/PermissionX](https://github.com/guolindev/PermissionX)
|
||||
|
||||
// 使用ViewBinding后的方法
|
||||
val mBinding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(mBinding.root)
|
||||
### EventBus APT
|
||||
|
||||
// ActivityMainBinding类是根据布局自动生成的 如果没有请先build一下项目
|
||||
// ViewBinding会将控件id转换为小驼峰命名法,所以为了保持一致规范,在xml里声明id时也请使用小驼峰命名法
|
||||
// 比如你有一个id为mText的控件,可以这样使用
|
||||
mBinding.mText.text = "ViewBinding"
|
||||
```
|
||||
`Fragment` 中 `ViewBinding` 的使用
|
||||
```
|
||||
// 原来的写法
|
||||
return inflater.inflate(R.layout.fragment_blank, container, false)
|
||||
事件总线这里选择的还是 **EventBus**,也有很多比较新的事件总线框架,还是选择了这个直接上手的
|
||||
在框架内我对 **EventBus** 进行了基类封装,自动注册和解除注册,在需要注册的类上添加 **@EventBusRegister** 注解即可,无需关心内存泄漏及没及时解除注册的情况,基类里已经做了处理
|
||||
|
||||
// 使用ViewBinding的写法
|
||||
mBinding = FragmentBlankBinding.inflate(inflater)
|
||||
return mBinding.root
|
||||
```kotlin
|
||||
@EventBusRegister
|
||||
class MainActivity : AppCompatActivity() {}
|
||||
```
|
||||
**资料:**
|
||||
官方文档: [https://developer.android.com/topic/libraries/view-binding](https://developer.android.com/topic/libraries/view-binding)
|
||||
CSDN: [https://blog.csdn.net/u010976213/article/details/104501830?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5](https://blog.csdn.net/u010976213/article/details/104501830?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5)
|
||||
|
||||
## ARoute
|
||||
`ARoute`是阿里巴巴的一个用于帮助 Android App 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
|
||||
很多使用 **EventBus** 的开发者其实都没有发现 **APT** 的功能,这是 **EventBus3.0** 的重大更新,使用 **EventBus APT** 可以在编译期生成订阅类,这样就可以避免使用低效率的反射,很多人不知道这个更新,用着**3.0**的版本,实际上却是**2.0**的效率。
|
||||
项目中已经在各模块中开启了 **EventBus APT**,**EventBus** 会在编译器对各模块生成订阅类,需要我们手动编写代码去注册这些订阅类:
|
||||
|
||||
```kotlin
|
||||
// 在APP壳的AppApplication类中
|
||||
EventBus
|
||||
.builder()
|
||||
.addIndex("各模块生成的订阅类的实例 类名在base_module.gradle脚本中进行了设置 比如 module_home 生成的订阅类就是 module_homeIndex")
|
||||
.installDefaultEventBus()
|
||||
```
|
||||
|
||||
### 屏幕适配 AndroidAutoSize
|
||||
|
||||
屏幕适配使用的是 **JessYan** 大佬的 今日头条屏幕适配方案终极版
|
||||
|
||||
GitHub: [https://github.com/JessYanCoding/AndroidAutoSize](https://github.com/JessYanCoding/AndroidAutoSize)
|
||||
|
||||
**使用方式:**
|
||||
|
||||
```
|
||||
// 在清单文件中声明
|
||||
<manifest>
|
||||
<application>
|
||||
// 主单位使用dp 没设置副单位
|
||||
<meta-data
|
||||
android:name="design_width_in_dp"
|
||||
android:value="360"/>
|
||||
<meta-data
|
||||
android:name="design_height_in_dp"
|
||||
android:value="640"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
// 默认是以竖屏的宽度为基准进行适配
|
||||
// 如果是横屏项目要适配Pad(Pad适配尽量使用两套布局 因为手机和Pad屏幕宽比差距很大 无法完美适配)
|
||||
<manifest>
|
||||
<application>
|
||||
// 以高度为基准进行适配 (还需要手动代码设置以高度为基准进行适配) 目前以高度适配比宽度为基准适配 效果要好
|
||||
<meta-data
|
||||
android:name="design_height_in_dp"
|
||||
android:value="400"/>
|
||||
</application>
|
||||
</manifest>
|
||||
|
||||
// 在Application 中设置
|
||||
// 屏幕适配 AndroidAutoSize 以横屏高度为基准进行适配
|
||||
AutoSizeConfig.getInstance().isBaseOnWidth = false
|
||||
```
|
||||
|
||||
### ARoute
|
||||
|
||||
**ARoute** 是阿里巴巴的一个用于帮助 **Android App** 进行组件化改造的框架 —— 支持模块间的路由、通信、解耦
|
||||
|
||||
**使用方式:**
|
||||
|
||||
```
|
||||
// 1.在需要进行路由跳转的Activity或Fragment上添加 @Route 注解
|
||||
@Route(path = "/test/activity")
|
||||
@ -206,113 +247,121 @@ class MainActivity : AppCompatActivity() {
|
||||
// 5.获取Fragment
|
||||
Fragment fragment = (Fragment) ARouter.getInstance().build("/test/fragment").navigation();
|
||||
```
|
||||
**资料:**
|
||||
|
||||
**资料:**
|
||||
|
||||
官方文档:[https://github.com/alibaba/ARouter](https://github.com/alibaba/ARouter)
|
||||
|
||||
## 屏幕适配 AndroidAutoSize
|
||||
屏幕适配使用的是 JessYan 大佬的 今日头条屏幕适配方案终极版
|
||||
GitHub: [https://github.com/JessYanCoding/AndroidAutoSize](https://github.com/JessYanCoding/AndroidAutoSize)
|
||||
**使用方式:**
|
||||
```
|
||||
// 在清单文件中声明
|
||||
<manifest>
|
||||
<application>
|
||||
// 主单位使用dp 没设置副单位
|
||||
<meta-data
|
||||
android:name="design_width_in_dp"
|
||||
android:value="360"/>
|
||||
<meta-data
|
||||
android:name="design_height_in_dp"
|
||||
android:value="640"/>
|
||||
</application>
|
||||
</manifest>
|
||||
### ViewBinding
|
||||
|
||||
// 默认是以竖屏的宽度为基准进行适配
|
||||
// 如果是横屏项目要适配Pad(Pad适配尽量使用两套布局 因为手机和Pad屏幕宽比差距很大 无法完美适配)
|
||||
<manifest>
|
||||
<application>
|
||||
// 以高度为基准进行适配 (还需要手动代码设置以高度为基准进行适配) 目前以高度适配比宽度为基准适配 效果要好
|
||||
<meta-data
|
||||
android:name="design_height_in_dp"
|
||||
android:value="400"/>
|
||||
</application>
|
||||
</manifest>
|
||||
通过视图绑定功能,可以更轻松地编写可与视图交互的代码。在模块中启用视图绑定之后,系统会为该模块中的每个 **XML** 布局文件生成一个绑定类。绑定类的实例包含对在相应布局中具有 **ID** 的所有视图的直接引用。
|
||||
在大多数情况下,视图绑定会替代 **findViewById**
|
||||
|
||||
// 在Application 中设置
|
||||
// 屏幕适配 AndroidAutoSize 以横屏高度为基准进行适配
|
||||
AutoSizeConfig.getInstance().isBaseOnWidth = false
|
||||
```
|
||||
## EventBus APT
|
||||
事件总线这里选择的还是`EventBus`,也有很多比较新的事件总线框架,还是选择了这个直接上手的
|
||||
在框架内我对`EventBus`进行了基类封装,自动注册和解除注册,在需要注册的类上添加`@EventBusRegister`注解即可,无需关心内存泄漏及没及时解除注册的情况,基类里已经做了处理
|
||||
```
|
||||
@EventBusRegister
|
||||
class MainActivity : AppCompatActivity() {
|
||||
}
|
||||
```
|
||||
很多使用`EventBus`的其实都没有发现APT的功能,这是`EventBus3.0`的重大更新,使用`EventBus APT`可以编译期生成订阅类,这样就可以避免使用低效率的反射,很多人不知道这个更新,用着3.0的版本,实际上却是2.0的效率
|
||||
项目中已经在各模块中开启了`EventBus APT`,`EventBus`会在编译器对各模块生成订阅类,需要我们手动代码去集成这些订阅类
|
||||
```
|
||||
// 因为App包依赖了所有的模块所以选择在App包下的Application中进行设置
|
||||
// 开启EventBus APT
|
||||
EventBus
|
||||
.builder()
|
||||
.addIndex("各模块生成的订阅类的实例 类名在 moduleBase.gradle 脚本中进行了设置 比如 Lib_Main 生成的订阅类就是 MainEventIndex")
|
||||
.installDefaultEventBus()
|
||||
```
|
||||
**使用方式:**
|
||||
|
||||
## PermissionX
|
||||
`PermissionX` 是郭霖的一个权限申请框架
|
||||
**使用方式:**
|
||||
```
|
||||
PermissionX.init(this)
|
||||
.permissions("需要申请的权限")
|
||||
.request { allGranted, grantedList, deniedList -> }
|
||||
```
|
||||
**资料:**
|
||||
GitHub: [https://github.com/guolindev/PermissionX](https://github.com/guolindev/PermissionX)
|
||||
## 组件化资源命名冲突
|
||||
组件化方案中有一个坑就是资源命名冲突的问题,小则导致合并清单文件时资源丢失,大则直接导致崩溃
|
||||
为了解决这个问题,需要在`build.gradle`文件中,对各模块声明资源前缀规则
|
||||
```
|
||||
按模块启用**ViewBinding**
|
||||
|
||||
```groovy
|
||||
// 模块下的build.gradle文件
|
||||
android {
|
||||
// 前缀最好以组件包名规定
|
||||
resourcePrefix "main_"
|
||||
// 开启ViewBinding
|
||||
// 高版本AS
|
||||
buildFeatures {
|
||||
viewBinding = true
|
||||
}
|
||||
// 低版本AS 最低3.6
|
||||
viewBinding {
|
||||
enabled = true
|
||||
}
|
||||
}
|
||||
```
|
||||
## Kotlin 协程
|
||||
`Kotlin Coroutines` 是一套解决异步编程的方案,在 Android 平台就是一个线程框架,用了都说好
|
||||
**使用示例:**
|
||||
* `Activity` 中
|
||||
```
|
||||
// 在 Activity 中 可以使用 lifecycleScope 协程作用域 进行开启协程
|
||||
// lifecycleScope 是 LifecycleOwner 的扩展属性 使用 lifecycleScope 你就无需关心Activity声明周期的问题 无需关心因为生命周期导致的协程泄漏的问题 可以说是真方便
|
||||
// 使用示例
|
||||
lifecycleScope.launch(Dispatchers.Default) {
|
||||
delay(1000L)
|
||||
ARouter.getInstance()
|
||||
.build(RouteUrl.MainActivity)
|
||||
.navigation()
|
||||
delay(100L)
|
||||
finish()
|
||||
}
|
||||
// launch() 函数开启了一个协程 调度器默认是Main 这里我指定了 Dispatchers.Default
|
||||
// 使用默认调度器可以直接使用 launch{}
|
||||
// Activity 中还有一个协程作用域可以使用 就是 MainScope() 它会返回一个协程作用域实例 使用这个就需要我们手动去处理声明周期的问题了
|
||||
```
|
||||
* `ViewModel` 中使用
|
||||
```
|
||||
// 在 ViewModel 中可以使用
|
||||
viewModelScope.launch {}
|
||||
// 默认的调度器也是Main 同样也不需要我们去做生命周期的处理
|
||||
```
|
||||
* `Flow`
|
||||
`Flow`类似于`RxJava`,它也有一系列的操作符
|
||||
**资料:**
|
||||
Google 推荐在 MVVM 架构中使用 Kotlin Flow: [https://juejin.im/post/6854573211930066951](https://juejin.im/post/6854573211930066951)
|
||||
即学即用Kotlin - 协程: [https://juejin.im/post/6854573211418361864](https://juejin.im/post/6854573211418361864)
|
||||
Kotlin Coroutines Flow 系列(1-5): [https://juejin.im/post/6844904057530908679](https://juejin.im/post/6844904057530908679)
|
||||
|
||||
## 结语
|
||||
* 本人是一个刚大学毕业的 Android 开发者,技术相对来说比较薄弱,写这个框架主要是为了在以后自己使用、对自己技术做一个总结、对其他想学习的同学提供一个例子、也是想把一些东西开源出去,让这个社区更加的美好。文档中可能有些描述不太专业和生硬,我也很少写这些东西,所以表达起来很生疏。
|
||||
* 如果你绝对该项目对你有用,那就请给一个 star🌟吧🥰
|
||||
**Activity** 中 **ViewBinding** 的使用
|
||||
|
||||
```kotlin
|
||||
// 之前设置视图的方法
|
||||
setContentView(R.layout.activity_main)
|
||||
|
||||
// 使用ViewBinding后的方法
|
||||
val mBinding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(mBinding.root)
|
||||
|
||||
// ActivityMainBinding类是根据布局自动生成的 如果没有请先build一下项目
|
||||
// ViewBinding会将控件id转换为小驼峰命名法,所以为了保持一致规范,在xml里声明id时也请使用小驼峰命名法
|
||||
// 比如你有一个id为mText的控件,可以这样使用
|
||||
mBinding.mText.text = "ViewBinding"
|
||||
```
|
||||
|
||||
**Fragment** 中 **ViewBinding** 的使用
|
||||
|
||||
```kotlin
|
||||
// 原来的写法
|
||||
return inflater.inflate(R.layout.fragment_blank, container, false)
|
||||
|
||||
// 使用ViewBinding的写法
|
||||
mBinding = FragmentBlankBinding.inflate(inflater)
|
||||
return mBinding.root
|
||||
```
|
||||
|
||||
**资料:**
|
||||
|
||||
官方文档: [https://developer.android.com/topic/libraries/view-binding](https://developer.android.com/topic/libraries/view-binding)
|
||||
|
||||
CSDN: [https://blog.csdn.net/u010976213/article/details/104501830?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5](https://blog.csdn.net/u010976213/article/details/104501830?depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5&utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromBaidu-5)
|
||||
|
||||
### ViewModel
|
||||
|
||||
**ViewModel** 类旨在以注重生命周期的方式存储和管理界面相关的数据。**ViewModel** 类让数据可在发生屏幕旋转等配置更改后继续留存。
|
||||
|
||||
**使用方式:**
|
||||
|
||||
```kotlin
|
||||
class MainViewModel : ViewModel(){}
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
// 获取无参构造的ViewModel实例
|
||||
val mViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
|
||||
}
|
||||
```
|
||||
|
||||
**资料:**
|
||||
|
||||
官方文档: [https://developer.android.com/topic/libraries/architecture/viewmodel](https://developer.android.com/topic/libraries/architecture/viewmodel)
|
||||
|
||||
Android ViewModel,再学不会你砍我: [https://juejin.im/post/6844903919064186888](https://juejin.im/post/6844903919064186888)
|
||||
|
||||
### LiveData
|
||||
|
||||
**LiveData** 是一种可观察的数据存储器类。与常规的可观察类不同,**LiveData** 具有生命周期感知能力,意指它遵循其他应用组件(如 **Activity**、**Fragment** 或 **Service**)的生命周期。这种感知能力可确保 **LiveData** 仅更新处于活跃生命周期状态的应用组件观察者
|
||||
|
||||
**LiveData** 分为可变值的 **MutableLiveData** 和不可变值的 **LiveData**
|
||||
|
||||
**常用方法:**
|
||||
|
||||
```kotlin
|
||||
fun test() {
|
||||
val liveData = MutableLiveData<String>()
|
||||
// 设置更新数据源
|
||||
liveData.value = "LiveData"
|
||||
// 将任务发布到主线程以设置给定值
|
||||
liveData.postValue("LiveData")
|
||||
// 获取值
|
||||
val value = liveData.value
|
||||
// 观察数据源更改(第一个参数应是owner:LifecycleOwner 比如实现了LifecycleOwner接口的Activity)
|
||||
liveData.observe(this, {
|
||||
// 数据源更改后触发的逻辑
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
**资料:**
|
||||
|
||||
官方文档: [https://developer.android.com/topic/libraries/architecture/livedata](https://developer.android.com/topic/libraries/architecture/livedata)
|
||||
|
||||
### Lifecycle
|
||||
|
||||
**Lifecycle** 是一个类,用于存储有关组件(如 **Activity** 或 **Fragment**)的生命周期状态的信息,并允许其他对象观察此状态。**LifecycleOwner** 是单一方法接口,表示类具有 **Lifecycle**。它具有一种方法(即 **getLifecycle()**),该方法必须由类实现。实现 **LifecycleObserver** 的组件可与实现 **LifecycleOwner** 的组件无缝协同工作,因为所有者可以提供生命周期,而观察者可以注册以观察生命周期。
|
||||
|
||||
**资料:**
|
||||
|
||||
官方文档: [https://developer.android.com/topic/libraries/architecture/lifecycle](https://developer.android.com/topic/libraries/architecture/lifecycle)
|
||||
BIN
img/img1.jpg
BIN
img/img1.jpg
Binary file not shown.
|
Before Width: | Height: | Size: 98 KiB |
Reference in New Issue
Block a user