Skip to content
Permalink
Browse files
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: #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
  • Loading branch information
petterh authored and facebook-github-bot committed Sep 30, 2019
1 parent e1d89fb commit 4b9350061fa3d186fdd3a973e1b46f60a7ac03b9
Showing 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;
}
}
@@ -7,6 +7,8 @@
package com.facebook.react.bridge;

import android.os.Bundle;
import android.os.Parcelable;

import androidx.annotation.Nullable;
import java.lang.reflect.Array;
import java.util.AbstractList;
@@ -218,6 +220,14 @@ public static WritableArray fromArray(Object array) {
for (boolean v : (boolean[]) array) {
catalystArray.pushBoolean(v);
}
} else if (array instanceof Parcelable[]) {
for (Parcelable v : (Parcelable[]) array) {
if (v instanceof Bundle) {
catalystArray.pushMap(fromBundle((Bundle) v));
} else {
throw new IllegalArgumentException("Unexpected array member type " + v.getClass());
}
}
} else {
throw new IllegalArgumentException("Unknown array type " + array.getClass());
}

0 comments on commit 4b93500

Please sign in to comment.