How does unity use the file stream to read resources under streamingassets

Time:2022-5-13

Purpose: to read a section of bytes specified in the file under streamingassets

Known: the starting position in the file and the length to be read

1. Read under Android

1.1 FileStream of c# cannot be used directly, and reading failed


var buffer = new byte[size];
FileStream stream = File.OpenRead(path);
stream.Read(buffer , pos, size);

report errors:

IsolatedStorageException: Could not find a part of the path “/jar:file:/data/app/com.xxx.xxxx-1/base.apk!/assets/xxx.pack”.

1.2 you can use unity native interface to interact with Android

Main process:

Java

public class XXXPlugin extends UnityPlayerNativeActivity {
    protected AssetManager assetManager;
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		assetManager = getAssets();
    }
 
	//Return byte array
	public byte[] LoadBytes(String path,int offset,int len)
	{
         //It can be cached without opening it every time
         InputStream inputStream = assetManager.open(path);
         try {
                 byte buf[] = new byte[len];
                 inputStream.reset();
                 
                //Pay attention to the reliability of skip and read
                 inputStream.skip(offset);
                 inputStream.read(buf,0,len)
 
                 inputStream.close();
                 return buf;
    
			} 
		catch (IOException e) {
				Log.v ("unity", e.getMessage());
		}
		return null;
	}
 
}

Pay attention to the reliability of skip and read. Each call may not return the correct length and may need to be called many times.

Refer to how does the skip method in InputStream work

C#:


		
            public static byte[] read_streamingpath_bytes(string path,IntPtr ptr, int pos, int size) 
            {
                using (AndroidJavaClass cls = new AndroidJavaClass("com.XXX.XXXPlugin";) ) 
                {
                   AndroidJavaObject  m_AndroidJavaObject = cls.GetStatic<AndroidJavaObject>("mainActivity");
 
                    byte[] s = m_AndroidJavaObject.Call<byte[]>("LoadBytes", path, pos, size);
 
                    return s;
                }
                return null;
            }

This method is to allocate memory in Java.

1.3 more flexible method: using JNI, you can transfer the pointer from c# to C++

Generate library libnativelib in Android studio So file, refer to nativereadbytes

C++


#include "com_XXX_NativeHelper.h"
#include <android/asset_manager_jni.h>
#include <android/asset_manager.h>
#include <string>
#include <jni.h>
#include <stdint.h>
 
#ifdef __cplusplus
extern "C" {
#endif
 
static AAssetManager *assetManager = nullptr;
 
JNIEXPORT void JNICALL Java_com_XXX_NativeHelper_SetAssetManager
        (JNIEnv *env, jobject jobj, jobject jassetManager) {
    assetManager = AAssetManager_fromJava(env, jassetManager);
}
 
JNIEXPORT int32_t JNICALL ReadAssetsBytesWithOffset(uint32_t pathKey, char* fileName, unsigned char** result, int32_t offset, int32_t length){
    if(assetManager == nullptr){
        return -1;
    }
    AAsset* asset = asset = AAssetManager_open(assetManager, fileName, AASSET_MODE_UNKNOWN);
    if(asset == nullptr){
        return -1;
    }
    off_t size = AAsset_getLength(asset);
    if(size > 0){
        try {
            AAsset_seek(asset, offset, SEEK_SET);
            AAsset_read(asset, *result, length);
        }catch (std::bad_alloc){
            *result = nullptr;
            return -1;
        }
    }
    AAsset_close(asset);
    return (int32_t)length;
}
#ifdef __cplusplus
}
#endif

Java

//XXXPlugin.java
public class XXXPlugin extends UnityPlayerNativeActivity {
    static{
        System.loadLibrary("NativeLib");
    }
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  mainActivity = this;
  
  
        //Set assetmanager to be used in C + +
  NativeHelper.SetAssetManager(getAssets());
   }
 
}
 
//NativeHelper.java
public class NativeHelper {
    public static native void SetAssetManager(AssetManager assetManager);
}

C#


 public class ReadNativeByte
    {
 
#if UNITY_ANDROID
        [DllImport("NativeLib")]
        public static extern int ReadAssetsBytesWithOffset(uint pathKey,string name, ref IntPtr ptr, int offset, int length);
#endif
    }

2. Reading under IOS

It can be read directly in c# and streamasetting only has read permission. Use openread


byte[] bytes = new byte[len];
FileStream stream = File.OpenRead(path);
stream.Seek(offset, SeekOrigin.Begin);
stream.Read(bytes, 0, len);

Supplement: organize the storage paths of various read-write folders on the mobile platform in the unit, such as the file paths of various folders such as streamingassets on various platforms

1: Use fewer resources folders

//Resource unloading
    /*
     *  Resources. Unloadasset (obj): unloading non GameObject type resources will unload the loaded resources and their clones in memory.
        Destroy (obj): only used to unload clones of GameObject type resources.
        Destroy immediately (obj): unloading GameObject type resources will unload the loaded resources and their clones in memory. However, this method can only be used in non editing mode, otherwise an error will be reported and the prompt will be changed to destroy immediately (obj, true). However, using this function in editing mode will delete the original assets in the folder.
     */

2:StreamingAssets

In the mobile terminal, it is only readable and cannot write data. It is mainly used to store binary files.

//The paths of these two folders under Android are the same 
    //Application.streamingAssetsPath = jar:file:///data/app/com.xxx.xxx-1.apk!/assets/  == "jar:file://"+Application.dataPath+"!assets/"
    //Application.dataPath+"!assets/" = /data/app/com.xxx.xxx-1.apk!assets/
    //Under IOS
    //Application.dataPath + "/Raw/" == @"file:///" + Application.streamingAssetsPath + "/"
    //Editor WIN 
    //@"file:///" + Application.streamingAssetsPath + "/"
    //"file:///" + Application.dataPath + "/StreamingAssets" + "/"
private string path = string.Empty;
    public string GetSAPath()
    {
        //Android platform plus file name
#if UNITY_ANDROID && !UNITY_EDITOR
        path =  Application.streamingAssetsPath + "/"
#elif UNITY_IPHONE && !UNITY_EDITOR
        path = @"file:///" + Application.streamingAssetsPath + "/";
#elif UNITY_STANDLONE_WIN||UNITY_EDITOR
       path =  @"file:///" + Application.streamingAssetsPath + "/";
#endif
        return path;
    }

3:Application.persistentDataPath

This directory is readable and writable. It is generally stored in local checkpoints, etc

This directory does not exist before packaging for direct use for archiving. It does not exist until the application is installed on the mobile phone.

The file is stored in the mobile phone sandbox because it cannot be stored directly,

1. Directly download and save to this location through the server, or download and update new resources through MD5 code comparison

2. If there is no server, it can only be read and written to the application indirectly through file stream Under the persistentdatapath file, and then through

Application. Persistent datapath to read the operation.

Note: files can be operated arbitrarily on PC / Mac, Android, iPad and iPhone. In addition, things in this directory on IOS can be backed up automatically by icloud.


Application.persistentDataPath + "/tempDic", "testXml"

Corresponding storage path

Windows store app:

application. Persistentdatapath points to% userprofile% \ appdata \ local \ packages \ < product name > \ localstate

ios:application. Persistentdatapath points to / var / mobile / containers / data / application / < guid > / documents

android:application. Persistent datapath points to the / storage / emulated / 0 / Android / data / < package name > / file on most devices (some old phones may point to the location on the SD card, if any). This path uses Android content. context. Getexternalfilesdir parsing

4:Application.temporaryCachePath

The operation of the file is the same as above, but this attribute is used to return a cache directory of temporary data (it will not be backed up and the cache will be cleared)

The above is my personal experience. I hope I can give you a reference, and I hope you can support developpaer. If there are mistakes or not fully considered, please don’t hesitate to comment.