Merge "Make TimestampedValue Parcelable"

This commit is contained in:
Treehugger Robot
2019-10-14 18:33:43 +00:00
committed by Gerrit Code Review
3 changed files with 54 additions and 69 deletions

View File

@@ -56,8 +56,7 @@ public final class TimeSignal implements Parcelable {
private static TimeSignal createFromParcel(Parcel in) {
String sourceId = in.readString();
TimestampedValue<Long> utcTime =
TimestampedValue.readFromParcel(in, null /* classLoader */, Long.class);
TimestampedValue<Long> utcTime = in.readParcelable(null /* classLoader */);
return new TimeSignal(sourceId, utcTime);
}
@@ -69,7 +68,7 @@ public final class TimeSignal implements Parcelable {
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(mSourceId);
TimestampedValue.writeToParcel(dest, mUtcTime);
dest.writeParcelable(mUtcTime, 0);
}
@NonNull

View File

@@ -19,6 +19,7 @@ package android.util;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemClock;
import java.util.Objects;
@@ -30,14 +31,14 @@ import java.util.Objects;
* If a suitable clock is used the reference time can be used to identify the age of a value or
* ordering between values.
*
* <p>To read and write a timestamped value from / to a Parcel see
* {@link #readFromParcel(Parcel, ClassLoader, Class)} and
* {@link #writeToParcel(Parcel, TimestampedValue)}.
* <p>This class implements {@link Parcelable} for convenience but instances will only actually be
* parcelable if the value type held is {@code null}, {@link Parcelable}, or one of the other types
* supported by {@link Parcel#writeValue(Object)} / {@link Parcel#readValue(ClassLoader)}.
*
* @param <T> the type of the value with an associated timestamp
* @hide
*/
public final class TimestampedValue<T> {
public final class TimestampedValue<T> implements Parcelable {
private final long mReferenceTimeMillis;
private final T mValue;
@@ -80,53 +81,6 @@ public final class TimestampedValue<T> {
+ '}';
}
/**
* Read a {@link TimestampedValue} from a parcel that was stored using
* {@link #writeToParcel(Parcel, TimestampedValue)}.
*
* <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
* and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
* supported by those methods.
*
* @param in the Parcel to read from
* @param classLoader the ClassLoader to pass to {@link Parcel#readValue(ClassLoader)}
* @param valueClass the expected type of the value, typically the same as {@code <T>} but can
* also be a subclass
* @throws RuntimeException if the value read is not compatible with {@code valueClass} or the
* object could not be read
*/
@SuppressWarnings("unchecked")
@NonNull
public static <T> TimestampedValue<T> readFromParcel(
@NonNull Parcel in, @Nullable ClassLoader classLoader, Class<? extends T> valueClass) {
long referenceTimeMillis = in.readLong();
T value = (T) in.readValue(classLoader);
// Equivalent to static code: if (!(value.getClass() instanceof {valueClass})) {
if (value != null && !valueClass.isAssignableFrom(value.getClass())) {
throw new RuntimeException("Value was of type " + value.getClass()
+ " is not assignable to " + valueClass);
}
return new TimestampedValue<>(referenceTimeMillis, value);
}
/**
* Write a {@link TimestampedValue} to a parcel so that it can be read using
* {@link #readFromParcel(Parcel, ClassLoader, Class)}.
*
* <p>The marshalling/unmarshalling of the value relies upon {@link Parcel#writeValue(Object)}
* and {@link Parcel#readValue(ClassLoader)} and so this method can only be used with types
* supported by those methods.
*
* @param dest the Parcel
* @param timestampedValue the value
* @throws RuntimeException if the value could not be written to the Parcel
*/
public static void writeToParcel(
@NonNull Parcel dest, @NonNull TimestampedValue<?> timestampedValue) {
dest.writeLong(timestampedValue.mReferenceTimeMillis);
dest.writeValue(timestampedValue.mValue);
}
/**
* Returns the difference in milliseconds between two instance's reference times.
*/
@@ -134,4 +88,37 @@ public final class TimestampedValue<T> {
@NonNull TimestampedValue<?> one, @NonNull TimestampedValue<?> two) {
return one.mReferenceTimeMillis - two.mReferenceTimeMillis;
}
public static final @NonNull Parcelable.Creator<TimestampedValue<?>> CREATOR =
new Parcelable.ClassLoaderCreator<TimestampedValue<?>>() {
@Override
public TimestampedValue<?> createFromParcel(@NonNull Parcel source) {
return createFromParcel(source, null);
}
@Override
public TimestampedValue<?> createFromParcel(
@NonNull Parcel source, @Nullable ClassLoader classLoader) {
long referenceTimeMillis = source.readLong();
Object value = source.readValue(classLoader);
return new TimestampedValue<>(referenceTimeMillis, value);
}
@Override
public TimestampedValue[] newArray(int size) {
return new TimestampedValue[size];
}
};
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeLong(mReferenceTimeMillis);
dest.writeValue(mValue);
}
}

View File

@@ -55,12 +55,12 @@ public class TimestampedValueTest {
TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
Parcel parcel = Parcel.obtain();
try {
TimestampedValue.writeToParcel(parcel, stringValue);
parcel.writeParcelable(stringValue, 0);
parcel.setDataPosition(0);
TimestampedValue<String> stringValueCopy =
TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
parcel.readParcelable(null /* classLoader */);
assertEquals(stringValue, stringValueCopy);
} finally {
parcel.recycle();
@@ -72,12 +72,12 @@ public class TimestampedValueTest {
TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
Parcel parcel = Parcel.obtain();
try {
TimestampedValue.writeToParcel(parcel, stringValue);
parcel.writeParcelable(stringValue, 0);
parcel.setDataPosition(0);
TimestampedValue<Object> stringValueCopy =
TimestampedValue.readFromParcel(parcel, null /* classLoader */, Object.class);
TimestampedValue<String> stringValueCopy =
parcel.readParcelable(null /* classLoader */);
assertEquals(stringValue, stringValueCopy);
} finally {
parcel.recycle();
@@ -85,15 +85,15 @@ public class TimestampedValueTest {
}
@Test
public void testParceling_valueClassIncompatible() {
TimestampedValue<String> stringValue = new TimestampedValue<>(1000, "Hello");
public void testParceling_valueClassNotParcelable() {
// This class is not one supported by Parcel.writeValue().
class NotParcelable {}
TimestampedValue<NotParcelable> notParcelableValue =
new TimestampedValue<>(1000, new NotParcelable());
Parcel parcel = Parcel.obtain();
try {
TimestampedValue.writeToParcel(parcel, stringValue);
parcel.setDataPosition(0);
TimestampedValue.readFromParcel(parcel, null /* classLoader */, Double.class);
parcel.writeParcelable(notParcelableValue, 0);
fail();
} catch (RuntimeException expected) {
} finally {
@@ -106,12 +106,11 @@ public class TimestampedValueTest {
TimestampedValue<String> nullValue = new TimestampedValue<>(1000, null);
Parcel parcel = Parcel.obtain();
try {
TimestampedValue.writeToParcel(parcel, nullValue);
parcel.writeParcelable(nullValue, 0);
parcel.setDataPosition(0);
TimestampedValue<Object> nullValueCopy =
TimestampedValue.readFromParcel(parcel, null /* classLoader */, String.class);
TimestampedValue<String> nullValueCopy = parcel.readParcelable(null /* classLoader */);
assertEquals(nullValue, nullValueCopy);
} finally {
parcel.recycle();