Merge pull request #3097 from ByteHamster/exoplayer-main-thread
Executing all ExoPlayer methods on main thread
This commit is contained in:
commit
97d08f3b00
|
@ -776,15 +776,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
|
|||
loadData();
|
||||
}
|
||||
|
||||
public void onEventMainThread(ServiceEvent event) {
|
||||
Log.d(TAG, "onEvent(" + event + ")");
|
||||
switch(event.action) {
|
||||
case SERVICE_STARTED:
|
||||
externalPlayerFragment.connectToPlaybackService();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public void onEventMainThread(ProgressEvent event) {
|
||||
Log.d(TAG, "onEvent(" + event + ")");
|
||||
switch(event.action) {
|
||||
|
|
|
@ -18,11 +18,13 @@ import com.bumptech.glide.Glide;
|
|||
|
||||
import com.bumptech.glide.request.RequestOptions;
|
||||
import de.danoeh.antennapod.R;
|
||||
import de.danoeh.antennapod.core.event.ServiceEvent;
|
||||
import de.danoeh.antennapod.core.feed.MediaType;
|
||||
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||
import de.danoeh.antennapod.core.util.playback.Playable;
|
||||
import de.danoeh.antennapod.core.util.playback.PlaybackController;
|
||||
import de.greenrobot.event.EventBus;
|
||||
import io.reactivex.Maybe;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.disposables.Disposable;
|
||||
|
@ -90,8 +92,11 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
loadMediaInfo();
|
||||
}
|
||||
|
||||
public void connectToPlaybackService() {
|
||||
controller.init();
|
||||
public void onEventMainThread(ServiceEvent event) {
|
||||
Log.d(TAG, "onEvent(" + event + ")");
|
||||
if (event.action == ServiceEvent.Action.SERVICE_STARTED) {
|
||||
controller.init();
|
||||
}
|
||||
}
|
||||
|
||||
private PlaybackController setupPlaybackController() {
|
||||
|
@ -109,12 +114,12 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
|
||||
@Override
|
||||
public boolean loadMediaInfo() {
|
||||
ExternalPlayerFragment fragment = ExternalPlayerFragment.this;
|
||||
if (fragment != null) {
|
||||
return fragment.loadMediaInfo();
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return ExternalPlayerFragment.this.loadMediaInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setupGUI() {
|
||||
ExternalPlayerFragment.this.loadMediaInfo();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -133,17 +138,28 @@ public class ExternalPlayerFragment extends Fragment {
|
|||
public void onResume() {
|
||||
super.onResume();
|
||||
onPositionObserverUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
controller.init();
|
||||
EventBus.getDefault().register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
super.onStop();
|
||||
if (controller != null) {
|
||||
controller.release();
|
||||
}
|
||||
EventBus.getDefault().unregister(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
super.onDestroy();
|
||||
Log.d(TAG, "Fragment is about to be destroyed");
|
||||
if (controller != null) {
|
||||
controller.release();
|
||||
}
|
||||
if (disposable != null) {
|
||||
disposable.dispose();
|
||||
}
|
||||
|
|
|
@ -165,6 +165,7 @@ public class ExoPlayerWrapper implements IPlayer {
|
|||
@Override
|
||||
public void seekTo(int i) throws IllegalStateException {
|
||||
mExoPlayer.seekTo(i);
|
||||
audioSeekCompleteListener.onSeekComplete(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -18,6 +18,7 @@ import java.io.File;
|
|||
import java.io.IOException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.concurrent.LinkedBlockingDeque;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
@ -57,16 +58,42 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
private final ReentrantLock playerLock;
|
||||
private CountDownLatch seekLatch;
|
||||
|
||||
private final ThreadPoolExecutor executor;
|
||||
private final PlayerExecutor executor;
|
||||
|
||||
/**
|
||||
* All ExoPlayer methods must be executed on the same thread.
|
||||
* We use the main application thread. This class allows to
|
||||
* "fake" an executor that just calls the methods on the
|
||||
* calling thread instead of submitting to an executor.
|
||||
* Other players are still executed in a background thread.
|
||||
*/
|
||||
private class PlayerExecutor {
|
||||
private boolean useCallerThread = true;
|
||||
private ThreadPoolExecutor threadPool;
|
||||
|
||||
public Future<?> submit(Runnable r) {
|
||||
if (useCallerThread) {
|
||||
r.run();
|
||||
return new FutureTask<Void>(() -> {}, null);
|
||||
} else {
|
||||
return threadPool.submit(r);
|
||||
}
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
threadPool.shutdown();
|
||||
}
|
||||
}
|
||||
|
||||
public LocalPSMP(@NonNull Context context,
|
||||
@NonNull PSMPCallback callback) {
|
||||
super(context, callback);
|
||||
|
||||
this.audioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
this.playerLock = new ReentrantLock();
|
||||
this.startWhenPrepared = new AtomicBoolean(false);
|
||||
executor = new ThreadPoolExecutor(1, 1, 5, TimeUnit.MINUTES, new LinkedBlockingDeque<>(),
|
||||
|
||||
executor = new PlayerExecutor();
|
||||
executor.threadPool = new ThreadPoolExecutor(1, 1, 5, TimeUnit.MINUTES, new LinkedBlockingDeque<>(),
|
||||
(r, executor) -> Log.d(TAG, "Rejected execution of runnable"));
|
||||
|
||||
mediaPlayer = null;
|
||||
|
@ -105,6 +132,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
@Override
|
||||
public void playMediaObject(@NonNull final Playable playable, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
|
||||
Log.d(TAG, "playMediaObject(...)");
|
||||
executor.useCallerThread = UserPreferences.useExoplayer();
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
try {
|
||||
|
@ -372,6 +400,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
*/
|
||||
@Override
|
||||
public void reinit() {
|
||||
executor.useCallerThread = UserPreferences.useExoplayer();
|
||||
executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
Log.d(TAG, "reinit()");
|
||||
|
@ -821,6 +850,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
@Override
|
||||
protected Future<?> endPlayback(final boolean hasEnded, final boolean wasSkipped,
|
||||
final boolean shouldContinue, final boolean toStoppedState) {
|
||||
executor.useCallerThread = UserPreferences.useExoplayer();
|
||||
return executor.submit(() -> {
|
||||
playerLock.lock();
|
||||
releaseWifiLockIfNecessary();
|
||||
|
@ -1012,7 +1042,7 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
mp -> genericSeekCompleteListener();
|
||||
|
||||
private void genericSeekCompleteListener() {
|
||||
Thread t = new Thread(() -> {
|
||||
Runnable r = () -> {
|
||||
Log.d(TAG, "genericSeekCompleteListener");
|
||||
if(seekLatch != null) {
|
||||
seekLatch.countDown();
|
||||
|
@ -1025,7 +1055,12 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||
setPlayerStatus(statusBeforeSeeking, media, getPosition());
|
||||
}
|
||||
playerLock.unlock();
|
||||
});
|
||||
t.start();
|
||||
};
|
||||
|
||||
if (executor.useCallerThread) {
|
||||
r.run();
|
||||
} else {
|
||||
new Thread(r).start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package de.danoeh.antennapod.core.service.playback;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.os.Vibrator;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.util.Log;
|
||||
|
@ -126,6 +128,7 @@ public class PlaybackServiceTaskManager {
|
|||
public synchronized void startPositionSaver() {
|
||||
if (!isPositionSaverActive()) {
|
||||
Runnable positionSaver = callback::positionSaverTick;
|
||||
positionSaver = useMainThreadIfNecessary(positionSaver);
|
||||
positionSaverFuture = schedExecutor.scheduleWithFixedDelay(positionSaver, POSITION_SAVER_WAITING_INTERVAL,
|
||||
POSITION_SAVER_WAITING_INTERVAL, TimeUnit.MILLISECONDS);
|
||||
|
||||
|
@ -158,6 +161,7 @@ public class PlaybackServiceTaskManager {
|
|||
public synchronized void startWidgetUpdater() {
|
||||
if (!isWidgetUpdaterActive()) {
|
||||
Runnable widgetUpdater = callback::onWidgetUpdaterTick;
|
||||
widgetUpdater = useMainThreadIfNecessary(widgetUpdater);
|
||||
widgetUpdaterFuture = schedExecutor.scheduleWithFixedDelay(widgetUpdater, WIDGET_UPDATER_NOTIFICATION_INTERVAL,
|
||||
WIDGET_UPDATER_NOTIFICATION_INTERVAL, TimeUnit.MILLISECONDS);
|
||||
|
||||
|
@ -184,7 +188,8 @@ public class PlaybackServiceTaskManager {
|
|||
sleepTimerFuture.cancel(true);
|
||||
}
|
||||
sleepTimer = new SleepTimer(waitingTime, shakeToReset, vibrate);
|
||||
sleepTimerFuture = schedExecutor.schedule(sleepTimer, 0, TimeUnit.MILLISECONDS);
|
||||
Runnable runnable = useMainThreadIfNecessary(sleepTimer);
|
||||
sleepTimerFuture = schedExecutor.schedule(runnable, 0, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -267,6 +272,7 @@ public class PlaybackServiceTaskManager {
|
|||
}
|
||||
Log.d(TAG, "Chapter loader stopped");
|
||||
};
|
||||
chapterLoader = useMainThreadIfNecessary(chapterLoader);
|
||||
chapterLoaderFuture = schedExecutor.submit(chapterLoader);
|
||||
}
|
||||
|
||||
|
@ -292,6 +298,17 @@ public class PlaybackServiceTaskManager {
|
|||
schedExecutor.shutdown();
|
||||
}
|
||||
|
||||
private Runnable useMainThreadIfNecessary(Runnable runnable) {
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
// Called in main thread => ExoPlayer is used
|
||||
// Run on ui thread even if called from schedExecutor
|
||||
Handler handler = new Handler();
|
||||
return () -> handler.post(runnable);
|
||||
} else {
|
||||
return runnable;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sleeps for a given time and then pauses playback.
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue