19
19
20
20
/**
21
21
* Data source to power a {@link FirestorePagingAdapter}.
22
+ *
23
+ * Note: although loadInitial, loadBefore, and loadAfter are not called on the main thread by the
24
+ * paging library, we treat them as if they were so that we can facilitate retry without
25
+ * managing our own thread pool or requiring the user to pass us an executor.
22
26
*/
23
27
@ RestrictTo (RestrictTo .Scope .LIBRARY_GROUP )
24
28
public class FirestoreDataSource extends PageKeyedDataSource <PageKey , DocumentSnapshot > {
@@ -43,29 +47,35 @@ public DataSource<PageKey, DocumentSnapshot> create() {
43
47
44
48
private final Query mBaseQuery ;
45
49
50
+ private Runnable mRetryRunnable ;
51
+
46
52
public FirestoreDataSource (Query baseQuery ) {
47
53
mBaseQuery = baseQuery ;
48
54
}
49
55
50
56
@ Override
51
- public void loadInitial (@ NonNull LoadInitialParams <PageKey > params ,
57
+ public void loadInitial (@ NonNull final LoadInitialParams <PageKey > params ,
52
58
@ NonNull final LoadInitialCallback <PageKey , DocumentSnapshot > callback ) {
53
59
54
60
// Set initial loading state
55
61
mLoadingState .postValue (LoadingState .LOADING_INITIAL );
56
62
57
63
mBaseQuery .limit (params .requestedLoadSize )
58
64
.get ()
59
- .addOnSuccessListener (new OnSuccessListener < QuerySnapshot > () {
65
+ .addOnSuccessListener (new OnLoadSuccessListener () {
60
66
@ Override
61
- public void onSuccess ( QuerySnapshot snapshot ) {
67
+ protected void setResult ( @ NonNull QuerySnapshot snapshot ) {
62
68
PageKey nextPage = getNextPageKey (snapshot );
63
69
callback .onResult (snapshot .getDocuments (), null , nextPage );
64
70
65
- mLoadingState .postValue (LoadingState .LOADED );
66
71
}
67
72
})
68
- .addOnFailureListener (new OnLoadFailureListener ());
73
+ .addOnFailureListener (new OnLoadFailureListener () {
74
+ @ Override
75
+ protected Runnable getRetryRunnable () {
76
+ return getRetryLoadInitial (params , callback );
77
+ }
78
+ });
69
79
70
80
}
71
81
@@ -80,7 +90,7 @@ public void loadBefore(@NonNull LoadParams<PageKey> params,
80
90
}
81
91
82
92
@ Override
83
- public void loadAfter (@ NonNull LoadParams <PageKey > params ,
93
+ public void loadAfter (@ NonNull final LoadParams <PageKey > params ,
84
94
@ NonNull final LoadCallback <PageKey , DocumentSnapshot > callback ) {
85
95
final PageKey key = params .key ;
86
96
@@ -89,16 +99,19 @@ public void loadAfter(@NonNull LoadParams<PageKey> params,
89
99
90
100
key .getPageQuery (mBaseQuery , params .requestedLoadSize )
91
101
.get ()
92
- .addOnSuccessListener (new OnSuccessListener < QuerySnapshot > () {
102
+ .addOnSuccessListener (new OnLoadSuccessListener () {
93
103
@ Override
94
- public void onSuccess ( QuerySnapshot snapshot ) {
104
+ protected void setResult ( @ NonNull QuerySnapshot snapshot ) {
95
105
PageKey nextPage = getNextPageKey (snapshot );
96
106
callback .onResult (snapshot .getDocuments (), nextPage );
97
-
98
- mLoadingState .postValue (LoadingState .LOADED );
99
107
}
100
108
})
101
- .addOnFailureListener (new OnLoadFailureListener ());
109
+ .addOnFailureListener (new OnLoadFailureListener () {
110
+ @ Override
111
+ protected Runnable getRetryRunnable () {
112
+ return getRetryLoadAfter (params , callback );
113
+ }
114
+ });
102
115
103
116
}
104
117
@@ -113,6 +126,21 @@ public LiveData<LoadingState> getLoadingState() {
113
126
return mLoadingState ;
114
127
}
115
128
129
+ public void retry () {
130
+ LoadingState currentState = mLoadingState .getValue ();
131
+ if (currentState != LoadingState .ERROR ) {
132
+ Log .w (TAG , "retry() not valid when in state: " + currentState );
133
+ return ;
134
+ }
135
+
136
+ if (mRetryRunnable == null ) {
137
+ Log .w (TAG , "retry() called with no eligible retry runnable." );
138
+ return ;
139
+ }
140
+
141
+ mRetryRunnable .run ();
142
+ }
143
+
116
144
@ Nullable
117
145
private DocumentSnapshot getLast (@ NonNull List <DocumentSnapshot > data ) {
118
146
if (data .isEmpty ()) {
@@ -122,10 +150,46 @@ private DocumentSnapshot getLast(@NonNull List<DocumentSnapshot> data) {
122
150
}
123
151
}
124
152
153
+ @ NonNull
154
+ private Runnable getRetryLoadAfter (@ NonNull final LoadParams <PageKey > params ,
155
+ @ NonNull final LoadCallback <PageKey , DocumentSnapshot > callback ) {
156
+ return new Runnable () {
157
+ @ Override
158
+ public void run () {
159
+ loadAfter (params , callback );
160
+ }
161
+ };
162
+ }
163
+
164
+ @ NonNull
165
+ private Runnable getRetryLoadInitial (@ NonNull final LoadInitialParams <PageKey > params ,
166
+ @ NonNull final LoadInitialCallback <PageKey , DocumentSnapshot > callback ) {
167
+ return new Runnable () {
168
+ @ Override
169
+ public void run () {
170
+ loadInitial (params , callback );
171
+ }
172
+ };
173
+ }
174
+
175
+ /**
176
+ * Success listener that sets success state and nullifies the retry runnable.
177
+ */
178
+ private abstract class OnLoadSuccessListener implements OnSuccessListener <QuerySnapshot > {
179
+
180
+ @ Override
181
+ public void onSuccess (QuerySnapshot snapshots ) {
182
+ mLoadingState .postValue (LoadingState .LOADED );
183
+ mRetryRunnable = null ;
184
+ }
185
+
186
+ protected abstract void setResult (@ NonNull QuerySnapshot snapshot );
187
+ }
188
+
125
189
/**
126
- * Error listener that just logs and sets the error state.
190
+ * Error listener that logs, sets the error state, and sets up retry .
127
191
*/
128
- private class OnLoadFailureListener implements OnFailureListener {
192
+ private abstract class OnLoadFailureListener implements OnFailureListener {
129
193
130
194
@ Override
131
195
public void onFailure (@ NonNull Exception e ) {
@@ -134,6 +198,11 @@ public void onFailure(@NonNull Exception e) {
134
198
// On error we do NOT post any value to the PagedList, we just tell
135
199
// the developer that we are now in the error state.
136
200
mLoadingState .postValue (LoadingState .ERROR );
201
+
202
+ // Set the retry action
203
+ mRetryRunnable = getRetryRunnable ();
137
204
}
205
+
206
+ protected abstract Runnable getRetryRunnable ();
138
207
}
139
208
}
0 commit comments