-
Notifications
You must be signed in to change notification settings - Fork 0
V3 Hilt版本使用说明
chenli edited this page Apr 10, 2024
·
2 revisions
- 1.1 在root's build.gradle中加入Jitpack仓库
allprojects {
repositories {
...
maven { url 'https://jitpack.io' }
}
}
- 1.2 在app's build.gradle中添加依赖
dependencies {
...
//框架地址
implementation 'com.github.cl-6666:mvvm-framework:v3.1.0'
//hilt官方地址:https://developer.android.google.cn/training/dependency-injection/hilt-android?hl=zh-cn
def hilt_version = "2.48.1"
api "com.google.dagger:hilt-android:$hilt_version"
kapt "com.google.dagger:hilt-android-compiler:$hilt_version"
}
- 1.3 在app's build.gradle中,android 模块下按需开启DataBinding与ViewBinding
AndroidStudio 4.0 以下版本------>
android {
...
dataBinding {
enabled = true
}
viewBinding {
enabled = true
}
}
AndroidStudio 4.0及以上版本 ------>
android {
...
buildFeatures {
dataBinding = true
viewBinding = true
}
}
- 1.4所有使用 Hilt 的应用都必须包含一个带有 @HiltAndroidApp 注解的 Application 类
@HiltAndroidApp
class AppApplication : BaseApplication() { ... }
如果由于某种原因,导致你不能继承[BaseApplication];你也可以在你自定义的Application的onCreate函
数中通过调用[BaseApplication.initAppConfig]来进行初始化
Hilt 会按照相应 Android 类的生命周期自动创建和销毁生成的组件类的实例。
生成的组件 | 创建时机 | 销毁时机 |
---|---|---|
SingletonComponent | Application#onCreate() | Application 已销毁 |
ActivityRetainedComponent | Activity#onCreate() | Activity#onDestroy() |
ViewModelComponent | ViewModel 已创建 | ViewModel 已销毁 |
ActivityComponent | Activity#onCreate() | Activity#onDestroy() |
FragmentComponent | Fragment#onAttach() | Fragment#onDestroy() |
ViewComponent | View#super() | View 已销毁 |
ViewWithFragmentComponent | View#super() | View 已销毁 |
ServiceComponent | Service#onCreate() | Service#onDestroy() |
一般我们项目中都会有一套自己定义的符合业务需求的基类 BaseActivity/BaseFragment,所以我们的基类需要继承本框架的Base类
- 不想用Databinding与ViewBinding-------可以继承 BaseVmActivity/BaseVmFragment
- 用Databinding-----------可以继承BaseVmDbActivity/BaseVmDbFragment**
- 用Viewbinding-----------可以继承BaseVmVbActivity/BaseVmVbFragment**
Activity:
abstract class BaseActivity<VM : BaseViewModel, DB : ViewDataBinding> : BaseVmDbActivity<VM, DB>() {
/**
* 当前Activity绑定的视图布局Id abstract修饰供子类实现
*/
abstract override fun layoutId(): Int
/**
* 当前Activityc创建后调用的方法 abstract修饰供子类实现
*/
abstract override fun initView(savedInstanceState: Bundle?)
/**
* 创建liveData数据观察
*/
override override fun createObserver()
/**
* 打开等待框 在这里实现你的等待框展示
*/
override fun showLoading(message: String) {
...
}
/**
* 关闭等待框 在这里实现你的等待框关闭
*/
override fun dismissLoading() {
...
}
}
Fragment:
abstract class BaseFragment<VM : BaseViewModel,DB:ViewDataBinding> : BaseVmDbFragment<VM,DB>() {
abstract override fun initView(savedInstanceState: Bundle?)
/**
* 懒加载 只有当前fragment视图显示时才会触发该方法 abstract修饰供子类实现
*/
abstract override fun lazyLoadData()
/**
* 创建liveData数据观察 懒加载之后才会触发
*/
override override fun createObserver()
/**
* Fragment执行onViewCreated后触发的方法
*/
override fun initData() {
}
/**
* 打开等待框 在这里实现你的等待框展示
*/
override fun showLoading(message: String) {
...
}
/**
* 关闭等待框 在这里实现你的等待框关闭
*/
override fun dismissLoading() {
...
}
}
- 3.1 创建MainViewModel类继承BaseViewModel
@HiltViewModel
class MainViewModel @Inject constructor(
private val apiService: ApiService
) : BaseViewModel() {}
- 3.2 创建HomeFragment 继承基类传入相关泛型,第一个泛型为你创建的MainViewModel,第二个泛型为ViewDataBind,保存fragment_home.xml后databinding会生成一个FragmentHomeBinding类。(如果没有生成,试着点击Build->Clean Project)
@AndroidEntryPoint
class HomeFragment : BaseFragment<MainViewModel, FragmentHomeBinding>() {
/**
* 初始化操作
*/
override fun initView(savedInstanceState: Bundle?) {
...
}
/**
* fragment 懒加载
*/
override fun lazyLoadData() {
...
}
}
- 4.1 新建请求配置类继承 BaseNetworkApi 示例:
@Module
@InstallIn(SingletonComponent::class)
object NetworkModule : BaseNetworkApi(){
/**
* 实现重写父类的setHttpClientBuilder方法,
* 在这里可以添加拦截器,可以对 OkHttpClient.Builder 做任意操作
*/
override fun setHttpClientBuilder(builder: OkHttpClient.Builder): OkHttpClient.Builder {
//普通网络日志显示写法
val httpLoggingInterceptor = HttpLoggingInterceptor { message ->
Timber.i(
"网络日志", message
)
}
httpLoggingInterceptor.level = HttpLoggingInterceptor.Level.BODY
//框架内部封装日志显示写法 hideVerticalLine = true代表隐藏横线
val loggingInterceptor = AndroidLoggingInterceptor.build(hideVerticalLine = true)
builder.apply {
/** 设置缓存配置 缓存最大10M */
cache(Cache(File(app.cacheDir, "cxk_cache"), 10 * 1024 * 1024))
/** 添加Cookies自动持久化 */
// cookieJar(cookieJar)
/** 演示添加缓存拦截器 可传入缓存天数,不传默认7天 */
addInterceptor(CacheInterceptor())
/** 演示添加公共heads 注意要设置在日志拦截器之前,不然Log中会不显示head信息 */
addInterceptor(MyHeadInterceptor())
/** 演示token过期拦截器演示 */
addInterceptor(TokenOutInterceptor())
/** 演示日志拦截器 */
addInterceptor(loggingInterceptor)
/** 超时时间 连接、读、写 */
connectTimeout(10, TimeUnit.SECONDS)
readTimeout(5, TimeUnit.SECONDS)
writeTimeout(5, TimeUnit.SECONDS)
}
return builder
}
/**
* 实现重写父类的setRetrofitBuilder方法,
* 在这里可以对Retrofit.Builder做任意操作,比如添加GSON解析器,protobuf等
*/
override fun setRetrofitBuilder(builder: Retrofit.Builder): Retrofit.Builder {
return builder.apply {
addConverterFactory(GsonConverterFactory.create(GsonBuilder().create()))
}
}
@Provides
fun provideApiService(): ApiService {
return NetworkModule.getApi(ApiService::class.java, Constants.BASE_URL, false)
}
}
- 4.2如果你请求服务器返回的数据有基类(没有可忽略这一步)例如:
{
"data": ...,
"errorCode": 0,
"errorMsg": ""
}
该示例格式是 玩Android Api返回的数据格式,如果errorCode等于0 请求成功,否则请求失败 作为开发者的角度来说,我们主要是想得到脱壳数据-data,且不想每次都判断errorCode==0请求是否成功或失败 这时我们可以在服务器返回数据基类中继承BaseResponse,实现相关方法:
data class ApiResponse<T>(var errorCode: Int, var errorMsg: String, var data: T) : BaseResponse<T>() {
// 这里是示例,wanandroid 网站返回的 错误码为 0 就代表请求成功,请你根据自己的业务需求来编写
override fun isSucces() = errorCode == 0
override fun getResponseCode() = errorCode
override fun getResponseData() = data
override fun getResponseMsg() = errorMsg
}
- 4.3 在ViewModel中发起请求,所有请求都是在viewModelScope中启动,请求会发生在IO线程,最终回调在主线程上,当页面销毁的时候,请求会统一取消,不用担心内存泄露的风险,框架做了2种请求使用方式
1、将请求数据包装给ResultState,在Activity/Fragment中去监听ResultState拿到数据做处理
@HiltViewModel
class MainViewModel @Inject constructor(
private val apiService: ApiService
) : BaseViewModel() {
@Inject
lateinit var downLoadManager: DownLoadManager
/**
*自动脱壳过滤处理请求结果,自动判断结果是否成功
*/
var articleListResult = MutableLiveData<ResultState<Data>>()
/**
* 不用框架帮脱壳
*/
var articleListResult2 = MutableLiveData<ResultState<ApiResponse<Data>>>()
/**
* 网络请求
*/
fun apiArticleListData() {
//1.在 Activity/Fragment的监听回调中拿到已脱壳的数据(项目有基类的可以用)
request(
{ apiService.getEntryAndExitData() }, //请求体
articleListResult,//请求的结果接收者,请求成功与否都会改变该值,在Activity或fragment中监听回调结果,具体可看loginActivity中的回调
true,//是否显示等待框,,默认false不显示 可以默认不传
"数据请求中..."//等待框内容,可以默认不填请求网络中...
)
//2.在Activity/Fragment中的监听拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类的可以用)
requestNoCheck(
{ apiService.getEntryAndExitData() },
articleListResult2,
true,
"数据请求中..."
)
}
}
@AndroidEntryPoint
class HomeFragment : BaseFragment<MainViewModel,FragmentHomeBinding>(){
private val mArticleListAdapter: ArticleListAdapter by lazy { ArticleListAdapter(arrayListOf()) }
override fun initView(savedInstanceState: Bundle?) {
mViewModel.apiArticleListData()
mDatabind.rvArticleList.init(LinearLayoutManager(activity), mArticleListAdapter, false)
}
override fun createObserver() {
super.createObserver()
//脱壳
mViewModel.articleListResult.observe(viewLifecycleOwner,
Observer { resultState ->
parseState(resultState, {
//请求成功 打印消息
mArticleListAdapter.submitList(it.datas)
}, {
//请求失败(网络连接问题,服务器的结果码不正确...异常都会走在这里)
toast("请求失败")
})
})
//不脱壳
mViewModel.articleListResult2.observe(viewLifecycleOwner) { resultState ->
parseState(resultState, {
if (it.errorCode == 0) {
//请求成功 打印消息
toast(it.data.toString())
} else {
//请求失败
toast(it.errorMsg)
}
}, {
//请求发生了异常
toast(it.errorMsg)
})
}
}
}
2、 直接在当前ViewModel中拿到请求结果
@HiltViewModel
class MyFragmentViewModel @Inject constructor(
private val apiService: ApiService
): BaseViewModel() {
fun testData() {
//1.拿到已脱壳的数据(项目有基类的可以用)
request({ apiService.getEntryAndExitData() }, {
//请求成功 已自动处理了 请求结果是否正常
}, {
//请求失败 网络异常,或者请求结果码错误都会回调在这里
}, true, "正在登录中...")
//2.拿到未脱壳的数据,你可以自己根据code做业务需求操作(项目没有基类或者不想框架帮忙脱壳的可以用)
requestNoCheck({ apiService.getEntryAndExitData() }, {
//请求成功 自己拿到数据做业务需求操作
if (it.errorCode == 0) {
//结果正确
} else {
//结果错误
}
}, {
//请求失败 网络异常回调在这里
}, true, "正在登录中...")
}
}
}
3、请求结果截图
//注入方法
@Inject
lateinit var downLoadManager: DownLoadManager
/**
* 下载演示
*/
suspend fun downloadPictures() {
// val apkDir = File(Environment.getExternalStorageDirectory(), "YourApp/apks")
// apkDir.mkdirs()
// val apkFile = File(apkDir, "your_apk_file_name.apk")
downLoadManager.downLoad("TAG",
"http://172.31.51.252:9999/hybd/bagl/app/1.2.1_20240220.apk",
"",
"",
reDownload = false,
whetherHttps = false,
object : OnDownLoadListener {
override fun onDownLoadPrepare(key: String) {
TODO("Not yet implemented")
}
override fun onDownLoadError(key: String, throwable: Throwable) {
TODO("Not yet implemented")
}
override fun onDownLoadSuccess(key: String, path: String, size: Long) {
TODO("Not yet implemented")
}
override fun onDownLoadPause(key: String) {
TODO("Not yet implemented")
}
override fun onUpdate(
key: String,
progress: Int,
read: Long,
count: Long,
done: Boolean
) {
TODO("Not yet implemented")
}
})
}
算了不写了,这个不重要,想具体看的话可以在
me.hgj.jetpackmvvm.ext.util
me.hgj.jetpackmvvm.ext.view
的包中看,反正你也可以自己写,按照自己的喜好与需求来
- Kotlin
- Jetpack
- Lifecycle: 观察 Android 生命周期并根据生命周期变化处理 UI 状态
- ViewModel: 管理与 UI 相关的数据持有者和生命周期感知。 允许数据在配置更改(例如屏幕旋转)中保存下来。
- ViewBinding: 使用声明性格式而不是以编程方式将布局中的 UI 组件绑定到应用程序中的数据源。
- Room: 通过在 SQLite 上提供抽象层来构建数据库,以允许流畅的数据库访问。
- Hilt: 用于依赖注入。
- LiveData
- ksp: Kotlin 符号处理 API。
- coil, Coil地址: Coil 是一个 Android 图片加载库,通过 Kotlin 协程的方式加载图片。
- Timber: 日志框架
- OkHttp:网络请求
- PersistentCookieJar:持久CookieJar实现
- logging-interceptor:网络日志
- Retrofit:网络请求