1

I share media with the appended function and have following problem: if I share too many items, the share intent silently fails. No debug message in the log or similar and no delayed select target sheet... Just nothing happens if I share too many items. Somewhere about 50 items seems to be the limit...

Does anyone know why this happens? Or has a solution for that? Is there an way to find out if I share too many uris?

public static void shareMediaUris(Activity activity, ArrayList<Uri> uris)
{
    Intent sharingIntent = new Intent(Intent.ACTION_SEND_MULTIPLE);
    sharingIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);// | Intent.FLAG_ACTIVITY_NEW_TASK);

    sharingIntent.setType("*/*");
    String[] mimetypes = {"image/*", "video/*"};
    sharingIntent.putExtra(Intent.EXTRA_MIME_TYPES, mimetypes);

    sharingIntent.putParcelableArrayListExtra(Intent.EXTRA_STREAM, uris);

    finishShare(activity, sharingIntent);
}

public static void finishShare(Activity activity, Intent intent)
{
    List<ResolveInfo> resolveInfo = MainApp.get().getPackageManager().queryIntentActivities(intent, 0);
    if (!resolveInfo.isEmpty())
    {
        for (int i = resolveInfo.size() - 1; i >= 0; i--)
        {
            if (resolveInfo.get(i).activityInfo.packageName.equals(MainApp.get().getPackageName()))
                resolveInfo.remove(i);
        }

        if (resolveInfo.size() == 0)
            activity.startActivity(Intent.createChooser(intent, MainApp.get().getString(R.string.share_with)));
        else
        {
            List<Intent> targetedShareIntents = new ArrayList<Intent>();
            for (int i = 0; i < resolveInfo.size(); i++)
            {
                Intent targetedShareIntent = (Intent) intent.clone();
                targetedShareIntent.setPackage(resolveInfo.get(i).activityInfo.packageName);
                targetedShareIntent.setClassName(resolveInfo.get(i).activityInfo.packageName, resolveInfo.get(i).activityInfo.name);
                targetedShareIntents.add(targetedShareIntent);
            }
            Intent chooserIntent = Intent.createChooser(targetedShareIntents.remove(targetedShareIntents.size() - 1), activity.getString(R.string.share_with));
            chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, targetedShareIntents.toArray(new Parcelable[targetedShareIntents.size()]));
            activity.startActivity(chooserIntent);
        }
    }
    else
        activity.startActivity(Intent.createChooser(intent, MainApp.get().getString(R.string.share_with)));
}
prom85
  • 16,896
  • 17
  • 122
  • 242

1 Answers1

1

Here's what Google's Issue Tracker knows about this issue: Issue 5878: Intent Extras Have Size Limitation. I.e. it's "by design" behaviour.

I managed to reproduce it. If I share 500 URIs it works, if I share 1000 URIs - it's silently do nothing (I believe, it's exact your experience), if I share several thousands - it crashes (size-limit, see my original answer's part).

I checked the source code of Android.
In Instrumentation class this snippet starts the chooser-activity, not start chooser-activity or crashes, depends on the size of the Bundle in the Intent.

        int result = ActivityManagerNative.getDefault()
            .startActivity(whoThread, who.getBasePackageName(), intent,
                    intent.resolveTypeIfNeeded(who.getContentResolver()),
                    token, target != null ? target.mEmbeddedID : null,
                    requestCode, 0, null, options);

To be more precise, transactNative() method of Binder class:

public boolean transact(int code, Parcel data, Parcel reply, int flags) throws RemoteException {
    Binder.checkParcel(this, code, data, "Unreasonably large binder buffer");
    return transactNative(code, data, reply, flags);
}

In both, non-crash cases it returns 0 as result i.e. ActivityManager.START_SUCCESS. More than that, onPause() of the original activity is being called!

The only difference is that it's immediately call onResume() of the original activity, which you can use as a hint, that something went wrong.

But in general, as there's very blurry line between "chooser hasn't started" and application crashed with:

E/JavaBinder: !!! FAILED BINDER TRANSACTION !!! (parcel size = 3100860)

I'd suggest to

  • limit number of possible shared items within the app just to avoid this behaviour

  • wrap startActivity(chooserIntent) into try-catch block to avoid crash due to TransactionTooLargeException

  • in case if after startActivity(chooserIntent) exception hasn't been thrown, but onResume() of original Activity called - treat it same way as exception (i.e. notify user, that there's technical issue and user has to select fewer items to share)

Original answer: I think, you just stuck with size-limit of Intent's Bundle - it's around 1MB

There's a nice article, which describes it here: Yet another post on Serializable vs Parcelable
And you can read more about it here: Is there some limits in android' bundle?

Community
  • 1
  • 1
Konstantin Loginov
  • 15,802
  • 5
  • 58
  • 95
  • Thanks for that detailed answer. For me, the limit is somewhere around 40 `URI`s, 500 would be more than fine for me... Any ideas on how to make that better? You probably just tried it without removing the app itself from the shown share targets... I will limit the items to share (something like 100 or 250 would be fine for me), but something about 40 seems to be too low... – prom85 Jan 13 '16 at 11:31
  • @prom85 The obvious thing you can try to see if it changes anything - disable `finishShare` and replace it with `activity.startActivity(Intent.createChooser(intent, MainApp.get().getString(R.string.share_with)));` just to see, if your "limit" is anyhow affected by your second method (And let us know!). If it stays the same - I can just guess, that your URIs is anyhow bigger, than mine(I just chose same sample image from emulator 500 times). I'll think more about it. Meanwhile I suggest to wait if anyone else can share their insights regarding the question. – Konstantin Loginov Jan 13 '16 at 11:38
  • The limit is affected by my second method. Anyway (I already forget), I use a custom share dialog now and just handle on the share intent to this dialog (without modifying it!) and the dialog displays the possible share targets... This way, I can remove my own app and even display them the way I want and I don't increase the intent size with the second method... Tested this now, it seems to have a limit somewhere near 500... Still your answer helps as it pointed out the size limit so I know now how to handle this. Thanks – prom85 Jan 13 '16 at 12:20
  • @prom85 just a though: 50 and 500 differs 10 times, does you have somewhere around `targetedShareIntents` (just curiosity)? Anyway, I'm glad, that you've figured out :-) – Konstantin Loginov Jan 13 '16 at 12:26
  • Don't understand what your question is... I don't use the `targetedShareIntents` anymore, I just use the base intent (NO cloning anymore). With the base intent I query the possible target apps and display them in my own dialog, then if the user selects a target, I just update the base intent in the sense of that I set the component on it so that it is directly shared with the target app... – prom85 Jan 13 '16 at 12:31