1

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:

  1. FragmentA starts ActivityB with intent extras
  2. ActivityB pass the data to the FragmentB
  3. 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?
Community
  • 1
  • 1
Addev
  • 31,819
  • 51
  • 183
  • 302
  • Generally you should not pass large pieces of data through Binder. Binder is not meant to handle large pieces of data, best to stick to primitives. – JoxTraex Aug 27 '15 at 11:36
  • Then using an alternative for the startActivityB case should be enough right? – Addev Aug 27 '15 at 11:37

0 Answers0