Skip to content

Commit 4b93500

Browse files
Petter Hesselbergfacebook-github-bot
Petter Hesselberg
authored andcommitted
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
1 parent e1d89fb commit 4b93500

File tree

2 files changed

+99
-0
lines changed

2 files changed

+99
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
package com.facebook.react.bridge;
2+
3+
import android.content.Context;
4+
import android.content.Intent;
5+
import android.os.Bundle;
6+
import android.os.Parcel;
7+
import android.os.Parcelable;
8+
9+
import androidx.annotation.NonNull;
10+
import androidx.test.platform.app.InstrumentationRegistry;
11+
import androidx.test.runner.AndroidJUnit4;
12+
13+
import com.facebook.soloader.SoLoader;
14+
15+
import org.junit.Before;
16+
import org.junit.Test;
17+
import org.junit.runner.RunWith;
18+
19+
import static com.facebook.react.bridge.Arguments.fromBundle;
20+
import static org.junit.Assert.assertEquals;
21+
import static org.junit.Assert.assertNotNull;
22+
23+
@RunWith(AndroidJUnit4.class)
24+
public class ArgumentsTest {
25+
26+
@Before
27+
public void setUp() {
28+
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
29+
SoLoader.init(context, false);
30+
}
31+
32+
@Test
33+
public void testFromBundle() {
34+
verifyBundle(createBundle());
35+
}
36+
37+
/**
38+
* When passing a bundle via {@link Intent} extras, it gets parceled and unparceled.
39+
* Any array of bundles will return as an array of {@link Parcelable} instead. This test
40+
* verifies that {@link Arguments#fromArray} handles this situation correctly.
41+
*/
42+
@Test
43+
public void testFromMarshaledBundle() {
44+
verifyBundle(marshalAndUnmarshalBundle(createBundle()));
45+
}
46+
47+
private void verifyBundle(@NonNull Bundle bundle) {
48+
WritableMap map = fromBundle(bundle);
49+
assertNotNull(map);
50+
51+
assertEquals(ReadableType.Array, map.getType("children"));
52+
ReadableArray children = map.getArray("children");
53+
assertNotNull(children);
54+
assertEquals(1, children.size());
55+
56+
assertEquals(ReadableType.Map, children.getType(0));
57+
ReadableMap child = children.getMap(0);
58+
assertNotNull(child);
59+
assertEquals("exampleChild", child.getString("exampleChildKey"));
60+
}
61+
62+
@NonNull
63+
private Bundle marshalAndUnmarshalBundle(@NonNull Bundle bundle) {
64+
Parcel parcel = null;
65+
try {
66+
parcel = Parcel.obtain();
67+
bundle.writeToParcel(parcel, 0);
68+
69+
byte[] bytes = parcel.marshall();
70+
parcel.unmarshall(bytes, 0, bytes.length);
71+
parcel.setDataPosition(0);
72+
return Bundle.CREATOR.createFromParcel(parcel);
73+
} finally {
74+
if (parcel != null) {
75+
parcel.recycle();
76+
}
77+
}
78+
}
79+
80+
@NonNull
81+
private Bundle createBundle() {
82+
Bundle bundle = new Bundle();
83+
Bundle[] children = new Bundle[1];
84+
children[0] = new Bundle();
85+
children[0].putString("exampleChildKey", "exampleChild");
86+
bundle.putSerializable("children", children);
87+
return bundle;
88+
}
89+
}

ReactAndroid/src/main/java/com/facebook/react/bridge/Arguments.java

+10
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
package com.facebook.react.bridge;
88

99
import android.os.Bundle;
10+
import android.os.Parcelable;
11+
1012
import androidx.annotation.Nullable;
1113
import java.lang.reflect.Array;
1214
import java.util.AbstractList;
@@ -218,6 +220,14 @@ public static WritableArray fromArray(Object array) {
218220
for (boolean v : (boolean[]) array) {
219221
catalystArray.pushBoolean(v);
220222
}
223+
} else if (array instanceof Parcelable[]) {
224+
for (Parcelable v : (Parcelable[]) array) {
225+
if (v instanceof Bundle) {
226+
catalystArray.pushMap(fromBundle((Bundle) v));
227+
} else {
228+
throw new IllegalArgumentException("Unexpected array member type " + v.getClass());
229+
}
230+
}
221231
} else {
222232
throw new IllegalArgumentException("Unknown array type " + array.getClass());
223233
}

0 commit comments

Comments
 (0)