Search Unity

DownloadHandlerAssetBundle.GetContent returns null for StreamingAsset

Discussion in 'Asset Bundles' started by adamt, Feb 18, 2018.

  1. adamt

    adamt

    Joined:
    Apr 1, 2014
    Posts:
    114
    We're still struggling to migrate our project to AssetBundles. One big roadblock is the unpredictability of simply loading assets from the normal channels (UnityWebRequest.GetAssetBundle and DownloadHandlerAssetBundle.GetContent).

    Our game is a mobile game on iOS and Android, and we utilize StreamingAssets to pre-populate AssetBundles in our binary. The code to load, for example, the iOS manifest file, is simply this:

    Code (CSharp):
    1. string bundleName = "iOS";
    2.  
    3. string basePath = Path.Combine((Application.platform == RuntimePlatform.Android ? "" : "file://") + Application.streamingAssetsPath, PlatformUtils.RuntimePlatformIdentifier);
    4. string bundlePath = Path.Combine(basePath, bundleName);
    5.  
    6. // We generate a Hash128 instance with random values in each section so the system doesn't attempt to cache the platform manifest
    7. UnityWebRequest request = UnityWebRequest.GetAssetBundle(bundlePath, randomHash, 0);
    8. UnityWebRequestAsyncOperation operation = request.SendWebRequest();
    9.  
    10. yield return operation;
    11.  
    12. if(operation.webRequest.isHttpError || operation.webRequest.isNetworkError || !string.IsNullOrEmpty(operation.webRequest.error))
    13. {
    14.     throw new Exception(string.Format("Failed to load AssetBundle from StreamingAssets. bundleName={0}, bundlePath={1}, isHttpError={2}, isNetworkError={3}, responseCode={4}, error={5}",
    15.         bundleName, bundlePath, operation.webRequest.isHttpError, operation.webRequest.isNetworkError, operation.webRequest.responseCode, operation.webRequest.error));
    16. }
    17.  
    18. AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(operation.webRequest);
    19. if(bundle == null)
    20. {
    21.     throw new Exception(string.Format("Failed to load AssetBundle from StreamingAssets (GetContent returned null). bundleName={0}, hash={1}, bundlePath={2}",
    22.         bundleName, hash, bundlePath));
    23. }
    24.  
    Most of the time, that code runs just fine. However, we're still getting random errors that look like this:

    The docs on the GetContent method are woefully unhelpful on the method's function:

    Gee, thanks. Is there some reason the method returned null? Maybe the device is out of space when the download handler attempted to copy the file into cache? Was the cache not ready yet? Bueller?

    The painful part about this error is that it's one of the very first things that happens during our game, so if a user encounters it, the experience is almost certainly a bad one.

    I want to use AssetBundles. We actually reverted to using the Resources folder for everything in a build last week that resulted in 0 errors related to loading files. Unfortunately, we had to go back to AssetBundles because for some reason the same files packed as AssetBundles into the StreamingAssets folder is ~20 MB smaller than the way Unity packs assets in the Resources directory, and Google Play has other annoying "features" (expansion files, ugh) when binaries are above 100 MB.

    Any ideas?

    P.S. Why does Path.Combine not complain when I'm using its overload that accepts 3 path arguments in my IDE (Rider) but complains in-editor when Unity tries to compile my project? I'm guessing this might be solved by trying to update our .NET profile to 4.6?

    P.P.S. Any way to avoid that ugly '(Application.platform == RuntimePlatform.Android ? "" : "file://")' ternary statement? Android builds will prepend 'jar:file://' onto the beginning of Application.streamingAssetsPath, but none of the other platforms append the protocol. For some reason I thought there was work being done to make things "just work" cross-platform when using but I guess there are still special cases.
     
  2. galran

    galran

    Joined:
    Oct 3, 2017
    Posts:
    2
    We are experiencing the same behavior inside our UWP project running inside VS2017. Any solution in sight?
     
  3. adamt

    adamt

    Joined:
    Apr 1, 2014
    Posts:
    114
    Nope!
     
  4. alexander_unity434

    alexander_unity434

    Joined:
    Jan 22, 2019
    Posts:
    1
    @Reichert Has this been fixed? Curious if Assetbundles are production ready.
     
  5. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    2,226
    My guess is that the issue is with hash that you pass. I don't understand the comment about avoiding caching. If you don't want the caching, don't pass the extra arguments to GetAssetBundle(), only pass the URI.
    For testing purposes - remove the extra arguments, then it should download and load the bundle all the time.
    Since you check for error on UWR (the check for error property is redundant, it will be empty/null if both isNetworkError and isHttpError are false), the only remaining reason for null result is bundle load failure, meaning the bundle is either corrupted or there is a hash/version/crc missmatch.

    Create a System.Uri() object with the path and pass that object to UWR, we support System.Uri as an alternative to string URIs and the overloads taking System.Uri are also a bit faster.