-Added serialized cache for transferring serializable objects too large for intent transactions.

-Fixed potential transaction too large exceptions for player intents.
This commit is contained in:
John Zhen Mo 2018-02-28 17:47:12 -08:00
parent b4668367c6
commit a1220c77da
5 changed files with 141 additions and 23 deletions

View File

@ -65,8 +65,8 @@ import org.schabi.newpipe.player.playback.PlaybackListener;
import org.schabi.newpipe.playlist.PlayQueue;
import org.schabi.newpipe.playlist.PlayQueueAdapter;
import org.schabi.newpipe.playlist.PlayQueueItem;
import org.schabi.newpipe.util.SerializedCache;
import java.io.Serializable;
import java.util.concurrent.TimeUnit;
import io.reactivex.Observable;
@ -106,7 +106,7 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
public static final String PLAYBACK_PITCH = "playback_pitch";
public static final String PLAYBACK_SPEED = "playback_speed";
public static final String PLAYBACK_QUALITY = "playback_quality";
public static final String PLAY_QUEUE = "play_queue";
public static final String PLAY_QUEUE_KEY = "play_queue_key";
public static final String APPEND_ONLY = "append_only";
public static final String SELECT_ON_APPEND = "select_on_append";
@ -207,10 +207,10 @@ public abstract class BasePlayer implements Player.EventListener, PlaybackListen
if (intent == null) return;
// Resolve play queue
if (!intent.hasExtra(PLAY_QUEUE)) return;
final Serializable playQueueCandidate = intent.getSerializableExtra(PLAY_QUEUE);
if (!(playQueueCandidate instanceof PlayQueue)) return;
final PlayQueue queue = (PlayQueue) playQueueCandidate;
if (!intent.hasExtra(PLAY_QUEUE_KEY)) return;
final String intentCacheKey = intent.getStringExtra(PLAY_QUEUE_KEY);
final PlayQueue queue = SerializedCache.getInstance().take(intentCacheKey, PlayQueue.class);
if (queue == null) return;
// Resolve append intents
if (intent.getBooleanExtra(APPEND_ONLY, false) && playQueue != null) {

View File

@ -118,6 +118,7 @@ abstract class AbstractInfoPlayQueue<T extends ListInfo, U extends InfoItem> ext
public void dispose() {
super.dispose();
if (fetchReactor != null) fetchReactor.dispose();
fetchReactor = null;
}
private static List<PlayQueueItem> extractListItems(final List<InfoItem> infos) {

View File

@ -84,6 +84,7 @@ public abstract class PlayQueue implements Serializable {
if (eventBroadcast != null) eventBroadcast.onComplete();
if (reportingReactor != null) reportingReactor.cancel();
eventBroadcast = null;
broadcastReceiver = null;
reportingReactor = null;
}

View File

@ -7,6 +7,8 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.preference.PreferenceManager;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AlertDialog;
@ -33,9 +35,9 @@ import org.schabi.newpipe.fragments.list.feed.FeedFragment;
import org.schabi.newpipe.fragments.list.kiosk.KioskFragment;
import org.schabi.newpipe.fragments.list.playlist.PlaylistFragment;
import org.schabi.newpipe.fragments.list.search.SearchFragment;
import org.schabi.newpipe.fragments.local.bookmark.LastPlayedFragment;
import org.schabi.newpipe.fragments.local.bookmark.LocalPlaylistFragment;
import org.schabi.newpipe.fragments.local.bookmark.MostPlayedFragment;
import org.schabi.newpipe.fragments.local.bookmark.LastPlayedFragment;
import org.schabi.newpipe.history.HistoryActivity;
import org.schabi.newpipe.player.BackgroundPlayer;
import org.schabi.newpipe.player.BackgroundPlayerActivity;
@ -59,39 +61,41 @@ public class NavigationHelper {
// Players
//////////////////////////////////////////////////////////////////////////*/
public static Intent getPlayerIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue,
final String quality) {
Intent intent = new Intent(context, targetClazz)
.putExtra(VideoPlayer.PLAY_QUEUE, playQueue);
public static Intent getPlayerIntent(@NonNull final Context context,
@NonNull final Class targetClazz,
@NonNull final PlayQueue playQueue,
@Nullable final String quality) {
Intent intent = new Intent(context, targetClazz);
final String cacheKey = SerializedCache.getInstance().put(playQueue, PlayQueue.class);
if (cacheKey != null) intent.putExtra(VideoPlayer.PLAY_QUEUE_KEY, cacheKey);
if (quality != null) intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality);
return intent;
}
public static Intent getPlayerIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue) {
public static Intent getPlayerIntent(@NonNull final Context context,
@NonNull final Class targetClazz,
@NonNull final PlayQueue playQueue) {
return getPlayerIntent(context, targetClazz, playQueue, null);
}
public static Intent getPlayerEnqueueIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue,
public static Intent getPlayerEnqueueIntent(@NonNull final Context context,
@NonNull final Class targetClazz,
@NonNull final PlayQueue playQueue,
final boolean selectOnAppend) {
return getPlayerIntent(context, targetClazz, playQueue)
.putExtra(BasePlayer.APPEND_ONLY, true)
.putExtra(BasePlayer.SELECT_ON_APPEND, selectOnAppend);
}
public static Intent getPlayerIntent(final Context context,
final Class targetClazz,
final PlayQueue playQueue,
public static Intent getPlayerIntent(@NonNull final Context context,
@NonNull final Class targetClazz,
@NonNull final PlayQueue playQueue,
final int repeatMode,
final float playbackSpeed,
final float playbackPitch,
final String playbackQuality) {
@Nullable final String playbackQuality) {
return getPlayerIntent(context, targetClazz, playQueue, playbackQuality)
.putExtra(BasePlayer.REPEAT_MODE, repeatMode)
.putExtra(BasePlayer.PLAYBACK_SPEED, playbackSpeed)

View File

@ -0,0 +1,112 @@
package org.schabi.newpipe.util;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.util.LruCache;
import android.util.Log;
import org.schabi.newpipe.MainActivity;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.UUID;
public class SerializedCache {
private static final boolean DEBUG = MainActivity.DEBUG;
private final String TAG = getClass().getSimpleName();
private static final SerializedCache instance = new SerializedCache();
private static final int MAX_ITEMS_ON_CACHE = 5;
private static final LruCache<String, CacheData> lruCache =
new LruCache<>(MAX_ITEMS_ON_CACHE);
private SerializedCache() {
//no instance
}
public static SerializedCache getInstance() {
return instance;
}
@Nullable
public <T> T take(@NonNull final String key, @NonNull final Class<T> type) {
if (DEBUG) Log.d(TAG, "take() called with: key = [" + key + "]");
synchronized (lruCache) {
return lruCache.get(key) != null ? getItem(lruCache.remove(key), type) : null;
}
}
@Nullable
public <T> T get(@NonNull final String key, @NonNull final Class<T> type) {
if (DEBUG) Log.d(TAG, "get() called with: key = [" + key + "]");
synchronized (lruCache) {
final CacheData data = lruCache.get(key);
return data != null ? getItem(data, type) : null;
}
}
@Nullable
public <T extends Serializable> String put(@NonNull T item, @NonNull final Class<T> type) {
final String key = UUID.randomUUID().toString();
return put(key, item, type) ? key : null;
}
public <T extends Serializable> boolean put(@NonNull final String key, @NonNull T item,
@NonNull final Class<T> type) {
if (DEBUG) Log.d(TAG, "put() called with: key = [" + key + "], item = [" + item + "]");
synchronized (lruCache) {
try {
lruCache.put(key, new CacheData<>(clone(item, type), type));
return true;
} catch (final Exception error) {
Log.e(TAG, "Serialization failed for: ", error);
}
}
return false;
}
public void clear() {
if (DEBUG) Log.d(TAG, "clear() called");
synchronized (lruCache) {
lruCache.evictAll();
}
}
public long size() {
synchronized (lruCache) {
return lruCache.size();
}
}
@Nullable
private <T> T getItem(@NonNull final CacheData data, @NonNull final Class<T> type) {
return type.isAssignableFrom(data.type) ? type.cast(data.item) : null;
}
@NonNull
private <T extends Serializable> T clone(@NonNull T item,
@NonNull final Class<T> type) throws Exception {
final ByteArrayOutputStream bytesOutput = new ByteArrayOutputStream();
try (final ObjectOutputStream objectOutput = new ObjectOutputStream(bytesOutput)) {
objectOutput.writeObject(item);
objectOutput.flush();
}
final Object clone = new ObjectInputStream(
new ByteArrayInputStream(bytesOutput.toByteArray())).readObject();
return type.cast(clone);
}
final private static class CacheData<T> {
private final T item;
private final Class<T> type;
private CacheData(@NonNull final T item, @NonNull Class<T> type) {
this.item = item;
this.type = type;
}
}
}