Commit 9f20683a by tonihei Committed by Oliver Woodman

Factor out Handler creation in construtor to prevent warning suppression.

Using new Handler(this) in a constructor potentially leaks an uninitialized
object. This is mostly not a problem because we don't use the Handler within
the constructor. Added a Util method to keep the warning suppression local.

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=199605377
parent cad3de91
......@@ -19,12 +19,14 @@ import android.os.Handler;
import android.os.Handler.Callback;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.BaseRenderer;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.Util;
import java.util.Arrays;
/**
......@@ -46,7 +48,7 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
private final MetadataDecoderFactory decoderFactory;
private final MetadataOutput output;
private final Handler outputHandler;
private final @Nullable Handler outputHandler;
private final FormatHolder formatHolder;
private final MetadataInputBuffer buffer;
private final Metadata[] pendingMetadata;
......@@ -61,11 +63,11 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
* @param output The output.
* @param outputLooper The looper associated with the thread on which the output should be called.
* If the output makes use of standard Android UI components, then this should normally be the
* looper associated with the application's main thread, which can be obtained using
* {@link android.app.Activity#getMainLooper()}. Null may be passed if the output should be
* called directly on the player's internal rendering thread.
* looper associated with the application's main thread, which can be obtained using {@link
* android.app.Activity#getMainLooper()}. Null may be passed if the output should be called
* directly on the player's internal rendering thread.
*/
public MetadataRenderer(MetadataOutput output, Looper outputLooper) {
public MetadataRenderer(MetadataOutput output, @Nullable Looper outputLooper) {
this(output, outputLooper, MetadataDecoderFactory.DEFAULT);
}
......@@ -73,16 +75,17 @@ public final class MetadataRenderer extends BaseRenderer implements Callback {
* @param output The output.
* @param outputLooper The looper associated with the thread on which the output should be called.
* If the output makes use of standard Android UI components, then this should normally be the
* looper associated with the application's main thread, which can be obtained using
* {@link android.app.Activity#getMainLooper()}. Null may be passed if the output should be
* called directly on the player's internal rendering thread.
* looper associated with the application's main thread, which can be obtained using {@link
* android.app.Activity#getMainLooper()}. Null may be passed if the output should be called
* directly on the player's internal rendering thread.
* @param decoderFactory A factory from which to obtain {@link MetadataDecoder} instances.
*/
public MetadataRenderer(MetadataOutput output, Looper outputLooper,
MetadataDecoderFactory decoderFactory) {
public MetadataRenderer(
MetadataOutput output, @Nullable Looper outputLooper, MetadataDecoderFactory decoderFactory) {
super(C.TRACK_TYPE_METADATA);
this.output = Assertions.checkNotNull(output);
this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this);
this.outputHandler =
outputLooper == null ? null : Util.createHandler(outputLooper, /* callback= */ this);
this.decoderFactory = Assertions.checkNotNull(decoderFactory);
formatHolder = new FormatHolder();
buffer = new MetadataInputBuffer();
......
......@@ -20,6 +20,7 @@ import android.os.Handler.Callback;
import android.os.Looper;
import android.os.Message;
import android.support.annotation.IntDef;
import android.support.annotation.Nullable;
import com.google.android.exoplayer2.BaseRenderer;
import com.google.android.exoplayer2.C;
import com.google.android.exoplayer2.ExoPlaybackException;
......@@ -27,6 +28,7 @@ import com.google.android.exoplayer2.Format;
import com.google.android.exoplayer2.FormatHolder;
import com.google.android.exoplayer2.util.Assertions;
import com.google.android.exoplayer2.util.MimeTypes;
import com.google.android.exoplayer2.util.Util;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.util.Collections;
......@@ -70,7 +72,7 @@ public final class TextRenderer extends BaseRenderer implements Callback {
private static final int MSG_UPDATE_OUTPUT = 0;
private final Handler outputHandler;
private final @Nullable Handler outputHandler;
private final TextOutput output;
private final SubtitleDecoderFactory decoderFactory;
private final FormatHolder formatHolder;
......@@ -87,30 +89,31 @@ public final class TextRenderer extends BaseRenderer implements Callback {
/**
* @param output The output.
* @param outputLooper The looper associated with the thread on which the output should be
* called. If the output makes use of standard Android UI components, then this should
* normally be the looper associated with the application's main thread, which can be obtained
* using {@link android.app.Activity#getMainLooper()}. Null may be passed if the output
* should be called directly on the player's internal rendering thread.
* @param outputLooper The looper associated with the thread on which the output should be called.
* If the output makes use of standard Android UI components, then this should normally be the
* looper associated with the application's main thread, which can be obtained using {@link
* android.app.Activity#getMainLooper()}. Null may be passed if the output should be called
* directly on the player's internal rendering thread.
*/
public TextRenderer(TextOutput output, Looper outputLooper) {
public TextRenderer(TextOutput output, @Nullable Looper outputLooper) {
this(output, outputLooper, SubtitleDecoderFactory.DEFAULT);
}
/**
* @param output The output.
* @param outputLooper The looper associated with the thread on which the output should be
* called. If the output makes use of standard Android UI components, then this should
* normally be the looper associated with the application's main thread, which can be obtained
* using {@link android.app.Activity#getMainLooper()}. Null may be passed if the output
* should be called directly on the player's internal rendering thread.
* @param outputLooper The looper associated with the thread on which the output should be called.
* If the output makes use of standard Android UI components, then this should normally be the
* looper associated with the application's main thread, which can be obtained using {@link
* android.app.Activity#getMainLooper()}. Null may be passed if the output should be called
* directly on the player's internal rendering thread.
* @param decoderFactory A factory from which to obtain {@link SubtitleDecoder} instances.
*/
public TextRenderer(TextOutput output, Looper outputLooper,
SubtitleDecoderFactory decoderFactory) {
public TextRenderer(
TextOutput output, @Nullable Looper outputLooper, SubtitleDecoderFactory decoderFactory) {
super(C.TRACK_TYPE_TEXT);
this.output = Assertions.checkNotNull(output);
this.outputHandler = outputLooper == null ? null : new Handler(outputLooper, this);
this.outputHandler =
outputLooper == null ? null : Util.createHandler(outputLooper, /* callback= */ this);
this.decoderFactory = decoderFactory;
formatHolder = new FormatHolder();
}
......
......@@ -29,6 +29,8 @@ import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.Parcel;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
......@@ -66,6 +68,7 @@ import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.checkerframework.checker.initialization.qual.UnknownInitialization;
import org.checkerframework.checker.nullness.qual.PolyNull;
/**
......@@ -237,13 +240,42 @@ public final class Util {
* @param length The output array length. Must be less or equal to the length of the input array.
* @return The copied array.
*/
@SuppressWarnings("nullness:assignment.type.incompatible")
@SuppressWarnings({"nullness:argument.type.incompatible", "nullness:return.type.incompatible"})
public static <T> T[] nullSafeArrayCopy(T[] input, int length) {
Assertions.checkArgument(length <= input.length);
return Arrays.copyOf(input, length);
}
/**
* Creates a {@link Handler} with the specified {@link Handler.Callback} on the current {@link
* Looper} thread. The method accepts partially initialized objects as callback under the
* assumption that the Handler won't be used to send messages until the callback is fully
* initialized.
*
* @param callback A {@link Handler.Callback}. May be a partially initialized class.
* @return A {@link Handler} with the specified callback on the current {@link Looper} thread.
*/
public static Handler createHandler(Handler.@UnknownInitialization Callback callback) {
return createHandler(Looper.myLooper(), callback);
}
/**
* Creates a {@link Handler} with the specified {@link Handler.Callback} on the specified {@link
* Looper} thread. The method accepts partially initialized objects as callback under the
* assumption that the Handler won't be used to send messages until the callback is fully
* initialized.
*
* @param looper A {@link Looper} to run the callback on.
* @param callback A {@link Handler.Callback}. May be a partially initialized class.
* @return A {@link Handler} with the specified callback on the current {@link Looper} thread.
*/
@SuppressWarnings({"nullness:argument.type.incompatible", "nullness:return.type.incompatible"})
public static Handler createHandler(
Looper looper, Handler.@UnknownInitialization Callback callback) {
return new Handler(looper, callback);
}
/**
* Instantiates a new single threaded executor whose thread has the specified name.
*
* @param threadName The name of the thread.
......
......@@ -291,7 +291,7 @@ public final class VideoFrameReleaseTimeHelper {
sampledVsyncTimeNs = C.TIME_UNSET;
choreographerOwnerThread = new HandlerThread("ChoreographerOwner:Handler");
choreographerOwnerThread.start();
handler = new Handler(choreographerOwnerThread.getLooper(), this);
handler = Util.createHandler(choreographerOwnerThread.getLooper(), /* callback= */ this);
handler.sendEmptyMessage(CREATE_CHOREOGRAPHER);
}
......
......@@ -35,6 +35,7 @@ import com.google.android.exoplayer2.source.chunk.Chunk;
import com.google.android.exoplayer2.source.dash.manifest.DashManifest;
import com.google.android.exoplayer2.upstream.Allocator;
import com.google.android.exoplayer2.util.ParsableByteArray;
import com.google.android.exoplayer2.util.Util;
import java.io.IOException;
import java.util.Iterator;
import java.util.Map;
......@@ -100,7 +101,6 @@ public final class PlayerEmsgHandler implements Handler.Callback {
* messages that generate DASH media source events.
* @param allocator An {@link Allocator} from which allocations can be obtained.
*/
@SuppressWarnings("nullness")
public PlayerEmsgHandler(
DashManifest manifest, PlayerEmsgCallback playerEmsgCallback, Allocator allocator) {
this.manifest = manifest;
......@@ -108,7 +108,7 @@ public final class PlayerEmsgHandler implements Handler.Callback {
this.allocator = allocator;
manifestPublishTimeToExpiryTimeUs = new TreeMap<>();
handler = new Handler(this);
handler = Util.createHandler(/* callback= */ this);
decoder = new EventMessageDecoder();
lastLoadedChunkEndTimeUs = C.TIME_UNSET;
lastLoadedChunkEndTimeBeforeRefreshUs = C.TIME_UNSET;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment