diff options
author | daniel oeh <daniel.oeh@gmail.com> | 2014-12-07 21:24:03 +0100 |
---|---|---|
committer | daniel oeh <daniel.oeh@gmail.com> | 2014-12-07 21:24:03 +0100 |
commit | f534481ed0f38159671081f646f0ab4f40415cbd (patch) | |
tree | 9e10f3de53befc2b8b441170f0062582e3573716 /core/src/main/java | |
parent | 24538d7ebb50cd18c83f6669743faa2d7be195ec (diff) | |
download | AntennaPod-f534481ed0f38159671081f646f0ab4f40415cbd.zip |
Avoid loading Chapters of multiple FeedItems at the same time
This should significantly reduce the time needed to load FeedItem lists with chapters, because chapters are from now on only loaded when a single FeedItem is requested.
Diffstat (limited to 'core/src/main/java')
4 files changed, 159 insertions, 79 deletions
diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java index d056917e1..42e4191f6 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedItem.java @@ -44,12 +44,45 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr private boolean read; private String paymentLink; private FlattrStatus flattrStatus; + + /** + * Is true if the database contains any chapters that belong to this item. This attribute is only + * written once by DBReader on initialization. + * The FeedItem might still have a non-null chapters value. In this case, the list of chapters + * has not been saved in the database yet. + * */ + private final boolean hasChapters; + + /** + * The list of chapters of this item. This might be null even if there are chapters of this item + * in the database. The 'hasChapters' attribute should be used to check if this item has any chapters. + * */ private List<Chapter> chapters; private FeedImage image; public FeedItem() { this.read = true; this.flattrStatus = new FlattrStatus(); + this.hasChapters = false; + } + + /** + * This constructor is used by DBReader. + * */ + public FeedItem(long id, String title, String link, Date pubDate, String paymentLink, long feedId, + FlattrStatus flattrStatus, boolean hasChapters, FeedImage image, boolean read, + String itemIdentifier) { + this.id = id; + this.title = title; + this.link = link; + this.pubDate = pubDate; + this.paymentLink = paymentLink; + this.feedId = feedId; + this.flattrStatus = flattrStatus; + this.hasChapters = hasChapters; + this.image = image; + this.read = read; + this.itemIdentifier = itemIdentifier; } /** @@ -64,6 +97,22 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr this.read = read; this.feed = feed; this.flattrStatus = new FlattrStatus(); + this.hasChapters = false; + } + + /** + * This constructor should be used for creating test objects involving chapter marks. + */ + public FeedItem(long id, String title, String itemIdentifier, String link, Date pubDate, boolean read, Feed feed, boolean hasChapters) { + this.id = id; + this.title = title; + this.itemIdentifier = itemIdentifier; + this.link = link; + this.pubDate = (pubDate != null) ? (Date) pubDate.clone() : null; + this.read = read; + this.feed = feed; + this.flattrStatus = new FlattrStatus(); + this.hasChapters = hasChapters; } public void updateFromOther(FeedItem other) { @@ -331,4 +380,8 @@ public class FeedItem extends FeedComponent implements ShownotesProvider, Flattr public String getHumanReadableIdentifier() { return title; } + + public boolean hasChapters() { + return hasChapters; + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java index defcfd598..2434ee0cf 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java +++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java @@ -245,14 +245,19 @@ public class FeedMedia extends FeedFile implements Playable { @Override public void loadChapterMarks() { - if (getChapters() == null && !localFileAvailable()) { + if (item == null && itemID != 0) { + item = DBReader.getFeedItem(ClientConfig.applicationCallbacks.getApplicationInstance(), itemID); + } + // check if chapters are stored in db and not loaded yet. + if (item != null && item.hasChapters() && item.getChapters() == null) { + DBReader.loadChaptersOfFeedItem(ClientConfig.applicationCallbacks.getApplicationInstance(), item); + } else if (item != null && item.getChapters() == null && !localFileAvailable()) { ChapterUtils.loadChaptersFromStreamUrl(this); if (getChapters() != null && item != null) { DBWriter.setFeedItem(ClientConfig.applicationCallbacks.getApplicationInstance(), item); } } - } @Override diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java index 6c0b6df74..217e6fba5 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/DBReader.java @@ -4,8 +4,22 @@ import android.content.Context; import android.database.Cursor; import android.database.SQLException; import android.util.Log; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Date; +import java.util.List; + import de.danoeh.antennapod.core.BuildConfig; -import de.danoeh.antennapod.core.feed.*; +import de.danoeh.antennapod.core.feed.Chapter; +import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.feed.FeedImage; +import de.danoeh.antennapod.core.feed.FeedItem; +import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.feed.FeedPreferences; +import de.danoeh.antennapod.core.feed.ID3Chapter; +import de.danoeh.antennapod.core.feed.SimpleChapter; +import de.danoeh.antennapod.core.feed.VorbisCommentChapter; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.util.DownloadError; import de.danoeh.antennapod.core.util.comparator.DownloadStatusComparator; @@ -14,11 +28,6 @@ import de.danoeh.antennapod.core.util.comparator.PlaybackCompletionDateComparato import de.danoeh.antennapod.core.util.flattr.FlattrStatus; import de.danoeh.antennapod.core.util.flattr.FlattrThing; -import java.util.ArrayList; -import java.util.Collections; -import java.util.Date; -import java.util.List; - /** * Provides methods for reading data from the AntennaPod database. * In general, all database calls in DBReader-methods are executed on the caller's thread. @@ -203,75 +212,26 @@ public final class DBReader { if (itemlistCursor.moveToFirst()) { do { - FeedItem item = new FeedItem(); - - item.setId(itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_ID)); - item.setTitle(itemlistCursor - .getString(PodDBAdapter.IDX_FI_SMALL_TITLE)); - item.setLink(itemlistCursor - .getString(PodDBAdapter.IDX_FI_SMALL_LINK)); - item.setPubDate(new Date(itemlistCursor - .getLong(PodDBAdapter.IDX_FI_SMALL_PUBDATE))); - item.setPaymentLink(itemlistCursor - .getString(PodDBAdapter.IDX_FI_SMALL_PAYMENT_LINK)); - item.setFeedId(itemlistCursor - .getLong(PodDBAdapter.IDX_FI_SMALL_FEED)); - itemIds.add(String.valueOf(item.getId())); - - item.setRead((itemlistCursor - .getInt(PodDBAdapter.IDX_FI_SMALL_READ) > 0)); - item.setItemIdentifier(itemlistCursor - .getString(PodDBAdapter.IDX_FI_SMALL_ITEM_IDENTIFIER)); - item.setFlattrStatus(new FlattrStatus(itemlistCursor - .getLong(PodDBAdapter.IDX_FI_SMALL_FLATTR_STATUS))); - long imageIndex = itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_IMAGE); + FeedImage image = null; if (imageIndex != 0) { - item.setImage(getFeedImage(adapter, imageIndex)); + image = getFeedImage(adapter, imageIndex); } - // extract chapters - boolean hasSimpleChapters = itemlistCursor - .getInt(PodDBAdapter.IDX_FI_SMALL_HAS_CHAPTERS) > 0; - if (hasSimpleChapters) { - Cursor chapterCursor = adapter - .getSimpleChaptersOfFeedItemCursor(item); - if (chapterCursor.moveToFirst()) { - item.setChapters(new ArrayList<Chapter>()); - do { - int chapterType = chapterCursor - .getInt(PodDBAdapter.KEY_CHAPTER_TYPE_INDEX); - Chapter chapter = null; - long start = chapterCursor - .getLong(PodDBAdapter.KEY_CHAPTER_START_INDEX); - String title = chapterCursor - .getString(PodDBAdapter.KEY_TITLE_INDEX); - String link = chapterCursor - .getString(PodDBAdapter.KEY_CHAPTER_LINK_INDEX); - - switch (chapterType) { - case SimpleChapter.CHAPTERTYPE_SIMPLECHAPTER: - chapter = new SimpleChapter(start, title, item, - link); - break; - case ID3Chapter.CHAPTERTYPE_ID3CHAPTER: - chapter = new ID3Chapter(start, title, item, - link); - break; - case VorbisCommentChapter.CHAPTERTYPE_VORBISCOMMENT_CHAPTER: - chapter = new VorbisCommentChapter(start, - title, item, link); - break; - } - if (chapter != null) { - chapter.setId(chapterCursor - .getLong(PodDBAdapter.KEY_ID_INDEX)); - item.getChapters().add(chapter); - } - } while (chapterCursor.moveToNext()); - } - chapterCursor.close(); - } + FeedItem item = new FeedItem(itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_ID), + itemlistCursor.getString(PodDBAdapter.IDX_FI_SMALL_TITLE), + itemlistCursor.getString(PodDBAdapter.IDX_FI_SMALL_LINK), + new Date(itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_PUBDATE)), + itemlistCursor.getString(PodDBAdapter.IDX_FI_SMALL_PAYMENT_LINK), + itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_FEED), + new FlattrStatus(itemlistCursor.getLong(PodDBAdapter.IDX_FI_SMALL_FLATTR_STATUS)), + itemlistCursor.getInt(PodDBAdapter.IDX_FI_SMALL_HAS_CHAPTERS) > 0, + image, + (itemlistCursor.getInt(PodDBAdapter.IDX_FI_SMALL_READ) > 0), + itemlistCursor.getString(PodDBAdapter.IDX_FI_SMALL_ITEM_IDENTIFIER)); + + itemIds.add(String.valueOf(item.getId())); + items.add(item); } while (itemlistCursor.moveToNext()); } @@ -367,6 +327,7 @@ public final class DBReader { return feed; } + private static FeedItem getMatchingItemForMedia(long itemId, List<FeedItem> items) { for (FeedItem item : items) { @@ -689,6 +650,9 @@ public final class DBReader { if (list.size() > 0) { item = list.get(0); loadFeedDataOfFeedItemlist(context, list); + if (item.hasChapters()) { + loadChaptersOfFeedItem(adapter, item); + } } } return item; @@ -696,12 +660,13 @@ public final class DBReader { } /** - * Loads a specific FeedItem from the database. + * Loads a specific FeedItem from the database. This method should not be used for loading more + * than one FeedItem because this method might query the database several times for each item. * * @param context A context that is used for opening a database connection. * @param itemId The ID of the FeedItem - * @return The FeedItem or null if the FeedItem could not be found. All FeedComponent-attributes of the FeedItem will - * also be loaded from the database. + * @return The FeedItem or null if the FeedItem could not be found. All FeedComponent-attributes + * as well as chapter marks of the FeedItem will also be loaded from the database. */ public static FeedItem getFeedItem(final Context context, final long itemId) { if (BuildConfig.DEBUG) @@ -737,6 +702,63 @@ public final class DBReader { } /** + * Loads the list of chapters that belongs to this FeedItem if available. This method overwrites + * any chapters that this FeedItem has. If no chapters were found in the database, the chapters + * reference of the FeedItem will be set to null. + * + * @param context A context that is used for opening a database connection. + * @param item The FeedItem + */ + public static void loadChaptersOfFeedItem(final Context context, final FeedItem item) { + PodDBAdapter adapter = new PodDBAdapter(context); + adapter.open(); + loadChaptersOfFeedItem(adapter, item); + adapter.close(); + } + + static void loadChaptersOfFeedItem(PodDBAdapter adapter, FeedItem item) { + Cursor chapterCursor = adapter + .getSimpleChaptersOfFeedItemCursor(item); + if (chapterCursor.moveToFirst()) { + item.setChapters(new ArrayList<Chapter>()); + do { + int chapterType = chapterCursor + .getInt(PodDBAdapter.KEY_CHAPTER_TYPE_INDEX); + Chapter chapter = null; + long start = chapterCursor + .getLong(PodDBAdapter.KEY_CHAPTER_START_INDEX); + String title = chapterCursor + .getString(PodDBAdapter.KEY_TITLE_INDEX); + String link = chapterCursor + .getString(PodDBAdapter.KEY_CHAPTER_LINK_INDEX); + + switch (chapterType) { + case SimpleChapter.CHAPTERTYPE_SIMPLECHAPTER: + chapter = new SimpleChapter(start, title, item, + link); + break; + case ID3Chapter.CHAPTERTYPE_ID3CHAPTER: + chapter = new ID3Chapter(start, title, item, + link); + break; + case VorbisCommentChapter.CHAPTERTYPE_VORBISCOMMENT_CHAPTER: + chapter = new VorbisCommentChapter(start, + title, item, link); + break; + } + if (chapter != null) { + chapter.setId(chapterCursor + .getLong(PodDBAdapter.KEY_ID_INDEX)); + item.getChapters().add(chapter); + } + } while (chapterCursor.moveToNext()); + } else { + item.setChapters(null); + } + chapterCursor.close(); + } + + /** * Returns the number of downloaded episodes. * * @param context A context that is used for opening a database connection. @@ -788,7 +810,7 @@ public final class DBReader { static FeedImage getFeedImage(PodDBAdapter adapter, final long id) { Cursor cursor = adapter.getImageCursor(id); if ((cursor.getCount() == 0) || !cursor.moveToFirst()) { - throw new SQLException("No FeedImage found at index: " + id); + return null; } FeedImage image = new FeedImage(id, cursor.getString(cursor .getColumnIndex(PodDBAdapter.KEY_TITLE)), diff --git a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java index 79124521f..ce41147e1 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/storage/PodDBAdapter.java @@ -693,7 +693,7 @@ public class PodDBAdapter { } values.put(KEY_FEED, item.getFeed().getId()); values.put(KEY_READ, item.isRead()); - values.put(KEY_HAS_CHAPTERS, item.getChapters() != null); + values.put(KEY_HAS_CHAPTERS, item.getChapters() != null || item.hasChapters()); values.put(KEY_ITEM_IDENTIFIER, item.getItemIdentifier()); values.put(KEY_FLATTR_STATUS, item.getFlattrStatus().toLong()); if (item.hasItemImage()) { @@ -848,7 +848,7 @@ public class PodDBAdapter { if (item.getMedia() != null) { removeFeedMedia(item.getMedia()); } - if (item.getChapters() != null) { + if (item.hasChapters() || item.getChapters() != null) { removeChaptersOfItem(item); } if (item.hasItemImage()) { |