Bug 30293 - java.lang.reflect.InvocationTargetException is thrown when instantiating a ZipArchive from a Stream
Summary: java.lang.reflect.InvocationTargetException is thrown when instantiating a Zi...
Alias: None
Product: Android
Classification: Xamarin
Component: General ()
Version: 5.2
Hardware: Macintosh Mac OS
: --- normal
Target Milestone: ---
Assignee: Jonathan Pryor
Depends on:
Reported: 2015-05-21 03:09 UTC by Adam Hartley [MSFT]
Modified: 2015-05-27 12:48 UTC (History)
4 users (show)

Is this bug a regression?: ---
Last known good build:

Sample project (182.15 KB, application/zip)
2015-05-21 03:09 UTC, Adam Hartley [MSFT]

Notice (2018-05-24): bugzilla.xamarin.com is now in read-only mode.

Please join us on Visual Studio Developer Community and in the Xamarin and Mono organizations on GitHub to continue tracking issues. Bugzilla will remain available for reference in read-only mode. We will continue to work on open Bugzilla bugs, copy them to the new locations as needed for follow-up, and add the new items under Related Links.

Our sincere thanks to everyone who has contributed on this bug tracker over the years. Thanks also for your understanding as we make these adjustments and improvements for the future.

Please create a new report on Developer Community or GitHub with your current version information, steps to reproduce, and relevant error messages or log files if you are hitting an issue that looks similar to this resolved bug and you do not yet see a matching new report.

Related Links:

Description Adam Hartley [MSFT] 2015-05-21 03:09:53 UTC
Created attachment 11273 [details]
Sample project

## Overview

A Java.Lang.RuntimeException: java.lang.reflect.InvocationTargetException is thrown when instantiating a ZipArchive from a Stream.

## Steps to reproduce

1. Download the sample project (attached).
2. Run the Android project
See line 24 of MainActivity.cs for the relevant code.

## Expected result

Should not crash.

## Actual result

Java.Lang.RuntimeException: java.lang.reflect.InvocationTargetException
at --- End of managed exception stack trace ---
at java.lang.RuntimeException: java.lang.reflect.InvocationTargetException
at at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:698)
at Caused by: java.lang.reflect.InvocationTargetException
at at java.lang.reflect.Method.invoke(Native Method)
at at java.lang.reflect.Method.invoke(Method.java:372)
at at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:903)
at ... 1 more
at Caused by: md52ce486a14f4bcd95899665e9d932190b.JavaProxyThrowable: System.IO.InvalidDataException: The contents of the stream are not in the zip archive format.
at at System.IO.Compression.ZipArchive.CreateZip (System.IO.Stream,System.IO.Compression.ZipArchiveMode) <IL 0x000b9, 0x0038b>
at at System.IO.Compression.ZipArchive..ctor (System.IO.Stream,System.IO.Compression.ZipArchiveMode) <IL 0x00028, 0x000d7>
at ZipCrash.Droid.MainActivity.OnCreate (Android.OS.Bundle) [0x00023] in /Users/Adam/Downloads/ZipCrash/Droid/MainActivity.cs:29
at Android.App.Activity.n_OnCreate_Landroid_os_Bundle_ (intptr,intptr,intptr) [0x00011] in /Users/builder/data/lanes/1780/3518c4ce/source/monodroid/src/Mono.Android/platforms/android-21/src/generated/Android.App.Activity.cs:2707
at at (wrapper dynamic-method) object.1739740d-6728-43c2-8c45-8ef00a08024a (intptr,intptr,intptr) <IL 0x00017, 0x0002b>
at at md50e099cb01f0a899b59c1a399b2b37919.MainActivity.n_onCreate(Native Method)
at at md50e099cb01f0a899b59c1a399b2b37919.MainActivity.onCreate(MainActivity.java:28)
at at android.app.Activity.performCreate(Activity.java:5990)
at at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1106)
at at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2278)
at at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2387)
at at android.app.ActivityThread.access$800(ActivityThread.java:151)
at at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1303)
at at android.os.Handler.dispatchMessage(Handler.java:102)
at at android.os.Looper.loop(Looper.java:135)
at at android.app.ActivityThread.main(ActivityThread.java:5254)
at ... 4 more 

## Notes

It seems to work if copied into a MemoryStream. See line 25-28 of MainActivity.cs for workaround.
Comment 1 Jonathan Pryor 2015-05-27 12:48:37 UTC
The exception is thrown from here:


Which unfortunately doesn't provide the inner exception. Some investigation later, and we see that the originally thrown exception is an ArgumentException, thrown from here:


i.e. the Stream *must* return true from Stream.CanSeek and Stream.CanRead, which isn't the case for the Android.Runtime.InputStreamInvoker that Assets.Open() returns -- InputStreamInvoker.CanSeek is false, because java.io.InputStream isn't seekable:


Some additional investigation shows that there is a way to seek files in Java via FileChannel.position(long):


There's just one problem here, at least as it relates to Assets: AssetManager.open() at runtime is returning an android.content.res.AssetManager.AssetInputStream:


AssetManager.AssetInputStream, meanwhile, doesn't provide a FileChannel, and thus (afaict) provides no way to explicitly set the file position. Consequently, it's not seekable. Consequently, this can't work.

Unfortunately, this is by design: ZipArchive requires a seekable Stream, and Assets.Open() can't provide one.

WORKAROUND: Don't use @(AndroidAsset). Use @(EmbeddedResource).


Assembly.GetManifestResourceStream() *does* return a Stream that is seekable and readable, and thus can be used with ZipArchive without error.

*Furthermore*, @(EmbeddedResource)  is compatible with all .NET environments -- .NET, Mono, Xamarin.iOS, Xamarin.Mac, etc. -- and thus promotes greater code and data sharing by reducing use of Android-specific concepts such as @(AndroidAsset). @(EmbeddedResource) is also faster, as there's no cross-VM overhead involved in reading the data.