23
23
import com .liulishuo .filedownloader .services .DownloadMgrInitialParams ;
24
24
import com .liulishuo .filedownloader .services .ForegroundServiceConfig ;
25
25
import com .liulishuo .filedownloader .stream .FileDownloadOutputStream ;
26
+ import com .liulishuo .filedownloader .util .FileDownloadExecutors ;
26
27
import com .liulishuo .filedownloader .util .FileDownloadHelper ;
27
28
import com .liulishuo .filedownloader .util .FileDownloadLog ;
28
29
import com .liulishuo .filedownloader .util .FileDownloadUtils ;
29
30
30
31
import java .io .File ;
31
32
import java .io .IOException ;
33
+ import java .util .ArrayList ;
32
34
import java .util .Iterator ;
35
+ import java .util .List ;
36
+ import java .util .concurrent .Future ;
37
+ import java .util .concurrent .ThreadPoolExecutor ;
38
+ import java .util .concurrent .atomic .AtomicInteger ;
33
39
34
40
/**
35
41
* The holder for supported custom components.
@@ -168,120 +174,149 @@ private DownloadMgrInitialParams getDownloadMgrInitialParams() {
168
174
return initialParams ;
169
175
}
170
176
171
- private static void maintainDatabase (FileDownloadDatabase .Maintainer maintainer ) {
177
+ private static void maintainDatabase (final FileDownloadDatabase .Maintainer maintainer ) {
172
178
final Iterator <FileDownloadModel > iterator = maintainer .iterator ();
173
- long refreshDataCount = 0 ;
174
- long removedDataCount = 0 ;
175
- long resetIdCount = 0 ;
179
+ final AtomicInteger removedDataCount = new AtomicInteger ( 0 ) ;
180
+ final AtomicInteger resetIdCount = new AtomicInteger ( 0 ) ;
181
+ final AtomicInteger refreshDataCount = new AtomicInteger ( 0 ) ;
176
182
final FileDownloadHelper .IdGenerator idGenerator = getImpl ().getIdGeneratorInstance ();
177
183
178
184
final long startTimestamp = System .currentTimeMillis ();
185
+ final List <Future > futures = new ArrayList <>();
186
+ final ThreadPoolExecutor maintainThreadPool = FileDownloadExecutors .newDefaultThreadPool (3 ,
187
+ FileDownloadUtils .getThreadPoolName ("MaintainDatabase" ));
179
188
try {
180
189
while (iterator .hasNext ()) {
181
- boolean isInvalid = false ;
182
190
final FileDownloadModel model = iterator .next ();
183
- do {
184
- if (model .getStatus () == FileDownloadStatus .progress
185
- || model .getStatus () == FileDownloadStatus .connected
186
- || model .getStatus () == FileDownloadStatus .error
187
- || (model .getStatus () == FileDownloadStatus .pending && model
188
- .getSoFar () > 0 )
191
+ final Future modelFuture = maintainThreadPool .submit (new Runnable () {
192
+ @ Override
193
+ public void run () {
194
+ boolean isInvalid = false ;
195
+ do {
196
+ if (model .getStatus () == FileDownloadStatus .progress
197
+ || model .getStatus () == FileDownloadStatus .connected
198
+ || model .getStatus () == FileDownloadStatus .error
199
+ || (model .getStatus () == FileDownloadStatus .pending && model
200
+ .getSoFar () > 0 )
189
201
) {
190
- // Ensure can be covered by RESUME FROM BREAKPOINT.
191
- model .setStatus (FileDownloadStatus .paused );
192
- }
193
- final String targetFilePath = model .getTargetFilePath ();
194
- if (targetFilePath == null ) {
195
- // no target file path, can't used to resume from breakpoint.
196
- isInvalid = true ;
197
- break ;
198
- }
199
-
200
- final File targetFile = new File (targetFilePath );
201
- // consider check in new thread, but SQLite lock | file lock aways effect, so
202
- // sync
203
- if (model .getStatus () == FileDownloadStatus .paused
204
- && FileDownloadUtils .isBreakpointAvailable (model .getId (), model ,
205
- model .getPath (), null )) {
206
- // can be reused in the old mechanism(no-temp-file).
207
-
208
- final File tempFile = new File (model .getTempFilePath ());
202
+ // Ensure can be covered by RESUME FROM BREAKPOINT.
203
+ model .setStatus (FileDownloadStatus .paused );
204
+ }
205
+ final String targetFilePath = model .getTargetFilePath ();
206
+ if (targetFilePath == null ) {
207
+ // no target file path, can't used to resume from breakpoint.
208
+ isInvalid = true ;
209
+ break ;
210
+ }
209
211
210
- if (!tempFile .exists () && targetFile .exists ()) {
211
- final boolean successRename = targetFile .renameTo (tempFile );
212
- if (FileDownloadLog .NEED_LOG ) {
213
- FileDownloadLog .d (FileDownloadDatabase .class ,
214
- "resume from the old no-temp-file architecture "
215
- + "[%B], [%s]->[%s]" ,
216
- successRename , targetFile .getPath (), tempFile .getPath ());
212
+ final File targetFile = new File (targetFilePath );
213
+ // consider check in new thread, but SQLite lock | file lock aways
214
+ // effect, so sync
215
+ if (model .getStatus () == FileDownloadStatus .paused
216
+ && FileDownloadUtils .isBreakpointAvailable (model .getId (), model ,
217
+ model .getPath (), null )) {
218
+ // can be reused in the old mechanism(no-temp-file).
219
+
220
+ final File tempFile = new File (model .getTempFilePath ());
221
+
222
+ if (!tempFile .exists () && targetFile .exists ()) {
223
+ final boolean successRename = targetFile .renameTo (tempFile );
224
+ if (FileDownloadLog .NEED_LOG ) {
225
+ FileDownloadLog .d (FileDownloadDatabase .class ,
226
+ "resume from the old no-temp-file architecture "
227
+ + "[%B], [%s]->[%s]" ,
228
+ successRename , targetFile .getPath (),
229
+ tempFile .getPath ());
230
+ }
231
+ }
232
+ }
217
233
234
+ /**
235
+ * Remove {@code model} from DB if it can't used for judging whether the
236
+ * old-downloaded file is valid for reused & it can't used for resuming
237
+ * from BREAKPOINT, In other words, {@code model} is no use anymore for
238
+ * FileDownloader.
239
+ */
240
+ if (model .getStatus () == FileDownloadStatus .pending
241
+ && model .getSoFar () <= 0 ) {
242
+ // This model is redundant.
243
+ isInvalid = true ;
244
+ break ;
218
245
}
219
- }
220
- }
221
246
222
- /**
223
- * Remove {@code model} from DB if it can't used for judging whether the
224
- * old-downloaded file is valid for reused & it can't used for resuming from
225
- * BREAKPOINT, In other words, {@code model} is no use anymore for
226
- * FileDownloader.
227
- */
228
- if (model .getStatus () == FileDownloadStatus .pending && model .getSoFar () <= 0 ) {
229
- // This model is redundant.
230
- isInvalid = true ;
231
- break ;
232
- }
247
+ if (!FileDownloadUtils .isBreakpointAvailable (model .getId (), model )) {
248
+ // It can't used to resuming from breakpoint.
249
+ isInvalid = true ;
250
+ break ;
251
+ }
233
252
234
- if (! FileDownloadUtils . isBreakpointAvailable ( model . getId (), model )) {
235
- // It can't used to resuming from breakpoint .
236
- isInvalid = true ;
237
- break ;
238
- }
253
+ if (targetFile . exists ( )) {
254
+ // It has already completed downloading .
255
+ isInvalid = true ;
256
+ break ;
257
+ }
239
258
240
- if (targetFile .exists ()) {
241
- // It has already completed downloading.
242
- isInvalid = true ;
243
- break ;
244
- }
259
+ } while (false );
260
+
261
+
262
+ if (isInvalid ) {
263
+ maintainer .onRemovedInvalidData (model );
264
+ removedDataCount .addAndGet (1 );
265
+ } else {
266
+ final int oldId = model .getId ();
267
+ final int newId = idGenerator .transOldId (oldId , model .getUrl (),
268
+ model .getPath (), model .isPathAsDirectory ());
269
+ if (newId != oldId ) {
270
+ if (FileDownloadLog .NEED_LOG ) {
271
+ FileDownloadLog .d (FileDownloadDatabase .class ,
272
+ "the id is changed on restoring from db:"
273
+ + " old[%d] -> new[%d]" ,
274
+ oldId , newId );
275
+ }
276
+ model .setId (newId );
277
+ maintainer .changeFileDownloadModelId (oldId , model );
278
+ resetIdCount .addAndGet (1 );
279
+ }
245
280
246
- } while (false );
247
-
248
-
249
- if (isInvalid ) {
250
- iterator .remove ();
251
- maintainer .onRemovedInvalidData (model );
252
- removedDataCount ++;
253
- } else {
254
- final int oldId = model .getId ();
255
- final int newId = idGenerator .transOldId (oldId , model .getUrl (), model .getPath (),
256
- model .isPathAsDirectory ());
257
- if (newId != oldId ) {
258
- if (FileDownloadLog .NEED_LOG ) {
259
- FileDownloadLog .d (FileDownloadDatabase .class ,
260
- "the id is changed on restoring from db:"
261
- + " old[%d] -> new[%d]" ,
262
- oldId , newId );
281
+ maintainer .onRefreshedValidData (model );
282
+ refreshDataCount .addAndGet (1 );
263
283
}
264
- model .setId (newId );
265
- maintainer .changeFileDownloadModelId (oldId , model );
266
- resetIdCount ++;
267
284
}
268
-
269
- maintainer .onRefreshedValidData (model );
270
- refreshDataCount ++;
271
- }
285
+ });
286
+ futures .add (modelFuture );
272
287
}
273
288
274
289
} finally {
275
- FileDownloadUtils .markConverted (FileDownloadHelper .getAppContext ());
276
- maintainer .onFinishMaintain ();
277
- // 566 data consumes about 140ms
290
+ final Future markConvertedFuture = maintainThreadPool .submit (new Runnable () {
291
+ @ Override
292
+ public void run () {
293
+ FileDownloadUtils .markConverted (FileDownloadHelper .getAppContext ());
294
+ }
295
+ });
296
+ futures .add (markConvertedFuture );
297
+ final Future finishMaintainFuture = maintainThreadPool .submit (new Runnable () {
298
+ @ Override
299
+ public void run () {
300
+ maintainer .onFinishMaintain ();
301
+ }
302
+ });
303
+ futures .add (finishMaintainFuture );
304
+ for (Future future : futures ) {
305
+ try {
306
+ if (!future .isDone ()) future .get ();
307
+ } catch (Exception e ) {
308
+ if (FileDownloadLog .NEED_LOG ) FileDownloadLog
309
+ .e (FileDownloadDatabase .class , e , e .getMessage ());
310
+ }
311
+ }
312
+ futures .clear ();
278
313
if (FileDownloadLog .NEED_LOG ) {
279
314
FileDownloadLog .d (FileDownloadDatabase .class ,
280
315
"refreshed data count: %d , delete data count: %d, reset id count:"
281
316
+ " %d. consume %d" ,
282
- refreshDataCount , removedDataCount , resetIdCount ,
317
+ refreshDataCount . get () , removedDataCount . get () , resetIdCount . get () ,
283
318
System .currentTimeMillis () - startTimestamp );
284
319
}
285
320
}
286
321
}
287
- }
322
+ }
0 commit comments