目的:读取streamingassets下的文件中指定的一段字节
已知:文件中的起始位置,和需要读取的长度
1.android下读取
1.1 不能直接使用C#的FileStream,读取失败
1
2
3
|
var buffer = new byte [size]; FileStream stream = File.OpenRead(path); stream.Read(buffer , pos, size); |
报错:
IsolatedStorageException: Could not find a part of the path "/jar:file:/data/app/com.xxx.xxxx-1/base.apk!/assets/xxx.pack".
1.2 可以使用Unity原生接口与Android交互
主要过程:
Java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public class XXXPlugin extends UnityPlayerNativeActivity { protected AssetManager assetManager; protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); assetManager = getAssets(); } //返回字节数组 public byte [] LoadBytes(String path, int offset, int len) { //可以缓存起来,不需每次都open InputStream inputStream = assetManager.open(path); try { byte buf[] = new byte [len]; inputStream.reset(); //注意skip、read的可靠性 inputStream.skip(offset); inputStream.read(buf, 0 ,len) inputStream.close(); return buf; } catch (IOException e) { Log.v ( "unity" , e.getMessage()); } return null ; } } |
注意skip、read的可靠性,每次调用不一定能返回正确长度,可能需多次调用。
参考how-does-the-skip-method-in-inputstream-work
C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
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 ; } |
这种方法是在Java分配内存。
1.3 更灵活的方法,使用JNI,可以从C#传递指针到C++
在Android Studio中生成库libNativeLib.so文件,参考NativeReadBytes
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
#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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
//XXXPlugin.java public class XXXPlugin extends UnityPlayerNativeActivity { static { System.loadLibrary( "NativeLib" ); } @Override protected void onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); mainActivity = this ; //设置AssetManager, C++中要使用 NativeHelper.SetAssetManager(getAssets()); } } //NativeHelper.java public class NativeHelper { public static native void SetAssetManager(AssetManager assetManager); } |
C#
1
2
3
4
5
6
7
8
|
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.IOS下读取
可以直接在C#下读取,StreamAsseting只有读的权限,用OpenRead
1
2
3
4
|
byte [] bytes = new byte [len]; FileStream stream = File.OpenRead(path); stream.Seek(offset, SeekOrigin.Begin); stream.Read(bytes, 0, len); |
补充:unity中 在移动平台各种读写文件夹存路径整理 如 StreamingAssets 等文件夹 各个路径在各种平台的文件路径
1:Resources 文件夹 少用
1
2
3
4
5
6
|
//资源卸载 /* * Resources.UnloadAsset(obj):卸载非 GameObject类型的资源,会将内存中已加载资源及其克隆体卸载。 Destroy(obj):仅用于卸载GameObject类型的资源的克隆体。 DestroyImmediately(obj):卸载GameObject类型的资源,会将内存中已加载资源及其克隆体卸载,但该方法只能用在非编辑模式下,否则会报错提示改为DestroyImmediately(obj, true),然而编辑模式下使用该函数会连文件夹里的原始Asset一并删除。 */ |
2:StreamingAssets
在移动端也是只可读的不能写入数据 主要用来存放二进制文件。
1
2
3
4
5
6
7
8
|
//安卓下这两个文件夹路径相同 //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/ //Ios下 //Application.dataPath + "/Raw/" == @"file:///" + Application.streamingAssetsPath + "/" //Editor WIN //@"file:///" + Application.streamingAssetsPath + "/" //"file:///" + Application.dataPath + "/StreamingAssets" + "/" |
1
2
3
4
5
6
7
8
9
10
11
12
13
|
private string path = string.Empty; public string GetSAPath() { //安卓平台 加文件名 # 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
这个目录可读可写 一般存本地关卡等
用于存档 直接使用 打包之前是没有这个目录的,直到应用程序在手机上安装完毕才有这个目录。
该文件存在手机沙盒中,因为不能直接存放文件,
1.通过服务器直接下载保存到该位置,也可以通过Md5码比对下载更新新的资源
2.没有服务器的,只有间接通过文件流的方式从本地读取并写入Application.persistentDataPath文件下,然后再通过
Application.persistentDataPath来读取操作。
注:在Pc/Mac电脑 以及Android跟Ipad、ipone都可对文件进行任意操作,另外在IOS上该目录下的东西可以被iCloud自动备份。
1
|
Application.persistentDataPath + "/tempDic" , "testXml" |
对应存储路径
Windows应用商店应用程序:
1
2
3
|
application.persistentdatapath指向%userprofile%\appdata\local\packages\<productName>\localstate ios:application.persistentdatapath指向/var/mobile/containers/data/application/<guid>/documents |
android:application.persistentdatapath指向大多数设备上的/storage/emulated/0/android/data/<packagename>/文件(有些旧手机可能指向SD卡上的位置,如果存在),该路径使用android.content.context.getexternalfilesdir解析
4:Application.temporaryCachePath
来操作文件 同上但是 此属性用于返回一个临时数据的缓存目录(不会备份并且清空缓存会清掉)
以上为个人经验,希望能给大家一个参考,也希望大家多多支持服务器之家。如有错误或未考虑完全的地方,望不吝赐教。
原文链接:https://blog.csdn.net/hjflyblue/article/details/90115739