Petterh/support parcelable array args (#26379)

Summary:
`ReactRootView.startReactApplication` takes a `Bundle` argument called `initialProperties`. This is translated to a `ReadableMap` via `Arguments.fromBundle`. If the bundle contains an array of bundles, this gets translated by `Arguments.fromArray`.

If the bundle was passed from one activity to another via intent extras, however, there is a problem. After the bundle has been marshaled and unmarshaled, the array of `Bundle`s come out the other end as an arrap of `Parcelable`s, although each array element remains a `Bundle`. This results in an "Unknown array type" exception.

This PR fixes this by adding support for `Parcelable` arrays – provided they contain only members of type `Bundle`.

## Changelog

[Android] [Fixed] - Don't throw "Unknown array type" exception when passing serialized bundle arrays in ReactRootView.startReactApplication's initialProperties parameter
Pull Request resolved: https://github.com/facebook/react-native/pull/26379

Test Plan: Added test class `ArgumentsTest`. The test method `testFromMarshaledBundle` fails when used on the old version of `Arguments`.

Differential Revision: D17661203

Pulled By: cpojer

fbshipit-source-id: 63612d78f49bdf9cc53f6f21ae883dba6cebce84
This commit is contained in:
Petter Hesselberg
2019-09-29 19:22:22 -07:00
committed by Facebook Github Bot
parent e1d89fbd9d
commit 4b9350061f
2 changed files with 99 additions and 0 deletions
@@ -0,0 +1,89 @@
package com.facebook.react.bridge;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import com.facebook.soloader.SoLoader;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import static com.facebook.react.bridge.Arguments.fromBundle;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@RunWith(AndroidJUnit4.class)
public class ArgumentsTest {
@Before
public void setUp() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
SoLoader.init(context, false);
}
@Test
public void testFromBundle() {
verifyBundle(createBundle());
}
/**
* When passing a bundle via {@link Intent} extras, it gets parceled and unparceled.
* Any array of bundles will return as an array of {@link Parcelable} instead. This test
* verifies that {@link Arguments#fromArray} handles this situation correctly.
*/
@Test
public void testFromMarshaledBundle() {
verifyBundle(marshalAndUnmarshalBundle(createBundle()));
}
private void verifyBundle(@NonNull Bundle bundle) {
WritableMap map = fromBundle(bundle);
assertNotNull(map);
assertEquals(ReadableType.Array, map.getType("children"));
ReadableArray children = map.getArray("children");
assertNotNull(children);
assertEquals(1, children.size());
assertEquals(ReadableType.Map, children.getType(0));
ReadableMap child = children.getMap(0);
assertNotNull(child);
assertEquals("exampleChild", child.getString("exampleChildKey"));
}
@NonNull
private Bundle marshalAndUnmarshalBundle(@NonNull Bundle bundle) {
Parcel parcel = null;
try {
parcel = Parcel.obtain();
bundle.writeToParcel(parcel, 0);
byte[] bytes = parcel.marshall();
parcel.unmarshall(bytes, 0, bytes.length);
parcel.setDataPosition(0);
return Bundle.CREATOR.createFromParcel(parcel);
} finally {
if (parcel != null) {
parcel.recycle();
}
}
}
@NonNull
private Bundle createBundle() {
Bundle bundle = new Bundle();
Bundle[] children = new Bundle[1];
children[0] = new Bundle();
children[0].putString("exampleChildKey", "exampleChild");
bundle.putSerializable("children", children);
return bundle;
}
}