2323import com .liulishuo .filedownloader .services .DownloadMgrInitialParams ;
2424import com .liulishuo .filedownloader .services .ForegroundServiceConfig ;
2525import com .liulishuo .filedownloader .stream .FileDownloadOutputStream ;
26+ import com .liulishuo .filedownloader .util .FileDownloadExecutors ;
2627import com .liulishuo .filedownloader .util .FileDownloadHelper ;
2728import com .liulishuo .filedownloader .util .FileDownloadLog ;
2829import com .liulishuo .filedownloader .util .FileDownloadUtils ;
2930
3031import java .io .File ;
3132import java .io .IOException ;
33+ import java .util .ArrayList ;
3234import 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 ;
3339
3440/**
3541 * The holder for supported custom components.
@@ -168,120 +174,149 @@ private DownloadMgrInitialParams getDownloadMgrInitialParams() {
168174 return initialParams ;
169175 }
170176
171- private static void maintainDatabase (FileDownloadDatabase .Maintainer maintainer ) {
177+ private static void maintainDatabase (final FileDownloadDatabase .Maintainer maintainer ) {
172178 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 ) ;
176182 final FileDownloadHelper .IdGenerator idGenerator = getImpl ().getIdGeneratorInstance ();
177183
178184 final long startTimestamp = System .currentTimeMillis ();
185+ final List <Future > futures = new ArrayList <>();
186+ final ThreadPoolExecutor maintainThreadPool = FileDownloadExecutors .newDefaultThreadPool (3 ,
187+ FileDownloadUtils .getThreadPoolName ("MaintainDatabase" ));
179188 try {
180189 while (iterator .hasNext ()) {
181- boolean isInvalid = false ;
182190 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 )
189201 ) {
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+ }
209211
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+ }
217233
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 ;
218245 }
219- }
220- }
221246
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+ }
233252
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+ }
239258
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+ }
245280
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 );
263283 }
264- model .setId (newId );
265- maintainer .changeFileDownloadModelId (oldId , model );
266- resetIdCount ++;
267284 }
268-
269- maintainer .onRefreshedValidData (model );
270- refreshDataCount ++;
271- }
285+ });
286+ futures .add (modelFuture );
272287 }
273288
274289 } 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 ();
278313 if (FileDownloadLog .NEED_LOG ) {
279314 FileDownloadLog .d (FileDownloadDatabase .class ,
280315 "refreshed data count: %d , delete data count: %d, reset id count:"
281316 + " %d. consume %d" ,
282- refreshDataCount , removedDataCount , resetIdCount ,
317+ refreshDataCount . get () , removedDataCount . get () , resetIdCount . get () ,
283318 System .currentTimeMillis () - startTimestamp );
284319 }
285320 }
286321 }
287- }
322+ }
0 commit comments