Merge pull request #4937 from ByteHamster/downloadservice-cleanup

Clean up DownloadService
This commit is contained in:
ByteHamster 2021-02-15 15:20:46 +01:00 committed by GitHub
commit 5b7c2b7483
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 87 additions and 96 deletions

View File

@ -68,8 +68,8 @@ public class DownloadlistAdapter extends BaseAdapter {
holder.secondaryActionButton.setContentDescription(context.getString(R.string.cancel_download_label)); holder.secondaryActionButton.setContentDescription(context.getString(R.string.cancel_download_label));
holder.secondaryActionButton.setTag(downloader); holder.secondaryActionButton.setTag(downloader);
holder.secondaryActionButton.setOnClickListener(butSecondaryListener); holder.secondaryActionButton.setOnClickListener(butSecondaryListener);
holder.secondaryActionProgress.setPercentage(0, request);
boolean percentageWasSet = false;
String status = ""; String status = "";
if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) { if (request.getFeedfileType() == Feed.FEEDFILETYPE_FEED) {
status += context.getString(R.string.download_type_feed); status += context.getString(R.string.download_type_feed);
@ -85,8 +85,12 @@ public class DownloadlistAdapter extends BaseAdapter {
status += " / " + Formatter.formatShortFileSize(context, request.getSize()); status += " / " + Formatter.formatShortFileSize(context, request.getSize());
holder.secondaryActionProgress.setPercentage( holder.secondaryActionProgress.setPercentage(
0.01f * Math.max(1, request.getProgressPercent()), request); 0.01f * Math.max(1, request.getProgressPercent()), request);
percentageWasSet = true;
} }
} }
if (!percentageWasSet) {
holder.secondaryActionProgress.setPercentage(0, request);
}
holder.status.setText(status); holder.status.setText(status);
return convertView; return convertView;

View File

@ -169,6 +169,7 @@ public class DownloadService extends Service {
Notification notification = notificationManager.updateNotifications( Notification notification = notificationManager.updateNotifications(
requester.getNumberOfDownloads(), downloads); requester.getNumberOfDownloads(), downloads);
startForeground(R.id.notification_downloading, notification); startForeground(R.id.notification_downloading, notification);
setupNotificationUpdaterIfNecessary();
syncExecutor.execute(() -> onDownloadQueued(intent)); syncExecutor.execute(() -> onDownloadQueued(intent));
} else if (numberOfDownloads.get() == 0) { } else if (numberOfDownloads.get() == 0) {
stopForeground(true); stopForeground(true);
@ -192,10 +193,6 @@ public class DownloadService extends Service {
registerReceiver(cancelDownloadReceiver, cancelDownloadReceiverFilter); registerReceiver(cancelDownloadReceiver, cancelDownloadReceiverFilter);
downloadCompletionThread.start(); downloadCompletionThread.start();
Notification notification = notificationManager.updateNotifications(
requester.getNumberOfDownloads(), downloads);
startForeground(R.id.notification_downloading, notification);
} }
@Override @Override
@ -258,13 +255,13 @@ public class DownloadService extends Service {
handleSuccessfulDownload(downloader); handleSuccessfulDownload(downloader);
removeDownload(downloader); removeDownload(downloader);
numberOfDownloads.decrementAndGet(); numberOfDownloads.decrementAndGet();
queryDownloadsAsync(); stopServiceIfEverythingDoneAsync();
}); });
} else { } else {
handleFailedDownload(downloader); handleFailedDownload(downloader);
removeDownload(downloader); removeDownload(downloader);
numberOfDownloads.decrementAndGet(); numberOfDownloads.decrementAndGet();
queryDownloadsAsync(); stopServiceIfEverythingDoneAsync();
} }
} catch (InterruptedException e) { } catch (InterruptedException e) {
Log.e(TAG, "DownloadCompletionThread was interrupted"); Log.e(TAG, "DownloadCompletionThread was interrupted");
@ -413,7 +410,7 @@ public class DownloadService extends Service {
} }
postDownloaders(); postDownloaders();
} }
queryDownloads(); stopServiceIfEverythingDone();
} }
}; };
@ -484,7 +481,7 @@ public class DownloadService extends Service {
postDownloaders(); postDownloaders();
}); });
} }
handler.post(this::queryDownloads); handler.post(this::stopServiceIfEverythingDone);
} }
private static boolean isEnqueued(@NonNull DownloadRequest request, private static boolean isEnqueued(@NonNull DownloadRequest request,
@ -541,30 +538,26 @@ public class DownloadService extends Service {
* Calls query downloads on the services main thread. This method should be used instead of queryDownloads if it is * Calls query downloads on the services main thread. This method should be used instead of queryDownloads if it is
* used from a thread other than the main thread. * used from a thread other than the main thread.
*/ */
private void queryDownloadsAsync() { private void stopServiceIfEverythingDoneAsync() {
handler.post(DownloadService.this::queryDownloads); handler.post(DownloadService.this::stopServiceIfEverythingDone);
} }
/** /**
* Check if there's something else to download, otherwise stop. * Check if there's something else to download, otherwise stop.
*/ */
private void queryDownloads() { private void stopServiceIfEverythingDone() {
Log.d(TAG, numberOfDownloads.get() + " downloads left"); Log.d(TAG, numberOfDownloads.get() + " downloads left");
if (numberOfDownloads.get() <= 0 && DownloadRequester.getInstance().hasNoDownloads()) { if (numberOfDownloads.get() <= 0 && DownloadRequester.getInstance().hasNoDownloads()) {
Log.d(TAG, "Number of downloads is " + numberOfDownloads.get() + ", attempting shutdown"); Log.d(TAG, "Attempting shutdown");
stopForeground(true); stopForeground(true);
stopSelf(); stopSelf();
if (notificationUpdater != null) {
// Trick to hide the notification more quickly when the service is stopped
// Without this, the second-last update of the notification stays for 3 seconds after onDestroy returns
notificationUpdater.run(); notificationUpdater.run();
} else { NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
Log.d(TAG, "Skipping notification update"); nm.cancel(R.id.notification_downloading);
}
} else {
setupNotificationUpdater();
Notification notification = notificationManager.updateNotifications(
requester.getNumberOfDownloads(), downloads);
startForeground(R.id.notification_downloading, notification);
} }
} }
@ -617,7 +610,7 @@ public class DownloadService extends Service {
/** /**
* Schedules the notification updater task if it hasn't been scheduled yet. * Schedules the notification updater task if it hasn't been scheduled yet.
*/ */
private void setupNotificationUpdater() { private void setupNotificationUpdaterIfNecessary() {
if (notificationUpdater == null) { if (notificationUpdater == null) {
Log.d(TAG, "Setting up notification updater"); Log.d(TAG, "Setting up notification updater");
notificationUpdater = new NotificationUpdater(); notificationUpdater = new NotificationUpdater();

View File

@ -28,7 +28,10 @@ public class DownloadServiceNotification {
private void setupNotificationBuilders() { private void setupNotificationBuilders() {
notificationCompatBuilder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_DOWNLOADING) notificationCompatBuilder = new NotificationCompat.Builder(context, NotificationUtils.CHANNEL_ID_DOWNLOADING)
.setOngoing(true) .setOngoing(false)
.setWhen(0)
.setOnlyAlertOnce(true)
.setShowWhen(false)
.setContentIntent(ClientConfig.downloadServiceCallbacks.getNotificationContentIntent(context)) .setContentIntent(ClientConfig.downloadServiceCallbacks.getNotificationContentIntent(context))
.setSmallIcon(R.drawable.ic_notification_sync); .setSmallIcon(R.drawable.ic_notification_sync);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {

View File

@ -16,7 +16,6 @@ import de.danoeh.antennapod.core.event.MessageEvent;
import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.Feed;
import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedItem;
import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.FeedMedia;
import de.danoeh.antennapod.core.feed.FeedPreferences;
import de.danoeh.antennapod.core.feed.LocalFeedUpdater; import de.danoeh.antennapod.core.feed.LocalFeedUpdater;
import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.service.download.DownloadStatus;
@ -31,6 +30,7 @@ import java.util.Collections;
import java.util.Date; import java.util.Date;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.Callable; import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutorService;
@ -121,7 +121,18 @@ public final class DBTasks {
throw new IllegalStateException("DBTasks.refreshAllFeeds() must not be called from the main thread."); throw new IllegalStateException("DBTasks.refreshAllFeeds() must not be called from the main thread.");
} }
refreshFeeds(context, DBReader.getFeedList(), initiatedByUser); List<Feed> feeds = DBReader.getFeedList();
ListIterator<Feed> iterator = feeds.listIterator();
while (iterator.hasNext()) {
if (!iterator.next().getPreferences().getKeepUpdated()) {
iterator.remove();
}
}
try {
refreshFeeds(context, feeds, false, false, false);
} catch (DownloadRequestException e) {
e.printStackTrace();
}
isRefreshing.set(false); isRefreshing.set(false);
SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE); SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, MODE_PRIVATE);
@ -134,38 +145,6 @@ public final class DBTasks {
// See Issue #2577 for the details of the rationale // See Issue #2577 for the details of the rationale
} }
/**
* @param context
* @param feedList the list of feeds to refresh
* @param initiatedByUser a boolean indicating if the refresh was triggered by user action.
*/
private static void refreshFeeds(final Context context,
final List<Feed> feedList,
boolean initiatedByUser) {
for (Feed feed : feedList) {
FeedPreferences prefs = feed.getPreferences();
// feeds with !getKeepUpdated can only be refreshed
// directly from the FeedActivity
if (prefs.getKeepUpdated()) {
try {
refreshFeed(context, feed);
} catch (DownloadRequestException e) {
e.printStackTrace();
DBWriter.addDownloadStatus(
new DownloadStatus(feed,
feed.getHumanReadableIdentifier(),
DownloadError.ERROR_REQUEST_ERROR,
false,
e.getMessage(),
initiatedByUser)
);
}
}
}
}
/** /**
* Downloads all pages of the given feed even if feed has not been modified since last refresh * Downloads all pages of the given feed even if feed has not been modified since last refresh
* *
@ -174,7 +153,7 @@ public final class DBTasks {
*/ */
public static void forceRefreshCompleteFeed(final Context context, final Feed feed) { public static void forceRefreshCompleteFeed(final Context context, final Feed feed) {
try { try {
refreshFeed(context, feed, true, true, false); refreshFeeds(context, Collections.singletonList(feed), true, true, false);
} catch (DownloadRequestException e) { } catch (DownloadRequestException e) {
e.printStackTrace(); e.printStackTrace();
DBWriter.addDownloadStatus( DBWriter.addDownloadStatus(
@ -209,19 +188,6 @@ public final class DBTasks {
} }
} }
/**
* Refresh a specific Feed. The refresh may get canceled if the feed does not seem to be modified
* and the last update was only few days ago.
*
* @param context Used for requesting the download.
* @param feed The Feed object.
*/
private static void refreshFeed(Context context, Feed feed)
throws DownloadRequestException {
Log.d(TAG, "refreshFeed(feed.id: " + feed.getId() +")");
refreshFeed(context, feed, false, false, false);
}
/** /**
* Refresh a specific feed even if feed has not been modified since last refresh * Refresh a specific feed even if feed has not been modified since last refresh
* *
@ -231,25 +197,31 @@ public final class DBTasks {
public static void forceRefreshFeed(Context context, Feed feed, boolean initiatedByUser) public static void forceRefreshFeed(Context context, Feed feed, boolean initiatedByUser)
throws DownloadRequestException { throws DownloadRequestException {
Log.d(TAG, "refreshFeed(feed.id: " + feed.getId() + ")"); Log.d(TAG, "refreshFeed(feed.id: " + feed.getId() + ")");
refreshFeed(context, feed, false, true, initiatedByUser); refreshFeeds(context, Collections.singletonList(feed), false, true, initiatedByUser);
} }
private static void refreshFeed(Context context, Feed feed, boolean loadAllPages, boolean force, boolean initiatedByUser) private static void refreshFeeds(Context context, List<Feed> feeds, boolean loadAllPages,
throws DownloadRequestException { boolean force, boolean initiatedByUser) throws DownloadRequestException {
Feed f; List<Feed> localFeeds = new ArrayList<>();
String lastUpdate = feed.hasLastUpdateFailed() ? null : feed.getLastUpdate(); List<Feed> normalFeeds = new ArrayList<>();
if (feed.getPreferences() == null) {
f = new Feed(feed.getDownload_url(), lastUpdate, feed.getTitle());
} else {
f = new Feed(feed.getDownload_url(), lastUpdate, feed.getTitle(),
feed.getPreferences().getUsername(), feed.getPreferences().getPassword());
}
f.setId(feed.getId());
if (f.isLocalFeed()) { for (Feed feed : feeds) {
new Thread(() -> LocalFeedUpdater.updateFeed(f, context)).start(); if (feed.isLocalFeed()) {
localFeeds.add(feed);
} else { } else {
DownloadRequester.getInstance().downloadFeed(context, f, loadAllPages, force, initiatedByUser); normalFeeds.add(feed);
}
}
if (!localFeeds.isEmpty()) {
new Thread(() -> {
for (Feed feed : localFeeds) {
LocalFeedUpdater.updateFeed(feed, context);
}
}).start();
}
if (!normalFeeds.isEmpty()) {
DownloadRequester.getInstance().downloadFeeds(context, feeds, loadAllPages, force, initiatedByUser);
} }
} }

View File

@ -17,6 +17,7 @@ import org.apache.commons.io.FilenameUtils;
import java.io.File; import java.io.File;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
@ -184,16 +185,31 @@ public class DownloadRequester implements DownloadStateProvider {
} }
/** /**
* Downloads a feed * Downloads a feed.
* *
* @param context The application's environment. * @param context The application's environment.
* @param feed Feed to download * @param feed Feeds to download
* @param loadAllPages Set to true to download all pages * @param loadAllPages Set to true to download all pages
*/ */
public synchronized void downloadFeed(Context context, Feed feed, boolean loadAllPages, public synchronized void downloadFeed(Context context, Feed feed, boolean loadAllPages,
boolean force, boolean initiatedByUser) boolean force, boolean initiatedByUser) throws DownloadRequestException {
throws DownloadRequestException { downloadFeeds(context, Collections.singletonList(feed), loadAllPages, force, initiatedByUser);
if (feedFileValid(feed)) { }
/**
* Downloads a list of feeds.
*
* @param context The application's environment.
* @param feeds Feeds to download
* @param loadAllPages Set to true to download all pages
*/
public synchronized void downloadFeeds(Context context, List<Feed> feeds, boolean loadAllPages,
boolean force, boolean initiatedByUser) throws DownloadRequestException {
List<DownloadRequest> requests = new ArrayList<>();
for (Feed feed : feeds) {
if (!feedFileValid(feed)) {
continue;
}
String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null; String username = (feed.getPreferences() != null) ? feed.getPreferences().getUsername() : null;
String password = (feed.getPreferences() != null) ? feed.getPreferences().getPassword() : null; String password = (feed.getPreferences() != null) ? feed.getPreferences().getPassword() : null;
String lastModified = feed.isPaged() || force ? null : feed.getLastUpdate(); String lastModified = feed.isPaged() || force ? null : feed.getLastUpdate();
@ -206,9 +222,12 @@ public class DownloadRequester implements DownloadStateProvider {
true, username, password, lastModified, true, args, initiatedByUser true, username, password, lastModified, true, args, initiatedByUser
); );
if (request != null) { if (request != null) {
download(context, request); requests.add(request);
} }
} }
if (!requests.isEmpty()) {
download(context, requests.toArray(new DownloadRequest[0]));
}
} }
public synchronized void downloadFeed(Context context, Feed feed) throws DownloadRequestException { public synchronized void downloadFeed(Context context, Feed feed) throws DownloadRequestException {