I'm investigating how to pass and save across configuration changes large amounts of data.
Basically I have a ActivityA
(holding FragmentA) and a ActivityB
(holding FragmentB
). The target is to pass a very large String from the FragmentA
to the FragmentB
.
With the following method I create the string:
public class StringHelper {
public static String generateRandomLongString() {
Random rand = new Random();
StringBuilder builder = new StringBuilder();
builder.append(rand.nextInt(1000));
builder.append("_");
for (int i = 0; i < 10000000; i++) {
builder.append((char) (rand.nextInt('z' - ' ') + ' '));
}
return builder.toString();
}
}
I try to follow these steps:
- FragmentA starts ActivityB with intent extras
- ActivityB pass the data to the FragmentB
- FragmentB handles the instance state management
Testing different solutions I noticed the following situations (in my devices and emulators):
IMPORTANT: Only the relevant code is shown in each piece of code
1. Passing the data to the ActivityB:
Intent intent = new Intent(v.getContext(), ActivityB.class);
String fakedData = StringHelper.generateRandomLongString();
Log.e("test", "Launching ActivityB, data = " + fakedData);
intent.putExtra("test", fakedData);
startActivity(intent);
the ActivityB:
public class ActivityB extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_second);
Log.e("Received "," "+getIntent().getStringExtra("test"));
}
}
This causes an Failure from system, since the bundle exceeds the max size for IPC.
java.lang.RuntimeException: Failure from system
at android.app.Instrumentation.execStartActivity(Instrumentation.java:1514)
at android.app.Activity.startActivityForResult(Activity.java:3917)
at android.app.Activity.startActivityForResult(Activity.java:3877)
at android.support.v4.app.FragmentActivity.startActivityFromFragment(FragmentActivity.java:813)
at android.support.v4.app.FragmentActivity$HostCallbacks.onStartActivityFromFragment(FragmentActivity.java:871)
at android.support.v4.app.Fragment.startActivity(Fragment.java:916)
This should not be a problem since there are other approach that can be used to pass the data to the activity, shared memory, singletons, static classes, etc (see THIS_LINK for more info)
2. Passing the data to the FragmentB:
Using the Fragment arguments I don't get crashes and the data arrives correctly
public class ActivityA extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
Fragment fragment;
//fragment = new MainActivityFragment();
String fakedData = StringHelper.generateRandomLongString();
Log.e("TEST", "Activity String=" + fakedData);
fragment = FragmentB.newInstance(fakedData);
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content,fragment, "fragmentTag").commit();
}
}
}
public class FragmentB extends Fragment {
public static FragmentB newInstance(String data) {
FragmentB result = new FragmentB();
Bundle args = new Bundle();
args.putString("data", data);
result.setArguments(args);
return result;
}
....
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.e("TEST", "DATA_FROM_ARGS=" + getArguments().getString("data"));
}
}
3. Saving the instance state in the FragmentB:
Amusing the FragmentB can modify the data and you want to keep the updated one. Also seems to work correctly with no crashes.
public class FragmentB extends Fragment {
private String myData;
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedInstanceState == null) {
myData = getArguments().getString("data");
} else {
myData = savedInstanceState.getString("savedData");
}
Log.e("TEST", "DATA=" + myData);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putString("savedData", myData);
}
}
Questions
- Why do I only get crashes in the step 1.
- Is there some case where the IPC enters in scene in the steps 2 or 3 generating a crash due to the bundle limit?
- Would it be correct to replace the step 1 with a shared memory communication and let the rest of the step as is?