ldlywt / fastjetpack Goto Github PK
View Code? Open in Web Editor NEW基于Kotlin、协程、Retrofit的网络请求封装,快速简单轻便。
基于Kotlin、协程、Retrofit的网络请求封装,快速简单轻便。
launchWithLoadingAndCollect和collectIn的区别在哪里? 我看了之前另外一个issue没能好好理解,可以请你举个例子告知一下吗
我这边的数据返回的数据不太规范,或者说后台比较灵活,ApiResponse里data有时候不是data,有时候是多个bean,比如{data1:xx,data2:xx,code:xx,message:xx},我尝试去修改ApiResponse,但是发现不生效,请问能怎么扩展这个ApiResponse呢,我按照以下的写法,参考message的调用方法调用,无法成功,我在调用函数的时候,泛型使用的是UserInfo,但是实际上data拿不到,后台数据是用userInfo包裹的,所以我就加上一个,当然,如果我重写一套Response应该能实现,但是这样就太多样板代码了,总觉得
open class ApiResponse(
open val data: T? = null,
open val userInfo: UserInfo? = null,
open val code: Int? = null,
open val message: String? = null,
open val error: Throwable? = null,
) : Serializable {
val isSuccess: Boolean
get() = (code == 1 || code == 200)
}
data class ApiSuccessResponse(val response: T) : ApiResponse(
data = response
)
data class ApiLoginResponse(override val userInfo: UserInfo?) :
ApiResponse(userInfo = userInfo)
class ApiEmptyResponse : ApiResponse()
data class ApiFailedResponse(override val code: Int?, override val message: String?) :
ApiResponse(code = code, message = message)
data class ApiErrorResponse(val throwable: Throwable) :
ApiResponse(error = throwable)
这个是否可以把flow放到viewmodel中更好一些?
base里接受loading是不是全局都能收到,是不是不太好啊!
1.如:登录;用户输入框的 username、password这种字段 长度、是否为空 之类的参数判断 放在 activity/fragment 中好 还是 viewModel 中好;根据谷歌官方架构指南以及他们提供的demo;我自己的理解好像是放到viewModel中更好;对项目结构设计不太懂。。。
2.如果在当前库的设计的基础上增加 room 缓存 可以提供下思路吗? 我开始参考google提供的demo中的设计;官方项目repo 都是返回的livedata 我觉得按照那样写模板代码太多了;就去掉了livedata 又借鉴您的项目 自己写了个四不像。。。
我自己尝试的写法:
抽象接口 Fetcher
interface Fetcher<ResultType> {
suspend fun loadFromNetWork(): ResultType
fun loadFromDb(): ResultType?
fun shouldFetch(data: ResultType?): Boolean
fun saveResult(data: ResultType)
suspend fun getResult(): Result<ResultType>
//异常返回的body处理
fun parseException(err: HttpException): Result.ErrorBody
}
实现类 SmartFetcher
abstract class SmartFetcher<RequestType> : Fetcher<RequestType> {
override suspend fun getResult(): Result<RequestType> {
var dbSource = loadFromDb()
return if (shouldFetch(dbSource)){
try {
var result = loadFromNetWork()
saveResult(result)
dbSource = loadFromDb()
Result.success(dbSource)
}catch (e: HttpException){
var error = parseException(e)
Result.error(error.message?:e.message(), dbSource, error)
}catch (e: Exception){
Result.error(e.message?:"unknown error", dbSource)
}
}else{
Result.success(dbSource)
}
}
}
调用示例,在Repo中 定义请求方法
suspend fun request(): Result<Bean> {
return object : SmartFetcher<Bean>() {
override suspend fun loadFromNetWork(): Bean {
TODO("Not yet implemented")
}
override fun loadFromDb(): Bean? {
TODO("Not yet implemented")
}
override fun shouldFetch(data: Bean?): Boolean {
TODO("Not yet implemented")
}
override fun saveResult(data: Bean) {
TODO("Not yet implemented")
}
}.getResult()
}
viewModel 中调用 repo 层:
var mData = MutableLiveData<Result<Bean>>()
fun sendEmailCode(){
viewModelScope.launch(Dispatchers.IO) {
mData.postValue(commonRepo.request())
}
}
activity 中监听
mViewModel.mSendCode.observe(this, Observer { res ->
dismissLoadingDialog()
})
失败返回的数据:
{
"status_code": 9218,
"message": "密码错误,剩余4次输入",
"data": []
}
成功返回的数据:
{
"status_code": 1000,
"message": "成功",
"data": {
"id_no": "BtLhO2qmaOrvQSA5xbYTVqOJGJtd/oHkvIkTChI3c3c=",
"is_verifyed": 0,
"phone": "c59le6L65XK1htExLNic/w==",
}
}
我重写你那个Gson解析器,抛ApiFailedResponse这个函数一直有错,大佬能帮忙看下吗?
感谢大佬分享!
如果有多个Act,发送Toast每个都会收到,导致重复显示,除了用LiveEventBus还有没有其他好的解耦方案?
java.lang.RuntimeException: Unable to start activity ComponentInfo{com.fastjetpack/com.aisier.ui.MainActivity}: java.lang.ClassCastException: android.widget.LinearLayout cannot be cast to android.widget.RelativeLayout
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3455)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3598)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2164)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:241)
at android.app.ActivityThread.main(ActivityThread.java:7582)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:944)
Caused by: java.lang.ClassCastException: android.widget.LinearLayout cannot be cast to android.widget.RelativeLayout
at com.aisier.databinding.ActivityMainBinding.bind(ActivityMainBinding.java:74)
at com.aisier.ui.MainActivity$special$$inlined$viewBindingActivity$default$1.invoke(ActivityViewBindings.kt:66)
at com.aisier.ui.MainActivity$special$$inlined$viewBindingActivity$default$1.invoke(Unknown Source:2)
at by.kirich1409.viewbindingdelegate.LifecycleViewBindingProperty.getValue(ViewBindingProperty.kt:75)
at by.kirich1409.viewbindingdelegate.LifecycleViewBindingProperty.getValue(ViewBindingProperty.kt:62)
at com.aisier.ui.MainActivity.getMBinding(MainActivity.kt:11)
at com.aisier.ui.MainActivity.init(MainActivity.kt:13)
at com.aisier.architecture.base.BaseToolBarActivity.onCreate(BaseToolBarActivity.kt:18)
at android.app.Activity.performCreate(Activity.java:7822)
at android.app.Activity.performCreate(Activity.java:7811)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1328)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3430)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3598)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2164)
at android.os.Handler.dispatchMessage(Handler.java:107)
at android.os.Looper.loop(Looper.java:241)
at android.app.ActivityThread.main(ActivityThread.java:7582)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:944)
首先感谢作者的分享,你的封装思路思路让我获益匪浅,我也打算在接下来的项目中参考你的写法(其实大部分都是引用你的代码,哈哈)。但美中不足的是,目前的写法要求所有的LiveData
必须采用自定义的StateLiveData
,而这样有数据倒灌的风险,所以为了方便开发者使用其他的LiveData
解决数据倒灌的问题,建议采用扩展函数的写法:
fun <T> LiveData<BaseResponse<T>>.observeState(
owner: LifecycleOwner,
callback: HttpRequestCallback<T>.() -> Unit
) {
val requestCallback = HttpRequestCallback<T>().apply(callback)
observe(owner, object : IStateObserver<T> {
override fun onStart() {
requestCallback.startCallback?.invoke()
}
override fun onSuccess(data: T) {
requestCallback.successCallback?.invoke(data)
}
override fun onEmpty() {
requestCallback.emptyCallback?.invoke()
}
override fun onFailure(e: ApiException) {
requestCallback.failureCallback?.invoke(e)
}
override fun onError(data: T?, e: ApiException) {
requestCallback.errorCallback?.invoke(data, e)
}
override fun onFinish() {
requestCallback.finishCallback?.invoke()
}
})
}
这里不再使用之前的IStateObserver
,而是以匿名内部类的形式传入自定义的Observer
。这样一来就只是给LiveData
加了一个扩展函数,解耦得更加彻底。
oneWay封装一耦合高是指复用性差吗?还有传递UI引用是指ViewModel的生命周期绑定吗?
作者你好,我在看到你用 flow封装请求中,尝试写了一下,但是在混淆后安装app, 网络请求总是会回调到 fail函数里 请问应该怎么处理
我只能获得返回的数据实体类,但是我需要response返回的headers
你没有调度到io,hongyang不回复我
2022-04-18 22:04:38.095 26015-26015/com.fastjetpack D/LeakCanary:
┬───
│ GC Root: Global variable in native code
│
├─ android.app.Activity$1 instance
│ Leaking: UNKNOWN
│ Retaining 837.0 kB in 1912 objects
│ Library leak match: instance field android.app.Activity$1#this$0
│ Anonymous subclass of android.app.IRequestFinishCallback$Stub
│ this$0 instance of leakcanary.internal.activity.LeakActivity with mDestroyed = true
│ ↓ Activity$1.this$0
│ ~~~~~~
╰→ leakcanary.internal.activity.LeakActivity instance
Leaking: YES (ObjectWatcher was watching this because leakcanary.internal.activity.LeakActivity received
Activity#onDestroy() callback and Activity#mDestroyed is true)
Retaining 836.5 kB in 1911 objects
key = bad1d16c-cfe5-450d-bfc5-bd68a81e02ef
watchDurationMillis = 73278
retainedDurationMillis = 68278
mApplication instance of com.aisier.App
mBase instance of android.app.ContextImpl
METADATA
Build.VERSION.SDK_INT: 29
Build.MANUFACTURER: Xiaomi
LeakCanary version: 2.7
App process name: com.fastjetpack
Stats: LruCache[maxSize=3000,hits=3873,misses=55638,hitRate=6%]
RandomAccess[bytes=3008222,reads=55638,travel=27328595255,range=20976546,size=25711956]
Heap dump reason: user request
Analysis duration: 5512 ms
launchAndCollectIn里边封装使用到了repeatOnLifecycle方法,但目前你demo的使用场景中,每次我让此Fragment生命周期onStart一下,collect函数便会回调一下上次新数据,感觉毫无意义。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.