从DataSource开始

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
class ActivityListDataSource(val groupId: String, val activityType: String) :  
PagingSource<Int, GroupActivity>() {
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, GroupActivity> {
Log.wtf("test", Exception())
return try {
val curPage = params.key ?: 0
val pageData = SuspendGroupApi.fetchGroupActivities(groupId, activityType, curPage)
val nextPage = curPage + pageData.count

if (pageData.total > 0) {
if (pageData.activities != null && pageData.activities!!.isNotEmpty()) {
LoadResult.Page(
pageData.activities!!.toList(),
null, if (nextPage < pageData.total) nextPage else null
)
} else {
LoadResult.Error(Exception("No more data"))
}
} else {
LoadResult.Error(Exception("No more data"))
}
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}

继承PagingSource类,可以看到我们只需要重写load,这是Paging的核心方法,我们就从这个方法开始看吧。
Log.wtf("test", Exception()),我们可以通过这行代码打印出一个异常,这样我们就可以看到方法的调用栈了。

1
2
3
at com.***.frodo.group.util.ActivityListDataSource.load(ActivityListDataSource.kt:11)
at androidx.paging.PageFetcherSnapshot.doInitialLoad(PageFetcherSnapshot.kt:306)
at androidx.paging.PageFetcherSnapshot$pageEventFlow$1.invokeSuspend(PageFetcherSnapshot.kt:149)

实际的调用栈很深,我们就从最顶层开始看吧。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
private suspend fun doInitialLoad(scope: CoroutineScope, state: PagerState<Key, Value>) {  
stateLock.withLock { state.setLoading(REFRESH, false) }

val params = loadParams(REFRESH, initialKey)
when (val result = pagingSource.load(params)) {
is Page<Key, Value> -> {
val insertApplied = stateLock.withLock { state.insert(0, REFRESH, result) }

// Update loadStates which are sent along with this load's Insert PageEvent.
stateLock.withLock {
state.loadStates.set(REFRESH, false, NotLoading.Incomplete)
if (result.prevKey == null) {
state.loadStates.set(
type = PREPEND,
remote = false,
state = when (remoteMediatorAccessor) {
null -> NotLoading.Complete
else -> NotLoading.Incomplete
}
)
}
if (result.nextKey == null) {
state.loadStates.set(
type = APPEND,
remote = false,
state = when (remoteMediatorAccessor) {
null -> NotLoading.Complete
else -> NotLoading.Incomplete
}
)
}
}

// Send insert event after load state updates, so that endOfPaginationReached is
// correctly reflected in the insert event. Note that we only send the event if the // insert was successfully applied in the case of cancellation due to page dropping. if (insertApplied) {
stateLock.withLock {
with(state) {
pageEventCh.send(result.toPageEvent(REFRESH, config.enablePlaceholders))
}
} }

// Launch any RemoteMediator boundary calls after applying initial insert.
if (remoteMediatorAccessor != null) {
if (result.prevKey == null || result.nextKey == null) {
val pagingState = stateLock.withLock { state.currentPagingState(lastHint) }

if (result.prevKey == null) {
remoteMediatorAccessor.doBoundaryCall(scope, PREPEND, pagingState)
}

if (result.nextKey == null) {
remoteMediatorAccessor.doBoundaryCall(scope, APPEND, pagingState)
}
}
}
}
is LoadResult.Error -> stateLock.withLock {
val loadState = Error(result.throwable)
if (state.loadStates.set(REFRESH, false, loadState)) {
pageEventCh.send(LoadStateUpdate(REFRESH, false, loadState))
}
}
}
}

首先,进入到PageFetcherSnapshot.doInitialLoad方法,开始调用了state.setLoading(REFRESH, false)方法,这个方法的作用是设置PagerStateLoading
接下来是这个方法loadParams(REFRESH, initialKey)是载入一些配置。
之后的pagingSource.load(params)就到了调用我们的load方法,拿掉数据后,将其加入PageStates.pages中。