diff --git a/sample/src/main/java/com/drake/net/sample/constants/Api.kt b/sample/src/main/java/com/drake/net/sample/constants/Api.kt index c6f18530f..a8439cc30 100644 --- a/sample/src/main/java/com/drake/net/sample/constants/Api.kt +++ b/sample/src/main/java/com/drake/net/sample/constants/Api.kt @@ -1,5 +1,9 @@ package com.drake.net.sample.constants + +/* +建议请求路径都写在一个单例类中, 方便查找和替换 +*/ object Api { const val HOST = "http://127.0.0.1:8091" @@ -9,4 +13,6 @@ object Api { const val GAME = "/game" const val DATA = "/data" const val ARRAY = "/array" + const val CONFIG = "/config" + const val USER_INFO = "/userInfo" } \ No newline at end of file diff --git a/sample/src/main/java/com/drake/net/sample/mock/MockDispatcher.kt b/sample/src/main/java/com/drake/net/sample/mock/MockDispatcher.kt index 02b7c24e3..0d2754a65 100644 --- a/sample/src/main/java/com/drake/net/sample/mock/MockDispatcher.kt +++ b/sample/src/main/java/com/drake/net/sample/mock/MockDispatcher.kt @@ -41,6 +41,8 @@ class MockDispatcher : Dispatcher() { Api.GAME -> getRawResponse(R.raw.game) Api.DATA -> getRawResponse(R.raw.data) Api.ARRAY -> getRawResponse(R.raw.array) + Api.USER_INFO -> getRawResponse(R.raw.user) + Api.CONFIG -> getRawResponse(R.raw.user) else -> MockResponse().setResponseCode(404) } } diff --git a/sample/src/main/java/com/drake/net/sample/model/ConfigModel.kt b/sample/src/main/java/com/drake/net/sample/model/ConfigModel.kt new file mode 100644 index 000000000..8714a1f4f --- /dev/null +++ b/sample/src/main/java/com/drake/net/sample/model/ConfigModel.kt @@ -0,0 +1,9 @@ +package com.drake.net.sample.model + + +import kotlinx.serialization.Serializable + +@Serializable +data class ConfigModel( + var maintain: Boolean = false +) \ No newline at end of file diff --git a/sample/src/main/java/com/drake/net/sample/model/UserInfoModel.kt b/sample/src/main/java/com/drake/net/sample/model/UserInfoModel.kt new file mode 100644 index 000000000..51897998d --- /dev/null +++ b/sample/src/main/java/com/drake/net/sample/model/UserInfoModel.kt @@ -0,0 +1,12 @@ +package com.drake.net.sample.model + + +import kotlinx.serialization.Serializable + +@Serializable +data class UserInfoModel( + var userId: Int = 0, + var username: String = "", + var age: Int = 0, + var balance: String = "" +) \ No newline at end of file diff --git a/sample/src/main/java/com/drake/net/sample/ui/fragment/ViewModelRequestFragment.kt b/sample/src/main/java/com/drake/net/sample/ui/fragment/ViewModelRequestFragment.kt index 982a66a75..ff2fb36d9 100644 --- a/sample/src/main/java/com/drake/net/sample/ui/fragment/ViewModelRequestFragment.kt +++ b/sample/src/main/java/com/drake/net/sample/ui/fragment/ViewModelRequestFragment.kt @@ -4,7 +4,9 @@ import androidx.fragment.app.viewModels import com.drake.engine.base.EngineFragment import com.drake.net.sample.R import com.drake.net.sample.databinding.FragmentViewModelRequestBinding +import com.drake.net.sample.utils.HttpUtils import com.drake.net.sample.vm.UserViewModel +import com.drake.net.utils.scopeNetLife class ViewModelRequestFragment : EngineFragment(R.layout.fragment_view_model_request) { @@ -12,6 +14,8 @@ class ViewModelRequestFragment : private val userViewModel: UserViewModel by viewModels() // 创建ViewModel override fun initView() { + + // 直接将用户信息绑定到视图上 binding.lifecycleOwner = this binding.m = userViewModel @@ -22,5 +26,12 @@ class ViewModelRequestFragment : } override fun initData() { + + scopeNetLife { + val configAsync = HttpUtils.getConfigAsync(this) + // 经常使用的请求可以封装函数 + val userInfo = HttpUtils.getUser() + configAsync.await() // 实际上在getUser之前就发起请求, 此处只是等待结果, 这就是并发请求 + } } } \ No newline at end of file diff --git a/sample/src/main/java/com/drake/net/sample/utils/HttpUtils.kt b/sample/src/main/java/com/drake/net/sample/utils/HttpUtils.kt new file mode 100644 index 000000000..04b9544c0 --- /dev/null +++ b/sample/src/main/java/com/drake/net/sample/utils/HttpUtils.kt @@ -0,0 +1,35 @@ +package com.drake.net.sample.utils + +import com.drake.net.Get +import com.drake.net.sample.constants.Api +import com.drake.net.sample.model.ConfigModel +import com.drake.net.sample.model.UserInfoModel +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.coroutineScope + + +/** + * 常用的请求方法建议写到一个工具类中 + */ +object HttpUtils { + + /** + * 获取配置信息 + * + * 本方法需要再调用await()才会返回结果, 属于异步方法 + */ + fun getConfigAsync(scope: CoroutineScope) = scope.Get(Api.CONFIG) + + /** + * 获取用户信息 + * 阻塞返回可直接返回结果 + * + * @param userId 如果为空表示请求自身用户信息 + */ + suspend fun getUser(userId: String? = null) = coroutineScope { + Get(Api.USER_INFO) { + param("userId", userId) + }.await() + } + +} \ No newline at end of file diff --git a/sample/src/main/java/com/drake/net/sample/vm/UserViewModel.kt b/sample/src/main/java/com/drake/net/sample/vm/UserViewModel.kt index 3d66d6a42..aa51e4254 100644 --- a/sample/src/main/java/com/drake/net/sample/vm/UserViewModel.kt +++ b/sample/src/main/java/com/drake/net/sample/vm/UserViewModel.kt @@ -8,24 +8,41 @@ import com.drake.net.sample.constants.Api import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.coroutineScope + +/** + * 强烈建议不要封装作用域, 封装异步任务即可, 作用域灵活随用随写(Activity/Fragment都可以写作用域) + * + * 不要企图把所有逻辑代码都写在ViewModel中就觉得自己会写MVVM了, 给代码换个位置不叫架构设计, 特别是还增加一堆无效代码情况下 + */ class UserViewModel : ViewModel() { // 用户信息 var userInfo: MutableLiveData = MutableLiveData() + var updateTime: Long = 0 + /** - * 拉取用户信息, 会自动通知页面更新, 同时页面销毁会自动取消网络请求 - * 其包含作用域, 生命周期跟随当前viewModel - * scopeNetLife/scopeDialog不推荐写在ViewModel中 + * 拉取用户信息, 只能监听返回结果, 仅当外部调用对象不在乎返回结果时使用 + * 会自动通知页面更新: 因为使用LiveData将请求结果回调出去, 建议将该liveData对象直接使用DataBinding绑定到页面上, 就会自动触发UI + * 同时页面销毁会自动取消网络请求: 因为他使用`scopeNetLife`. 生命周期跟随当前viewModel + * + * 本质上我并不推荐将Scope定义在ViewModel中(仅仅换个位置要多写很多代码), 特别是妄图在ViewModel中请求网络却要求更新UI */ fun fetchUserInfo() = scopeNetLife { userInfo.value = Get(Api.GAME).await() + updateTime = System.currentTimeMillis() } - /** 返回Deferred, 可以灵活使用, 支持并发组合 */ - fun CoroutineScope.fetchList() = Get(Api.TEST) + /** + * 非阻塞异步任务 + * 返回Deferred, 调用await()才会返回结果. 调用即执行任务 + */ + fun fetchList(scope: CoroutineScope) = scope.Get(Api.TEST) - /** 直接返回数据, 会阻塞直至数据返回 */ + /** + * 阻塞异步任务 + * 直接返回结果 + */ suspend fun fetchPrecessData() = coroutineScope { val response = Get(Api.TEST).await() response + "处理数据" diff --git a/sample/src/main/res/raw/config.json b/sample/src/main/res/raw/config.json new file mode 100644 index 000000000..9b18eec5d --- /dev/null +++ b/sample/src/main/res/raw/config.json @@ -0,0 +1,3 @@ +{ + "maintain": false +} \ No newline at end of file diff --git a/sample/src/main/res/raw/user.json b/sample/src/main/res/raw/user.json new file mode 100644 index 000000000..a8cfdde18 --- /dev/null +++ b/sample/src/main/res/raw/user.json @@ -0,0 +1,6 @@ +{ + "userId": 23123132, + "username": "test_account", + "age": 17, + "balance": "123.24" +} \ No newline at end of file