Search Unity

  1. Click here to see what's on sale for the "Best of Super Sale" on the Asset Store
    Dismiss Notice
  2. Good news ✨ We have more Unite Now videos available for you to watch on-demand! Come check them out and ask our experts any questions!
    Dismiss Notice

DownloadHandlerAssetBundle.GetContent returns null for StreamingAsset

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

  1. adamt

    adamt

    Joined:
    Apr 1, 2014
    Posts:
    116
    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:
    116
    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,715
    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.
     
  6. Pdpalma

    Pdpalma

    Joined:
    Aug 14, 2014
    Posts:
    11
    Any solution for this?
     
  7. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    2,715
    Solution to what exactly? Please, be specific.
     
  8. Pdpalma

    Pdpalma

    Joined:
    Aug 14, 2014
    Posts:
    11
    About the UnityWebRequestAssetBundle.GetAssetBundle() that returns nulls

    This is my code. On UnityEditor works, but doesn't in Android 9

    Code (CSharp):
    1.  using (UnityWebRequest www = UnityWebRequestAssetBundle.GetAssetBundle("my-asset-bundle-url"))
    2.             {
    3.                 yield return www.SendWebRequest();
    4.  
    5.                 if (www.isNetworkError || www.isHttpError)
    6.                 {
    7.                     Debug.Log(wr.error);
    8.                 }
    9.                 else
    10.                 {
    11.                     // Get downloaded asset bundle
    12.                     AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(www);
    13.  
    14.                 }
    15.             }
     
  9. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    2,715
    Are you downloading the same asset bundle as in Editor?
    Asset bundles have to be build for each platform, bundle built for Editor is not compatible with Android.
    Check the logcat, likely it will have something printed about failure to load the bundle.
     
  10. Pdpalma

    Pdpalma

    Joined:
    Aug 14, 2014
    Posts:
    11
    I built the asset bundle for android. I've debugg the app in my phone and in this line AssetBundle bundle = DownloadHandlerAssetBundle.GetContent(www); returns null. There's no errors.
     
  11. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    2,715
    Check the logcat, there should be something printed there.
     
  12. Shefich

    Shefich

    Joined:
    May 23, 2013
    Posts:
    29
    Hi guys,

    I have the same experience with the DownloadHandlerAssetBundle.GetContent, it always returns null on some devices.

    First of all I have small number of devices, which couldn't load Asset Bundles from Streaming Assets folder. I can see in my console, that loading assets (AssetBundle.LoadFromFileAsync("assets path")) appears to be null.
    So I added assets to the server and added code in the app to load them. Everything works fine for all the Android phones.
    But some phones (I don't have them, so I can't provide Logcat) show null value, if I check DownloadHandlerAssetBundle.GetContent.
    I even made more tests with WWW.LoadFromCacheOrDownload. It has the same experince. Most of devices work good and they are easily loading assets from the server. But some devices return null if I'm trying to load asset: www.assetBundle.

    No erros in Fabric, or Firebase Crashlytics, except NullReferenceException, if I'm trying to use assetBundle, which is currently null.
    No UnityWebRequest.isNetworkError or UnityWebRequest.isHttpError, no www.error.
    Tested with compressed and uncompressed assets bundles. Of course
    BuildTarget.Android there used.
     
    Last edited: Jun 19, 2020
  13. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    2,715
    Are issues on specific phone models or not?
    Are you using Asset bundle caching?
     
  14. casmap

    casmap

    Joined:
    Jan 23, 2015
    Posts:
    5
    i need solution for my problem: i clear cache and re-open app without internet connection. How do i save and load asset bundle from local file?
     
    Last edited: Aug 6, 2020
  15. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    2,715
    You can pass URI to local file to UnityWebRequest or you can use AssetBundle.LoadFromFileAsync.
     
  16. casmap

    casmap

    Joined:
    Jan 23, 2015
    Posts:
    5
    I mean when i downloaded Asset Bundle successfully i want to write it into local file. Because i have problem if user clear cache and re-open app without internet connection
     
  17. Aurimas-Cernius

    Aurimas-Cernius

    Unity Technologies

    Joined:
    Jul 31, 2013
    Posts:
    2,715
    The it's better to download it as file (DownloadHandlerFile) and then load it.
     
    casmap likes this.
  18. casmap

    casmap

    Joined:
    Jan 23, 2015
    Posts:
    5
    Thank you very much for your suggestion
     
  19. Shefich

    Shefich

    Joined:
    May 23, 2013
    Posts:
    29
    Not the specific phone models and no specific Android versions.
    No caching. Everytime I'm loading new asset, I'm deleting current or cached in memory version. However it happens mostly on application startup and there's no cached in memory assets.

    Just simple code:
    Code (CSharp):
    1. AssetBundleCreateRequest bundleReq = AssetBundle.LoadFromFileAsync(GetBundlesAddress("asset"));
    After this code there's check for request == null or assetbundle inside == null. And on some devices I receive logs, that some of these are null.

    Code to return AssetBundle address:

    Code (CSharp):
    1. string GetBundlesAddress (string assetToLoad)
    2.     {
    3.         string bundlesFolder = "";
    4.         if (Application.platform == RuntimePlatform.IPhonePlayer)
    5.         {
    6.             bundlesFolder = "iosbundles";
    7.         } else if (Application.platform == RuntimePlatform.Android)
    8.         {
    9.             bundlesFolder = "androidbundles";
    10.         } else
    11.         {
    12.             if (Directory.Exists ("Assets/StreamingAssets/iosbundles"))
    13.             {
    14.                 bundlesFolder = "iosbundles";
    15.             } else if (Directory.Exists ("Assets/StreamingAssets/androidbundles"))
    16.             {
    17.                 bundlesFolder = "androidbundles";
    18.             } else
    19.             {
    20.                 Debug.LogWarning ("NO ASSET BUNDLES!!!");
    21.             }
    22.         }
    23.         return Path.Combine(Application.streamingAssetsPath, bundlesFolder, assetToLoad);
    24.     }
     
unityunity