unity android streamasset file

(1)

baidu    unity android streamasset file

(2)

unity使用文件流操作streamingassets下的文件

https://www.cnblogs.com/vsirWaiter/p/7941171.html

背景:

1、Unity第一次启动时将streamingassets下的首包资源拷贝到persistentDataPath目录下。

2、Unity-android平台上的的这种操作只能使用www去加载streamingassets下的文件,导致速度问题。

备注:

1、此方案需要您对安卓有一点点的了解。

解决方案:

1、在打包的时候,将streamingassets下的文件目录做一个记录,每一个打包进去的相对streamingassets路径下的文件都记录在一个files.txt里面,比如streamingassets下有文件bundles/testbundles.unity3d,这样记录进files.txt的名字就是bundles/testbundles.unity3d

2、安卓层读取文件流,在unity里用www读取files.txt,然后读取files.txt里的路径数据,循环。然后将路径传入android获取流,然后将流传入unity,然后unity用文件流再将此流写入persistentDataPath,这里的效率问题其实可以忽略的,unity的实质就是一个android的activity,虽然写流放在了C#,但对www加载的优化已经提高了很多倍了,安卓代码如下。

复制代码

        public byte[] LoadAB(String path)
        {
             InputStream inputStream = null ;
                 try {      
                      inputStream = assetManagerAndoird.open(path);
                  }
              catch (IOException e) {
                   Log.v ("unity", e.getMessage());
                  }
              return readtextbytes(inputStream);
        }

复制代码

3、第2条的代码,如果路径上指定的文件为空,大部分真机的app会直接crash,而不会主动去抛出异常。

4、针对第3条的优化,在OnCreate里获取根目录下的所有文件及文件夹,AssetManager assets = assets.list(""); 

然后使用递归获取到所有子目录下的所有文件,分别记录路径+文件名,并将信息记录到一个列表List<String> fileNameList = new ArrayList<String>()。

5、每次在读取文件时先判空fileNameList.contains(path),判断这个文件是否存在,如果存在则读取,如果不存在则不读取,不存在的时候需要返回0字节的流到unity,如果返回null,同样会发生崩溃,例如return new byte[0];,这样就能完全防止app发生崩溃。

 

好了,关于streamingassets文件夹下的文件的拷贝和加载就说到这里了。

(3)

关于Android/IOS里边读取StreamingAsset目录 文件流

https://blog.csdn.net/gy373499700/article/details/49249169

1.用Android接口来读取streamingAsset目录 可以提高效率,www方式效率不好 还耗内存。读取byte后直接CreateFromMemory 来创建bundle。其他模式还是CreateFromFile来创建效率更好,memory的方式耗内存,所以大文件还是要拷出来放在file目录 用 CreateFromFile来创建,IOS上全部用CreateFromFile。

public byte[] getFromAssetss(String fileName){ //android里边读取streamingAsset目录的文件
        try {
        //得到资源中的Raw数据流  
            InputStream in = getResources().getAssets().open(fileName);   
          
            //得到数据的大小  
            int length = in.available();         
          
            byte [] buffer = new byte[length];          
          
            //读取数据  
            in.read(buffer);           
            //依test.txt的编码类型选择合适的编码,如果不调整会乱码   
           // res = EncodingUtils.getString(buffer, "BIG5");   
              
            //关闭      
            in.close();  
            return  buffer;
        
        }
        catch (Exception e)
        {
            e.printStackTrace();
            return null;
        }     

}


关于文件读取速度 1 最快的是File目录下的File.readallbytes   其次用上边的接口读取StreamingAssets 目录 最后是WWW方式异步读取StreamingAssets 目录。

-----------------------------------------------------------------------------

上边是读取整个byte文件

2.下边方法是读StreamingAssets 文件流方式,读取的是byte的部分数据,注意的是native和java的交互的引用计数不得超过512 也就是一瞬间同时请求return的数据不能太多 不然内存的自动释放 反应不过来。解决的方法是把该段数据请求堆栈设为局部引用。在Unity里边如下使用:

for (int i=0; i<5000; i++) {
AndroidJNI.PushLocalFrame (0);
byte[] data = CenterGhome.getStreamFromAssetss ("test.x", 0, a1.Length);
AndroidJNI.PopLocalFrame (System.IntPtr.Zero);
}

------------------

   // HashMap<String, InputStream> filemap = new HashMap<String, InputStream>();
   // byte [] buffer = new byte[5*1024*1024];;
public byte [] getStreamFromAssetss(String fileName, int offset,int length){ //Android里边  not pathName
        try {
        /* InputStream in=null;
        if(!filemap.containsKey(fileName))
        {
        in = getResources().getAssets().open(fileName);   
        filemap.put(fileName, in);
        in.mark(10*1024*1024);//设置标记方便reset恢复原始位置
        }
        else
        {
        in = filemap.get(fileName);
        }*/
       


        InputStream in = getResources().getAssets().open(fileName);   
            //得到数据的大小  
            if(in!=null)
            {                    
            // int length0 = in.available();       
            byte []  buffer = new byte[length]; //这里new 好像比去unity里边copy data 快
           
            //  in.reset();//恢复原始位置
             in.skip(offset);
             int readCount = 0; // 已经成功读取的字节的个数
             while (readCount < length)
             {
             readCount += in.read(buffer, readCount, length - readCount);//必须要慢慢读 不然可能读不完全。
             }
            in.close();
            in=null;
            return  buffer;//一帧内请求不得超过512次数  。unity里边调用要设置局部引用AndroidJNI.PushLocalFrame (0);AndroidJNI.PopLocalFrame (System.IntPtr.Zero);不然超过512就要报错。
            }
        }
        catch (Exception e)
        {
        Log.d("Unityjar","load Stream failed:" +fileName+e.getMessage());
            return null;
        }  
        
        return null;
}

-------------------------------------------IOS--------------------------------------------------------------

ios下边的目录可以使用FIle.Read 直接读取,但是StreamAsseting目录 文件流只能使用myStream = File.OpenRead(myPath); 来读。原因是FileMode.Open有读写权限 而StreamAsseting只有读的权限 所以 用FIle.Open会出错:Access to the path "/..Raw/4.page" is denied.

Note that on some platforms it is not possible to directly access the StreamingAssets folder because there is no file system access in the web platforms, and because it is compressed into the .apk file on Android. On those platforms, a url will be returned, which can be used using the WWW class.

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

3.Native层通过JNI去读StreamingAsset资源:unity里边由于需要后台线程去加载资源,不能直接访问AndroidJni,然后就自己使用native层来读 使用.so库来加载:

Android.mk:

LOCAL_PATH := $(call my-dir)


include $(CLEAR_VARS)
LOCAL_MODULE    := dsnative
LOCAL_SRC_FILES := dsnative.cpp
LOCAL_SHARED_LIBRARIES += libandroid
LOCAL_LDLIBS += -llog -landroid -lEGL -lz

include $(BUILD_SHARED_LIBRARY)

------
Application.mk

APP_ABI := armeabi armeabi-v7a x86
APP_STL:=stlport_static


java里边调用初始化接口:
public native void  initreadFromAssets(AssetManager ass);// Setup activity layout

@Override protected void onCreate (Bundle savedInstanceState)

{System.loadLibrary("dsnative");initreadFromAssets(getAssets());}

c#里边使用native接口:

[DllImport("dsnative")]private static extern void getNativeStreamFromAssets(string filename,byte[] data, int offset,int length);
byte[] data=new byte[1024*1024];

getNativeStreamFromAssets("test.x",data, a1.Length,b1.Length);


JNI写好后 直接在eclipse里边build一下,就会产生lib文件夹 生成对应的dsnative.so文件

dsnative.cpp


    #include <jni.h>
    #include <sys/types.h>
    #include <stdlib.h>
    #include <android/asset_manager_jni.h>
    #include <android/asset_manager.h>
    #include <android/log.h>
    #include <stdio.h>
    #include <unistd.h>
    #include <dlfcn.h>
    #include <stdio.h>
    #include <fcntl.h>
     
    #define LOGD(...)  __android_log_print(ANDROID_LOG_DEBUG,"UnityNative",__VA_ARGS__) // 定义LOGD类型
    #define  LOGW(...)  __android_log_print(ANDROID_LOG_WARN,"UnityNative",__VA_ARGS__)
     
     
     
     
     
    extern "C" {
     
     
     
    float add(float x, float y)//test
    {
    //    LOGD("aaaaaaaaaaaaaa");
        return x + y;
    }
     
    static AAssetManager* mgr=NULL;
    void  Java_com_meiyu_dungeonstriker_DSActivity_initreadFromAssets(JNIEnv* env,jclass tis,jobject assetManager)
    {
         LOGW("init_DSActivity_initreadFromAssets");
         mgr = AAssetManager_fromJava(env, assetManager);
         if(mgr==NULL)
           {
             LOGW(" %s","AAssetManager==NULL");
              return ;
           }
     
    }
     
    void getNativeStreamFromOffsetAssets(char* fileName,char* data, int offset,int length,int dataoffset)
    {
             if(mgr==NULL)
             {
                 LOGW("mgr is null ");
                 return ;
             }
     
             AAsset* asset = AAssetManager_open(mgr, fileName,AASSET_MODE_UNKNOWN);
             if(asset==NULL)
             {
                 LOGW("asset is null ");
                 return ;
             }
             AAsset_seek(asset,offset,SEEK_SET);
             data = data+dataoffset;
             AAsset_read(asset, data, length);
             AAsset_close(asset);
     
    }
     
    void getNativeStreamFromAssets(char* fileName,char* data, int offset,int length)
    {
             if(mgr==NULL)
             {
                 LOGW("mgr is null ");
                 return ;
             }
     
             AAsset* asset = AAssetManager_open(mgr, fileName,AASSET_MODE_UNKNOWN);
             if(asset==NULL)
             {
                 LOGW("asset is null ");
                 return ;
             }
             AAsset_seek(asset,offset,SEEK_SET);
             AAsset_read(asset, data, length);
             AAsset_close(asset);
     
    }
     
    JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *jvm, void *reserved)
    {
        LOGW("JNI_OnLoad");
     
     
        JNIEnv* env = NULL;
    #ifdef __cplusplus
        if (jvm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
    #else
        if ((*jvm)->GetEnv(jvm,(void**) &env, JNI_VERSION_1_4) != JNI_OK)
    #endif
            {
                return -1;
            }
    #ifdef __cplusplus
            jclass DSActivity = env->FindClass("com/meiyu/dungeonstriker/DSActivity");
    #else
            jclass DSActivity = (*env)->FindClass(env,"com/meiyu/dungeonstriker/DSActivity");
    #endif
            if(DSActivity != NULL)
            {
    #ifdef __cplusplus
                jmethodID getSDPath = env->GetStaticMethodID( DSActivity, "getSDPath","()Ljava/lang/String;");
    #else
                jmethodID getSDPath = (*env)->GetStaticMethodID(env, DSActivity, "getSDPath","()Ljava/lang/String;");
    #endif
                jstring jstr = NULL;
    #ifdef __cplusplus
                jstr = (jstring)env->CallStaticObjectMethod(DSActivity, getSDPath);
    #else
                jstr = (jstring)(*env)->CallStaticObjectMethod(env, DSActivity, getSDPath);
    #endif
                const char* cstr = NULL;
    #ifdef __cplusplus
                cstr = env->GetStringUTFChars(jstr, 0);
    #else
                cstr = (*env)->GetStringUTFChars(env,jstr, 0);
    #endif
                if(cstr==NULL)
                {
                    LOGW("11getSDPath=NULL");
                }
                else
                {
                    //LOGW("g_ResPath=%s",g_ResPath);
                    //LOGW("cstr=%s",cstr);
                    LOGW("11getSDPath OK");
                }
     
            //    strcpy(g_ResPath,cstr);///
     
    #ifdef __cplusplus
                env->ReleaseStringUTFChars(jstr,cstr);
    #else
                (*env)->ReleaseStringUTFChars(env,jstr, cstr);
    #endif
                //LOGW(cstr);
                //__android_log_print(ANDROID_LOG_WARN,"Unity",(const char*)cstr);
            }
     
     
            return JNI_VERSION_1_4;
    }
    JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *jvm, void *reserved)
    {
        LOGW("JNI_OnUnload");
        return;
    }
    }
————————————————
版权声明:本文为CSDN博主「枫叶林GD」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/gy373499700/article/details/49249169

(4)

Unity3D研究院之Android同步方法读取streamingAssets(八十八)

http://www.xuanyusong.com/archives/4033

版本Unity5.3.3

Android 小米pad1

首先非常感谢 @守着阳光 同学在下面的留言。让我解决了一个大的谜团。。

 

开始我知道 StreamingAssets 路径是这个 path = “jar:file://” + Application.dataPath + “!/assets/”;

文档在这里: http://docs.unity3d.com/Manual/StreamingAssets.html

后来我知道了一个新API Application.streamingAssetsPath

Application.streamingAssetsPath 其实就等于 “jar:file://” + Application.dataPath + “!/assets/”;

然而问题就出现在这个路径上。我打印了一下LOG

Application.streamingAssetsPath = jar:file:///data/app/com.xxx.xxx-1.apk!/assets

Application.dataPath+”!assets” = /data/app/com.xxx.xxx-1.apk!assets

也就是说Application.streamingAssetsPath  多了一个   jar:file://

那么如果想在Android上同步方法AssetBundle.LoadFromFile 就得用 Application.dataPath+”!assets”这个路径。

从此这段代码就正常了。

C#

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

using UnityEngine;

using System.Collections;

 

public class NewBehaviourScript : MonoBehaviour {

 

public SpriteRenderer spriteRenderer;

void Start () {

 

// /data/app/com.xxx.xxx-1.apk!assets/yusong.unity3d

string path = Application.dataPath+"!assets/yusong.unity3d";

 

 

AssetBundle assetbundle = AssetBundle.LoadFromFile(path);

 

Sprite sprite = assetbundle.LoadAsset<Sprite>("0");

 

spriteRenderer.sprite =sprite;

}

 

}

Unity的坑啊~ 55555555555555555555

还有这个路径只能用来AssetBundle.LoadFromFile 。如果想用File类操作。 比如File.ReadAllText  或者 File.Exists  Directory.Exists 这样都是不行的。

———————————-!!从今天以后下面的代码已经可以作废了!!—————————

streamingAssets 这个目录在IOS下是可以同步读取的,但是在Android下必须用www来异步读取。。这就很恶心了~所以最近我就在想办法如何能在Android下也能同步读取。如下图所示,我把一个sprite打成assetbundle并且放在StreamingAssets目录下。

Snip20160428_1.pnguploading.4e448015.gif转存失败重新上传取消Unity3D研究院之Android同步方法读取streamingAssets(八十八) - 雨松MOMO程序研究院 - 1

assetbundle的压缩格式 ,我使用的是unity5.x的lz4方式。

C#

1

2

3

4

5

6

7

8

[MenuItem ("Assets/Build AssetBundles")]

static void BuildAllAssetBundles ()

{

BuildPipeline.BuildAssetBundles ("Assets/StreamingAssets",BuildAssetBundleOptions.ChunkBasedCompression,BuildTarget.Android);

 

AssetDatabase.SaveAssets ();

AssetDatabase.Refresh();

}

然后创建一个3D Sprite 在Hierarchy里 试图把这个ab里的sprite加载上去。

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

using UnityEngine;

using System.Collections;

 

public class NewBehaviourScript : MonoBehaviour {

 

public SpriteRenderer spriteRenderer;

void Start () {

//注释掉的代码是 unity自己的同步方式, 但是在Android上不行, 可是在IOS上可以

// AssetBundle assetbundle = AssetBundle.LoadFromFile(Application.streamingAssetsPath +"/yusong.unity3d");

//

// Sprite sprite = assetbundle.LoadAsset<Sprite>("0");

//

// spriteRenderer.sprite =sprite;

 

 

//以下代码通过JAVA代码来同步读取并且返回给unity

AndroidJavaClass m_AndroidJavaClass = new AndroidJavaClass("com.unity3d.player.UnityPlayer");

AndroidJavaObject m_AndroidJavaObject  = null;

if (m_AndroidJavaClass != null) {

m_AndroidJavaObject = m_AndroidJavaClass.GetStatic<AndroidJavaObject>("currentActivity");

}

byte[] s = m_AndroidJavaObject.Call<byte[]>("LoadAB","yusong.unity3d");

AssetBundle assetbundle = AssetBundle.LoadFromMemory(s);

 

Sprite sprite = assetbundle.LoadAsset<Sprite>("0");

spriteRenderer.sprite =sprite;

 

}

 

}

然后,把unity导出成android工程。。

Snip20160428_2.pnguploading.4e448015.gif转存失败重新上传取消Unity3D研究院之Android同步方法读取streamingAssets(八十八) - 雨松MOMO程序研究院 - 2

用eclipse打开刚刚导出的工程。找到UnityPlayerActivity.java类 添加如下代码

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

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

package com.yusong.momo;

 

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

 

import com.unity3d.player.*;

 

import android.app.Activity;

import android.content.res.AssetManager;

import android.content.res.Configuration;

import android.graphics.PixelFormat;

import android.os.Bundle;

import android.util.Log;

import android.view.KeyEvent;

import android.view.MotionEvent;

import android.view.View;

import android.view.Window;

import android.view.WindowManager;

 

public class UnityPlayerActivity extends Activity

{

protected UnityPlayer mUnityPlayer; // don't change the name of this variable; referenced from native code

 

protected AssetManager assetManager;

// Setup activity layout

@Override protected void onCreate (Bundle savedInstanceState)

{

requestWindowFeature(Window.FEATURE_NO_TITLE);

super.onCreate(savedInstanceState);

 

getWindow().setFormat(PixelFormat.RGBX_8888); // <--- This makes xperia play happy

 

mUnityPlayer = new UnityPlayer(this);

setContentView(mUnityPlayer);

mUnityPlayer.requestFocus();

assetManager = getAssets();

}

 

 

private byte[] readtextbytes(InputStream inputStream)

{

 

  ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

//长度这里暂时先写成1024

  byte buf[] = new byte [1024];

 

  int len;

 

  try {

 

   while ((len = inputStream.read(buf)) != -1) {

 

    outputStream.write(buf, 0, len);

 

   }

 

   outputStream.close();

 

   inputStream.close();

 

  } catch (IOException e) {

 

  }

  return outputStream.toByteArray();

}

 

//读取assetbund并且返回字节数组

public byte[] LoadAB(String path)

{

InputStream inputStream = null ;

  try {

 

   inputStream = assetManager.open(path);

 

  } catch (IOException e) {

 

   Log.v ("unity", e.getMessage());

 

  }

 

  return readtextbytes(inputStream);

}

// Quit Unity

@Override protected void onDestroy ()

{

mUnityPlayer.quit();

super.onDestroy();

}

 

// Pause Unity

@Override protected void onPause()

{

super.onPause();

mUnityPlayer.pause();

}

 

// Resume Unity

@Override protected void onResume()

{

super.onResume();

mUnityPlayer.resume();

}

 

// This ensures the layout will be correct.

@Override public void onConfigurationChanged(Configuration newConfig)

{

super.onConfigurationChanged(newConfig);

mUnityPlayer.configurationChanged(newConfig);

}

 

// Notify Unity of the focus change.

@Override public void onWindowFocusChanged(boolean hasFocus)

{

super.onWindowFocusChanged(hasFocus);

mUnityPlayer.windowFocusChanged(hasFocus);

}

 

// For some reason the multiple keyevent type is not supported by the ndk.

// Force event injection by overriding dispatchKeyEvent().

@Override public boolean dispatchKeyEvent(KeyEvent event)

{

if (event.getAction() == KeyEvent.ACTION_MULTIPLE)

return mUnityPlayer.injectEvent(event);

return super.dispatchKeyEvent(event);

}

 

// Pass any events not handled by (unfocused) views straight to UnityPlayer

@Override public boolean onKeyUp(int keyCode, KeyEvent event)     { return mUnityPlayer.injectEvent(event); }

@Override public boolean onKeyDown(int keyCode, KeyEvent event)   { return mUnityPlayer.injectEvent(event); }

@Override public boolean onTouchEvent(MotionEvent event)          { return mUnityPlayer.injectEvent(event); }

/*API12*/ public boolean onGenericMotionEvent(MotionEvent event)  { return mUnityPlayer.injectEvent(event); }

}

 

OK 大功告成, 我的sprite已经可以同步加载了。

如下图所示,那么实际上unity把已经把streamingAssets目录下的资源放在了android的assets目录下。

 

 

那么我们同步加载的原理也是利用Android的AssetManager这个类来读取的。

刚和同事讨论了一下,如果有效率的问题,我们可以在ndk里读取assets下的资源。 比如向这样~  c#  调用  ndk 读取完直接返回给c# 这样就可以不通过java这一层。。

http://www.cppblog.com/johndragon/archive/2012/12/28/196754.html

最后希望大家可以帮忙多多测试看看,谢谢啦~~

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值