From 8e6ae343008da566e04f318c8695e408ac28db06 Mon Sep 17 00:00:00 2001 From: Yossi Elkrief Date: Wed, 15 Jul 2015 13:31:39 +0300 Subject: [PATCH 1/4] Handle not sending messages to dead thread. Fix issue https://github.com/google/ExoPlayer/issues/426 --- .../google/android/exoplayer/upstream/Loader.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java b/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java index 6eb477fd0df..e7958e6cc07 100644 --- a/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java +++ b/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java @@ -241,13 +241,13 @@ public void run() { if (!loadable.isLoadCanceled()) { loadable.load(); } - sendEmptyMessage(MSG_END_OF_SOURCE); + passEmptyMessage(MSG_END_OF_SOURCE); } catch (IOException e) { obtainMessage(MSG_IO_EXCEPTION, e).sendToTarget(); } catch (InterruptedException e) { // The load was canceled. Assertions.checkState(loadable.isLoadCanceled()); - sendEmptyMessage(MSG_END_OF_SOURCE); + passEmptyMessage(MSG_END_OF_SOURCE); } catch (Exception e) { // This should never happen, but handle it anyway. Log.e(TAG, "Unexpected exception loading stream", e); @@ -262,6 +262,14 @@ public void run() { } } +private void passEmptyMessage(int what){ + if (getLooper().getThread().getState()!=Thread.State.TERMINATED) + { + sendEmptyMessage(what); + } +} + + @Override public void handleMessage(Message msg) { if (msg.what == MSG_FATAL_ERROR) { From ebc734157a9be6bd151c5bb03a84e458b0ec6e62 Mon Sep 17 00:00:00 2001 From: Yossi Elkrief Date: Wed, 15 Jul 2015 13:35:06 +0300 Subject: [PATCH 2/4] Apply code formatting --- .../android/exoplayer/upstream/Loader.java | 488 ++++++++++-------- 1 file changed, 261 insertions(+), 227 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java b/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java index e7958e6cc07..61dae6bffbc 100644 --- a/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java +++ b/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java @@ -15,286 +15,320 @@ */ package com.google.android.exoplayer.upstream; -import com.google.android.exoplayer.util.Assertions; -import com.google.android.exoplayer.util.Util; - import android.annotation.SuppressLint; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; +import com.google.android.exoplayer.util.Assertions; +import com.google.android.exoplayer.util.Util; + import java.io.IOException; import java.util.concurrent.ExecutorService; /** * Manages the background loading of {@link Loadable}s. */ -public final class Loader { +public final class Loader +{ - /** - * Thrown when an unexpected exception is encountered during loading. - */ - public static final class UnexpectedLoaderException extends IOException { + private static final int MSG_END_OF_SOURCE = 0; + private static final int MSG_IO_EXCEPTION = 1; + private static final int MSG_FATAL_ERROR = 2; + private final ExecutorService downloadExecutorService; + private LoadTask currentTask; + private boolean loading; - public UnexpectedLoaderException(Exception cause) { - super("Unexpected " + cause.getClass().getSimpleName() + ": " + cause.getMessage(), cause); + /** + * @param threadName A name for the loader's thread. + */ + public Loader(String threadName) + { + this.downloadExecutorService = Util.newSingleThreadExecutor(threadName); } - } - - /** - * Interface definition of an object that can be loaded using a {@link Loader}. - */ - public interface Loadable { - /** - * Cancels the load. + * Invokes {@link #startLoading(Looper, Loadable, Callback)}, using the {@link Looper} + * associated with the calling thread. + * + * @param loadable The {@link Loadable} to load. + * @param callback A callback to invoke when the load ends. + * @throws IllegalStateException If the calling thread does not have an associated {@link Looper}. */ - void cancelLoad(); + public void startLoading(Loadable loadable, Callback callback) + { + Looper myLooper = Looper.myLooper(); + Assertions.checkState(myLooper != null); + startLoading(myLooper, loadable, callback); + } /** - * Whether the load has been canceled. + * Invokes {@link #startLoading(Looper, Loadable, Callback)}, using the {@link Looper} + * associated with the calling thread. Loading is delayed by {@code delayMs}. * - * @return True if the load has been canceled. False otherwise. + * @param loadable The {@link Loadable} to load. + * @param callback A callback to invoke when the load ends. + * @param delayMs Number of milliseconds to wait before calling {@link Loadable#load()}. + * @throws IllegalStateException If the calling thread does not have an associated {@link Looper}. */ - boolean isLoadCanceled(); + public void startLoading(Loadable loadable, Callback callback, int delayMs) + { + Looper myLooper = Looper.myLooper(); + Assertions.checkState(myLooper != null); + startLoading(myLooper, loadable, callback, delayMs); + } /** - * Performs the load, returning on completion or cancelation. + * Start loading a {@link Loadable}. + *

+ * A {@link Loader} instance can only load one {@link Loadable} at a time, and so this method + * must not be called when another load is in progress. * - * @throws IOException - * @throws InterruptedException + * @param looper The looper of the thread on which the callback should be invoked. + * @param loadable The {@link Loadable} to load. + * @param callback A callback to invoke when the load ends. */ - void load() throws IOException, InterruptedException; - - } - - /** - * Interface definition for a callback to be notified of {@link Loader} events. - */ - public interface Callback { + public void startLoading(Looper looper, Loadable loadable, Callback callback) + { + startLoading(looper, loadable, callback, 0); + } /** - * Invoked when loading has been canceled. + * Start loading a {@link Loadable} after {@code delayMs} has elapsed. + *

+ * A {@link Loader} instance can only load one {@link Loadable} at a time, and so this method + * must not be called when another load is in progress. * - * @param loadable The loadable whose load has been canceled. + * @param looper The looper of the thread on which the callback should be invoked. + * @param loadable The {@link Loadable} to load. + * @param callback A callback to invoke when the load ends. + * @param delayMs Number of milliseconds to wait before calling {@link Loadable#load()}. */ - void onLoadCanceled(Loadable loadable); + public void startLoading(Looper looper, Loadable loadable, Callback callback, int delayMs) + { + Assertions.checkState(!loading); + loading = true; + currentTask = new LoadTask(looper, loadable, callback, delayMs); + downloadExecutorService.submit(currentTask); + } /** - * Invoked when the data source has been fully loaded. + * Whether the {@link Loader} is currently loading a {@link Loadable}. * - * @param loadable The loadable whose load has completed. + * @return Whether the {@link Loader} is currently loading a {@link Loadable}. */ - void onLoadCompleted(Loadable loadable); + public boolean isLoading() + { + return loading; + } /** - * Invoked when the data source is stopped due to an error. - * - * @param loadable The loadable whose load has failed. + * Cancels the current load. + *

+ * This method should only be called when a load is in progress. */ - void onLoadError(Loadable loadable, IOException exception); - - } - - private static final int MSG_END_OF_SOURCE = 0; - private static final int MSG_IO_EXCEPTION = 1; - private static final int MSG_FATAL_ERROR = 2; - - private final ExecutorService downloadExecutorService; - - private LoadTask currentTask; - private boolean loading; - - /** - * @param threadName A name for the loader's thread. - */ - public Loader(String threadName) { - this.downloadExecutorService = Util.newSingleThreadExecutor(threadName); - } - - /** - * Invokes {@link #startLoading(Looper, Loadable, Callback)}, using the {@link Looper} - * associated with the calling thread. - * - * @param loadable The {@link Loadable} to load. - * @param callback A callback to invoke when the load ends. - * @throws IllegalStateException If the calling thread does not have an associated {@link Looper}. - */ - public void startLoading(Loadable loadable, Callback callback) { - Looper myLooper = Looper.myLooper(); - Assertions.checkState(myLooper != null); - startLoading(myLooper, loadable, callback); - } - - /** - * Invokes {@link #startLoading(Looper, Loadable, Callback)}, using the {@link Looper} - * associated with the calling thread. Loading is delayed by {@code delayMs}. - * - * @param loadable The {@link Loadable} to load. - * @param callback A callback to invoke when the load ends. - * @param delayMs Number of milliseconds to wait before calling {@link Loadable#load()}. - * @throws IllegalStateException If the calling thread does not have an associated {@link Looper}. - */ - public void startLoading(Loadable loadable, Callback callback, int delayMs) { - Looper myLooper = Looper.myLooper(); - Assertions.checkState(myLooper != null); - startLoading(myLooper, loadable, callback, delayMs); - } - - /** - * Start loading a {@link Loadable}. - *

- * A {@link Loader} instance can only load one {@link Loadable} at a time, and so this method - * must not be called when another load is in progress. - * - * @param looper The looper of the thread on which the callback should be invoked. - * @param loadable The {@link Loadable} to load. - * @param callback A callback to invoke when the load ends. - */ - public void startLoading(Looper looper, Loadable loadable, Callback callback) { - startLoading(looper, loadable, callback, 0); - } - - /** - * Start loading a {@link Loadable} after {@code delayMs} has elapsed. - *

- * A {@link Loader} instance can only load one {@link Loadable} at a time, and so this method - * must not be called when another load is in progress. - * - * @param looper The looper of the thread on which the callback should be invoked. - * @param loadable The {@link Loadable} to load. - * @param callback A callback to invoke when the load ends. - * @param delayMs Number of milliseconds to wait before calling {@link Loadable#load()}. - */ - public void startLoading(Looper looper, Loadable loadable, Callback callback, int delayMs) { - Assertions.checkState(!loading); - loading = true; - currentTask = new LoadTask(looper, loadable, callback, delayMs); - downloadExecutorService.submit(currentTask); - } - - /** - * Whether the {@link Loader} is currently loading a {@link Loadable}. - * - * @return Whether the {@link Loader} is currently loading a {@link Loadable}. - */ - public boolean isLoading() { - return loading; - } - - /** - * Cancels the current load. - *

- * This method should only be called when a load is in progress. - */ - public void cancelLoading() { - Assertions.checkState(loading); - currentTask.quit(); - } - - /** - * Releases the {@link Loader}. - *

- * This method should be called when the {@link Loader} is no longer required. - */ - public void release() { - if (loading) { - cancelLoading(); + public void cancelLoading() + { + Assertions.checkState(loading); + currentTask.quit(); } - downloadExecutorService.shutdown(); - } - @SuppressLint("HandlerLeak") - private final class LoadTask extends Handler implements Runnable { + /** + * Releases the {@link Loader}. + *

+ * This method should be called when the {@link Loader} is no longer required. + */ + public void release() + { + if (loading) + { + cancelLoading(); + } + downloadExecutorService.shutdown(); + } - private static final String TAG = "LoadTask"; + /** + * Interface definition of an object that can be loaded using a {@link Loader}. + */ + public interface Loadable + { + + /** + * Cancels the load. + */ + void cancelLoad(); + + /** + * Whether the load has been canceled. + * + * @return True if the load has been canceled. False otherwise. + */ + boolean isLoadCanceled(); + + /** + * Performs the load, returning on completion or cancelation. + * + * @throws IOException + * @throws InterruptedException + */ + void load() throws IOException, InterruptedException; - private final Loadable loadable; - private final Loader.Callback callback; - private final int delayMs; + } - private volatile Thread executorThread; + /** + * Interface definition for a callback to be notified of {@link Loader} events. + */ + public interface Callback + { + + /** + * Invoked when loading has been canceled. + * + * @param loadable The loadable whose load has been canceled. + */ + void onLoadCanceled(Loadable loadable); + + /** + * Invoked when the data source has been fully loaded. + * + * @param loadable The loadable whose load has completed. + */ + void onLoadCompleted(Loadable loadable); + + /** + * Invoked when the data source is stopped due to an error. + * + * @param loadable The loadable whose load has failed. + */ + void onLoadError(Loadable loadable, IOException exception); - public LoadTask(Looper looper, Loadable loadable, Loader.Callback callback, int delayMs) { - super(looper); - this.loadable = loadable; - this.callback = callback; - this.delayMs = delayMs; } - public void quit() { - loadable.cancelLoad(); - if (executorThread != null) { - executorThread.interrupt(); - } + /** + * Thrown when an unexpected exception is encountered during loading. + */ + public static final class UnexpectedLoaderException extends IOException + { + + public UnexpectedLoaderException(Exception cause) + { + super("Unexpected " + cause.getClass().getSimpleName() + ": " + cause.getMessage(), cause); + } + } - @Override - public void run() { - try { - executorThread = Thread.currentThread(); - if (delayMs > 0) { - Thread.sleep(delayMs); + @SuppressLint("HandlerLeak") + private final class LoadTask extends Handler implements Runnable + { + + private static final String TAG = "LoadTask"; + + private final Loadable loadable; + private final Loader.Callback callback; + private final int delayMs; + + private volatile Thread executorThread; + + public LoadTask(Looper looper, Loadable loadable, Loader.Callback callback, int delayMs) + { + super(looper); + this.loadable = loadable; + this.callback = callback; + this.delayMs = delayMs; } - if (!loadable.isLoadCanceled()) { - loadable.load(); + + public void quit() + { + loadable.cancelLoad(); + if (executorThread != null) + { + executorThread.interrupt(); + } } - passEmptyMessage(MSG_END_OF_SOURCE); - } catch (IOException e) { - obtainMessage(MSG_IO_EXCEPTION, e).sendToTarget(); - } catch (InterruptedException e) { - // The load was canceled. - Assertions.checkState(loadable.isLoadCanceled()); - passEmptyMessage(MSG_END_OF_SOURCE); - } catch (Exception e) { - // This should never happen, but handle it anyway. - Log.e(TAG, "Unexpected exception loading stream", e); - obtainMessage(MSG_IO_EXCEPTION, new UnexpectedLoaderException(e)).sendToTarget(); - } catch (Error e) { - // We'd hope that the platform would kill the process if an Error is thrown here, but the - // executor may catch the error (b/20616433). Throw it here, but also pass and throw it from - // the handler thread so that the process dies even if the executor behaves in this way. - Log.e(TAG, "Unexpected error loading stream", e); - obtainMessage(MSG_FATAL_ERROR, e).sendToTarget(); - throw e; - } - } -private void passEmptyMessage(int what){ - if (getLooper().getThread().getState()!=Thread.State.TERMINATED) - { - sendEmptyMessage(what); - } -} + @Override + public void run() + { + try + { + executorThread = Thread.currentThread(); + if (delayMs > 0) + { + Thread.sleep(delayMs); + } + if (!loadable.isLoadCanceled()) + { + loadable.load(); + } + passEmptyMessage(MSG_END_OF_SOURCE); + } + catch (IOException e) + { + obtainMessage(MSG_IO_EXCEPTION, e).sendToTarget(); + } + catch (InterruptedException e) + { + // The load was canceled. + Assertions.checkState(loadable.isLoadCanceled()); + passEmptyMessage(MSG_END_OF_SOURCE); + } + catch (Exception e) + { + // This should never happen, but handle it anyway. + Log.e(TAG, "Unexpected exception loading stream", e); + obtainMessage(MSG_IO_EXCEPTION, new UnexpectedLoaderException(e)).sendToTarget(); + } + catch (Error e) + { + // We'd hope that the platform would kill the process if an Error is thrown here, but the + // executor may catch the error (b/20616433). Throw it here, but also pass and throw it from + // the handler thread so that the process dies even if the executor behaves in this way. + Log.e(TAG, "Unexpected error loading stream", e); + obtainMessage(MSG_FATAL_ERROR, e).sendToTarget(); + throw e; + } + } + + private void passEmptyMessage(int what) + { + if (getLooper().getThread().getState() != Thread.State.TERMINATED) + { + sendEmptyMessage(what); + } + } - @Override - public void handleMessage(Message msg) { - if (msg.what == MSG_FATAL_ERROR) { - throw (Error) msg.obj; - } - onFinished(); - if (loadable.isLoadCanceled()) { - callback.onLoadCanceled(loadable); - return; - } - switch (msg.what) { - case MSG_END_OF_SOURCE: - callback.onLoadCompleted(loadable); - break; - case MSG_IO_EXCEPTION: - callback.onLoadError(loadable, (IOException) msg.obj); - break; - } - } + @Override + public void handleMessage(Message msg) + { + if (msg.what == MSG_FATAL_ERROR) + { + throw (Error) msg.obj; + } + onFinished(); + if (loadable.isLoadCanceled()) + { + callback.onLoadCanceled(loadable); + return; + } + switch (msg.what) + { + case MSG_END_OF_SOURCE: + callback.onLoadCompleted(loadable); + break; + case MSG_IO_EXCEPTION: + callback.onLoadError(loadable, (IOException) msg.obj); + break; + } + } - private void onFinished() { - loading = false; - currentTask = null; - } + private void onFinished() + { + loading = false; + currentTask = null; + } - } + } } From 892d99d069fba40117bcbff79eb6505c8c0fc1da Mon Sep 17 00:00:00 2001 From: Yossi Elkrief Date: Wed, 15 Jul 2015 19:09:30 +0300 Subject: [PATCH 3/4] Update Loader.java revert code formatting. --- .../android/exoplayer/upstream/Loader.java | 504 +++++++++--------- 1 file changed, 240 insertions(+), 264 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java b/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java index 61dae6bffbc..022c97a389c 100644 --- a/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java +++ b/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java @@ -15,320 +15,296 @@ */ package com.google.android.exoplayer.upstream; +import com.google.android.exoplayer.util.Assertions; +import com.google.android.exoplayer.util.Util; + import android.annotation.SuppressLint; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.util.Log; -import com.google.android.exoplayer.util.Assertions; -import com.google.android.exoplayer.util.Util; - import java.io.IOException; import java.util.concurrent.ExecutorService; /** * Manages the background loading of {@link Loadable}s. */ -public final class Loader -{ +public final class Loader { - private static final int MSG_END_OF_SOURCE = 0; - private static final int MSG_IO_EXCEPTION = 1; - private static final int MSG_FATAL_ERROR = 2; - private final ExecutorService downloadExecutorService; - private LoadTask currentTask; - private boolean loading; + /** + * Thrown when an unexpected exception is encountered during loading. + */ + public static final class UnexpectedLoaderException extends IOException { - /** - * @param threadName A name for the loader's thread. - */ - public Loader(String threadName) - { - this.downloadExecutorService = Util.newSingleThreadExecutor(threadName); + public UnexpectedLoaderException(Exception cause) { + super("Unexpected " + cause.getClass().getSimpleName() + ": " + cause.getMessage(), cause); } + } + + /** + * Interface definition of an object that can be loaded using a {@link Loader}. + */ + public interface Loadable { + /** - * Invokes {@link #startLoading(Looper, Loadable, Callback)}, using the {@link Looper} - * associated with the calling thread. - * - * @param loadable The {@link Loadable} to load. - * @param callback A callback to invoke when the load ends. - * @throws IllegalStateException If the calling thread does not have an associated {@link Looper}. + * Cancels the load. */ - public void startLoading(Loadable loadable, Callback callback) - { - Looper myLooper = Looper.myLooper(); - Assertions.checkState(myLooper != null); - startLoading(myLooper, loadable, callback); - } + void cancelLoad(); /** - * Invokes {@link #startLoading(Looper, Loadable, Callback)}, using the {@link Looper} - * associated with the calling thread. Loading is delayed by {@code delayMs}. + * Whether the load has been canceled. * - * @param loadable The {@link Loadable} to load. - * @param callback A callback to invoke when the load ends. - * @param delayMs Number of milliseconds to wait before calling {@link Loadable#load()}. - * @throws IllegalStateException If the calling thread does not have an associated {@link Looper}. + * @return True if the load has been canceled. False otherwise. */ - public void startLoading(Loadable loadable, Callback callback, int delayMs) - { - Looper myLooper = Looper.myLooper(); - Assertions.checkState(myLooper != null); - startLoading(myLooper, loadable, callback, delayMs); - } + boolean isLoadCanceled(); /** - * Start loading a {@link Loadable}. - *

- * A {@link Loader} instance can only load one {@link Loadable} at a time, and so this method - * must not be called when another load is in progress. + * Performs the load, returning on completion or cancelation. * - * @param looper The looper of the thread on which the callback should be invoked. - * @param loadable The {@link Loadable} to load. - * @param callback A callback to invoke when the load ends. + * @throws IOException + * @throws InterruptedException */ - public void startLoading(Looper looper, Loadable loadable, Callback callback) - { - startLoading(looper, loadable, callback, 0); - } + void load() throws IOException, InterruptedException; + + } + + /** + * Interface definition for a callback to be notified of {@link Loader} events. + */ + public interface Callback { /** - * Start loading a {@link Loadable} after {@code delayMs} has elapsed. - *

- * A {@link Loader} instance can only load one {@link Loadable} at a time, and so this method - * must not be called when another load is in progress. + * Invoked when loading has been canceled. * - * @param looper The looper of the thread on which the callback should be invoked. - * @param loadable The {@link Loadable} to load. - * @param callback A callback to invoke when the load ends. - * @param delayMs Number of milliseconds to wait before calling {@link Loadable#load()}. + * @param loadable The loadable whose load has been canceled. */ - public void startLoading(Looper looper, Loadable loadable, Callback callback, int delayMs) - { - Assertions.checkState(!loading); - loading = true; - currentTask = new LoadTask(looper, loadable, callback, delayMs); - downloadExecutorService.submit(currentTask); - } + void onLoadCanceled(Loadable loadable); /** - * Whether the {@link Loader} is currently loading a {@link Loadable}. + * Invoked when the data source has been fully loaded. * - * @return Whether the {@link Loader} is currently loading a {@link Loadable}. + * @param loadable The loadable whose load has completed. */ - public boolean isLoading() - { - return loading; - } + void onLoadCompleted(Loadable loadable); /** - * Cancels the current load. - *

- * This method should only be called when a load is in progress. + * Invoked when the data source is stopped due to an error. + * + * @param loadable The loadable whose load has failed. */ - public void cancelLoading() - { - Assertions.checkState(loading); - currentTask.quit(); + void onLoadError(Loadable loadable, IOException exception); + + } + + private static final int MSG_END_OF_SOURCE = 0; + private static final int MSG_IO_EXCEPTION = 1; + private static final int MSG_FATAL_ERROR = 2; + + private final ExecutorService downloadExecutorService; + + private LoadTask currentTask; + private boolean loading; + + /** + * @param threadName A name for the loader's thread. + */ + public Loader(String threadName) { + this.downloadExecutorService = Util.newSingleThreadExecutor(threadName); + } + + /** + * Invokes {@link #startLoading(Looper, Loadable, Callback)}, using the {@link Looper} + * associated with the calling thread. + * + * @param loadable The {@link Loadable} to load. + * @param callback A callback to invoke when the load ends. + * @throws IllegalStateException If the calling thread does not have an associated {@link Looper}. + */ + public void startLoading(Loadable loadable, Callback callback) { + Looper myLooper = Looper.myLooper(); + Assertions.checkState(myLooper != null); + startLoading(myLooper, loadable, callback); + } + + /** + * Invokes {@link #startLoading(Looper, Loadable, Callback)}, using the {@link Looper} + * associated with the calling thread. Loading is delayed by {@code delayMs}. + * + * @param loadable The {@link Loadable} to load. + * @param callback A callback to invoke when the load ends. + * @param delayMs Number of milliseconds to wait before calling {@link Loadable#load()}. + * @throws IllegalStateException If the calling thread does not have an associated {@link Looper}. + */ + public void startLoading(Loadable loadable, Callback callback, int delayMs) { + Looper myLooper = Looper.myLooper(); + Assertions.checkState(myLooper != null); + startLoading(myLooper, loadable, callback, delayMs); + } + + /** + * Start loading a {@link Loadable}. + *

+ * A {@link Loader} instance can only load one {@link Loadable} at a time, and so this method + * must not be called when another load is in progress. + * + * @param looper The looper of the thread on which the callback should be invoked. + * @param loadable The {@link Loadable} to load. + * @param callback A callback to invoke when the load ends. + */ + public void startLoading(Looper looper, Loadable loadable, Callback callback) { + startLoading(looper, loadable, callback, 0); + } + + /** + * Start loading a {@link Loadable} after {@code delayMs} has elapsed. + *

+ * A {@link Loader} instance can only load one {@link Loadable} at a time, and so this method + * must not be called when another load is in progress. + * + * @param looper The looper of the thread on which the callback should be invoked. + * @param loadable The {@link Loadable} to load. + * @param callback A callback to invoke when the load ends. + * @param delayMs Number of milliseconds to wait before calling {@link Loadable#load()}. + */ + public void startLoading(Looper looper, Loadable loadable, Callback callback, int delayMs) { + Assertions.checkState(!loading); + loading = true; + currentTask = new LoadTask(looper, loadable, callback, delayMs); + downloadExecutorService.submit(currentTask); + } + + /** + * Whether the {@link Loader} is currently loading a {@link Loadable}. + * + * @return Whether the {@link Loader} is currently loading a {@link Loadable}. + */ + public boolean isLoading() { + return loading; + } + + /** + * Cancels the current load. + *

+ * This method should only be called when a load is in progress. + */ + public void cancelLoading() { + Assertions.checkState(loading); + currentTask.quit(); + } + + /** + * Releases the {@link Loader}. + *

+ * This method should be called when the {@link Loader} is no longer required. + */ + public void release() { + if (loading) { + cancelLoading(); } + downloadExecutorService.shutdown(); + } - /** - * Releases the {@link Loader}. - *

- * This method should be called when the {@link Loader} is no longer required. - */ - public void release() - { - if (loading) - { - cancelLoading(); - } - downloadExecutorService.shutdown(); - } + @SuppressLint("HandlerLeak") + private final class LoadTask extends Handler implements Runnable { - /** - * Interface definition of an object that can be loaded using a {@link Loader}. - */ - public interface Loadable - { - - /** - * Cancels the load. - */ - void cancelLoad(); - - /** - * Whether the load has been canceled. - * - * @return True if the load has been canceled. False otherwise. - */ - boolean isLoadCanceled(); - - /** - * Performs the load, returning on completion or cancelation. - * - * @throws IOException - * @throws InterruptedException - */ - void load() throws IOException, InterruptedException; + private static final String TAG = "LoadTask"; - } + private final Loadable loadable; + private final Loader.Callback callback; + private final int delayMs; - /** - * Interface definition for a callback to be notified of {@link Loader} events. - */ - public interface Callback - { - - /** - * Invoked when loading has been canceled. - * - * @param loadable The loadable whose load has been canceled. - */ - void onLoadCanceled(Loadable loadable); - - /** - * Invoked when the data source has been fully loaded. - * - * @param loadable The loadable whose load has completed. - */ - void onLoadCompleted(Loadable loadable); - - /** - * Invoked when the data source is stopped due to an error. - * - * @param loadable The loadable whose load has failed. - */ - void onLoadError(Loadable loadable, IOException exception); + private volatile Thread executorThread; + public LoadTask(Looper looper, Loadable loadable, Loader.Callback callback, int delayMs) { + super(looper); + this.loadable = loadable; + this.callback = callback; + this.delayMs = delayMs; } - /** - * Thrown when an unexpected exception is encountered during loading. - */ - public static final class UnexpectedLoaderException extends IOException - { + public void quit() { + loadable.cancelLoad(); + if (executorThread != null) { + executorThread.interrupt(); + } + } - public UnexpectedLoaderException(Exception cause) - { - super("Unexpected " + cause.getClass().getSimpleName() + ": " + cause.getMessage(), cause); - } + @Override + public void run(){ + try + { + executorThread = Thread.currentThread(); + if (delayMs > 0) + { + Thread.sleep(delayMs); + } + if (!loadable.isLoadCanceled()) + { + loadable.load(); + } + passEmptyMessage(MSG_END_OF_SOURCE); + } + catch (IOException e) + { + obtainMessage(MSG_IO_EXCEPTION, e).sendToTarget(); + } + catch (InterruptedException e) + { + // The load was canceled. + Assertions.checkState(loadable.isLoadCanceled()); + passEmptyMessage(MSG_END_OF_SOURCE); + } + catch (Exception e) + { + // This should never happen, but handle it anyway. + Log.e(TAG, "Unexpected exception loading stream", e); + obtainMessage(MSG_IO_EXCEPTION, new UnexpectedLoaderException(e)).sendToTarget(); + } + catch (Error e) + { + // We'd hope that the platform would kill the process if an Error is thrown here, but the + // executor may catch the error (b/20616433). Throw it here, but also pass and throw it from + // the handler thread so that the process dies even if the executor behaves in this way. + Log.e(TAG, "Unexpected error loading stream", e); + obtainMessage(MSG_FATAL_ERROR, e).sendToTarget(); + throw e; + } + } + private void passEmptyMessage(int what){ + if (getLooper().getThread().getState() != Thread.State.TERMINATED) + { + sendEmptyMessage(what); + } } - @SuppressLint("HandlerLeak") - private final class LoadTask extends Handler implements Runnable - { - - private static final String TAG = "LoadTask"; - - private final Loadable loadable; - private final Loader.Callback callback; - private final int delayMs; - - private volatile Thread executorThread; - - public LoadTask(Looper looper, Loadable loadable, Loader.Callback callback, int delayMs) - { - super(looper); - this.loadable = loadable; - this.callback = callback; - this.delayMs = delayMs; - } - - public void quit() - { - loadable.cancelLoad(); - if (executorThread != null) - { - executorThread.interrupt(); - } - } - - @Override - public void run() - { - try - { - executorThread = Thread.currentThread(); - if (delayMs > 0) - { - Thread.sleep(delayMs); - } - if (!loadable.isLoadCanceled()) - { - loadable.load(); - } - passEmptyMessage(MSG_END_OF_SOURCE); - } - catch (IOException e) - { - obtainMessage(MSG_IO_EXCEPTION, e).sendToTarget(); - } - catch (InterruptedException e) - { - // The load was canceled. - Assertions.checkState(loadable.isLoadCanceled()); - passEmptyMessage(MSG_END_OF_SOURCE); - } - catch (Exception e) - { - // This should never happen, but handle it anyway. - Log.e(TAG, "Unexpected exception loading stream", e); - obtainMessage(MSG_IO_EXCEPTION, new UnexpectedLoaderException(e)).sendToTarget(); - } - catch (Error e) - { - // We'd hope that the platform would kill the process if an Error is thrown here, but the - // executor may catch the error (b/20616433). Throw it here, but also pass and throw it from - // the handler thread so that the process dies even if the executor behaves in this way. - Log.e(TAG, "Unexpected error loading stream", e); - obtainMessage(MSG_FATAL_ERROR, e).sendToTarget(); - throw e; - } - } - - private void passEmptyMessage(int what) - { - if (getLooper().getThread().getState() != Thread.State.TERMINATED) - { - sendEmptyMessage(what); - } - } - - - @Override - public void handleMessage(Message msg) - { - if (msg.what == MSG_FATAL_ERROR) - { - throw (Error) msg.obj; - } - onFinished(); - if (loadable.isLoadCanceled()) - { - callback.onLoadCanceled(loadable); - return; - } - switch (msg.what) - { - case MSG_END_OF_SOURCE: - callback.onLoadCompleted(loadable); - break; - case MSG_IO_EXCEPTION: - callback.onLoadError(loadable, (IOException) msg.obj); - break; - } - } - - private void onFinished() - { - loading = false; - currentTask = null; - } + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_FATAL_ERROR) { + throw (Error) msg.obj; + } + onFinished(); + if (loadable.isLoadCanceled()) { + callback.onLoadCanceled(loadable); + return; + } + switch (msg.what) { + case MSG_END_OF_SOURCE: + callback.onLoadCompleted(loadable); + break; + case MSG_IO_EXCEPTION: + callback.onLoadError(loadable, (IOException) msg.obj); + break; + } + } + private void onFinished() { + loading = false; + currentTask = null; } + } + } From 9ffee6101294ac6179cd69cfce6331b72013f020 Mon Sep 17 00:00:00 2001 From: Yossi Elkrief Date: Wed, 15 Jul 2015 19:12:19 +0300 Subject: [PATCH 4/4] Update Loader.java --- .../android/exoplayer/upstream/Loader.java | 74 ++++++++----------- 1 file changed, 31 insertions(+), 43 deletions(-) diff --git a/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java b/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java index 022c97a389c..ccdb8e1da9d 100644 --- a/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java +++ b/library/src/main/java/com/google/android/exoplayer/upstream/Loader.java @@ -232,54 +232,42 @@ public void quit() { } @Override - public void run(){ - try - { - executorThread = Thread.currentThread(); - if (delayMs > 0) - { - Thread.sleep(delayMs); - } - if (!loadable.isLoadCanceled()) - { - loadable.load(); - } - passEmptyMessage(MSG_END_OF_SOURCE); - } - catch (IOException e) - { - obtainMessage(MSG_IO_EXCEPTION, e).sendToTarget(); - } - catch (InterruptedException e) - { - // The load was canceled. - Assertions.checkState(loadable.isLoadCanceled()); - passEmptyMessage(MSG_END_OF_SOURCE); - } - catch (Exception e) - { - // This should never happen, but handle it anyway. - Log.e(TAG, "Unexpected exception loading stream", e); - obtainMessage(MSG_IO_EXCEPTION, new UnexpectedLoaderException(e)).sendToTarget(); - } - catch (Error e) - { - // We'd hope that the platform would kill the process if an Error is thrown here, but the - // executor may catch the error (b/20616433). Throw it here, but also pass and throw it from - // the handler thread so that the process dies even if the executor behaves in this way. - Log.e(TAG, "Unexpected error loading stream", e); - obtainMessage(MSG_FATAL_ERROR, e).sendToTarget(); - throw e; + public void run() { + try { + executorThread = Thread.currentThread(); + if (delayMs > 0) { + Thread.sleep(delayMs); + } + if (!loadable.isLoadCanceled()) { + loadable.load(); + } + passEmptyMessage(MSG_END_OF_SOURCE); + } catch (IOException e) { + obtainMessage(MSG_IO_EXCEPTION, e).sendToTarget(); + } catch (InterruptedException e) { + // The load was canceled. + Assertions.checkState(loadable.isLoadCanceled()); + passEmptyMessage(MSG_END_OF_SOURCE); + } catch (Exception e) { + // This should never happen, but handle it anyway. + Log.e(TAG, "Unexpected exception loading stream", e); + obtainMessage(MSG_IO_EXCEPTION, new UnexpectedLoaderException(e)).sendToTarget(); + } catch (Error e) { + // We'd hope that the platform would kill the process if an Error is thrown here, but the + // executor may catch the error (b/20616433). Throw it here, but also pass and throw it from + // the handler thread so that the process dies even if the executor behaves in this way. + Log.e(TAG, "Unexpected error loading stream", e); + obtainMessage(MSG_FATAL_ERROR, e).sendToTarget(); + throw e; } } - + private void passEmptyMessage(int what){ - if (getLooper().getThread().getState() != Thread.State.TERMINATED) - { - sendEmptyMessage(what); + if (getLooper().getThread().getState() != Thread.State.TERMINATED){ + sendEmptyMessage(what); } } - + @Override public void handleMessage(Message msg) { if (msg.what == MSG_FATAL_ERROR) {