前言
最近在学习Jetpack Compose,在之前的Android开发中,我们经常使用 Retrofit 实现网络数据请求,那在Jetpack Compose我们应该怎样使用Retrofit实现网络数据请求呢,这一篇介绍下使用 Retrofit+协程+Kotlin Flow+Paging 实现网络数据分页查询。
篇外准备
为了更真实的网络请求,我使用 SpringBoot 创建了一个项目来写我们使用的接口,我们看下代码:
@RestController
@CrossOrigin(origins = "*")
@RequestMapping("/users")
public class UserController {
@GetMapping("/getUserList")
public List<UserVo> getUserList(Integer pageNum, Integer pageSize) {
System.out.println("pageNum==="+pageNum + " ,pageSize===="+pageSize);
List<UserVo> list = new ArrayList<>();
for(int i = 0; i < 500; i++) {
UserVo userVo = new UserVo();
userVo.setUserId(i);
userVo.setName("小明"+i);
list.add(userVo);
}
int start = pageNum * pageSize - pageSize;
int end = start + pageSize;
return list.subList(start, end);
}
}
@Data
public class UserVo {
private int userId;
private String name;
}
接口比较简单,只是根据pageNum和pageSize参数返回了包含name和userId的列表数据,接口的结构为:
(当然,这部分是可以不看的,正常开发中接口由后端负责...)
依赖引入
implementation "androidx.paging:paging-compose:1.0.0-alpha14"
implementation 'com.squareup.retrofit2:retrofit:2.6.1'
implementation 'com.squareup.retrofit2:converter-gson:2.1.0'
实体类创建
我们看了接口的结构,创建一个和接口的字段对应上的User类,代码如下:
@Parcelize
class User(
val userId: Long,
val name: String,
): Parcelable
Retrofit单例创建
object HttpClient {
private const val BASE_URL = "你的服务器IP或域名,端口"
private val retrofit: Retrofit by lazy {
Retrofit.Builder()
.baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create())
.build()
}
fun <T> create(clazz: Class<T>): T {
return retrofit.create(clazz) as T
}
}
网络请求接口 UserApi 创建
interface UserApi{
@GET("/users/getUserList")
suspend fun getUserList(
@Query("pageNum") pageNum: Int,
@Query("pageSize") pageSize: Int
): List<User>
}
UserSource分页数据源创建
class UserSource: PagingSource<Int, User>() {
override fun getRefreshKey(state: PagingState<Int, User>): Int? {
return null
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, User> {
val nextPage = params.key ?: 1
/** 网络数据请求*/
val data = HttpClient.create(UserApi::class.java).getUserList(nextPage, 10)
return try {
LoadResult.Page(
data = data,
prevKey = if (nextPage == 1) null else nextPage - 1,
nextKey = nextPage + 1
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
UserViewModel创建
class UserViewModel: ViewModel() {
val userItems: Flow<PagingData<User>> =
Pager(PagingConfig(pageSize = 10, prefetchDistance = 1)) {
UserSource()
}.flow.cachedIn(viewModelScope)
}
UserScreen组合函数页面创建
这个页面比较简单,只是一个显示user数据的列表
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun UserScreen(viewModel: UserViewModel = UserViewModel()) {
val lazyUserItems = viewModel.userItems.collectAsLazyPagingItems()
val scrollState = rememberLazyListState()
Surface(
Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.background)
.autoCloseKeyboard()
) {
Scaffold(
content = { innerPadding ->
Box(modifier = Modifier.padding(innerPadding)) {
LazyColumn(
contentPadding = innerPadding,
state = scrollState
) {
items(lazyUserItems) {
it?.let {
Column {
Box(modifier = Modifier
.fillMaxWidth()
.height(60.dp),
contentAlignment = Alignment.Center
) {
Text(
text = "name: ${it.name}, userId: ${it.userId}",
fontSize = 18.sp
)
}
CQDivider()
}
}
}
lazyUserItems.apply {
when (loadState.append) {
is LoadState.Loading -> {
item {
Loading()
}
} else -> {
}
}
}
item {
Spacer(modifier = Modifier.height(60.dp))
}
}
}
}
)
}
}
到这里,使用 Retrofit+协程+Kotlin Flow+Paging 实现网络数据分页查询的功能就实现了,我们来看下效果:
总结
(1)Retrofit 是一个Android开发常用的网络请求框架,使用起来高效简洁。
(2)协程 是一个Kotlin提供了一种名为协程的轻量级线程,可以简化异步编程,性能更高。
(3)Kotlin Flow 是基于 Kotlin 协程的库,专门用于处理异步数据流,它的设计灵感来自于响应式编程,类似RxJava。
(4)Paging 是一个Jetpack库里的分页组件。