首页 >Unity3D频道 >【Unity3D研究院之游戏开发】 > Unity3D研究院之Android同步方法读取streamingAssets(八十八)
2016
04-28

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

版本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”这个路径。

从此这段代码就正常了。

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

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

	[MenuItem ("Assets/Build AssetBundles")]
	static void BuildAllAssetBundles ()
	{
		BuildPipeline.BuildAssetBundles ("Assets/StreamingAssets",BuildAssetBundleOptions.ChunkBasedCompression,BuildTarget.Android);

		AssetDatabase.SaveAssets ();
		AssetDatabase.Refresh();
	}

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

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

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

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目录下。

Snip20160428_3

 

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

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

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

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

 

最后编辑:
作者:雨松MOMO
专注移动互联网,Unity3D游戏开发
捐 赠写博客不易,如果您想请我喝一杯星巴克的话?就进来看吧!

Unity3D研究院之Android同步方法读取streamingAssets(八十八)》有 77 条评论

  1. lvfy说:

    亲自测试(windows和android) 新版API, Application.streamingAssetsPath 可以使用。

  2. 荷包蛋饭特稀说:

    Unity5.3.4 亲测这样写也能用了AssetBundle.LoadFromFile(Application.streamingAssetsPath +”/yusong.unity3d”);

  3. 烏拉龜说:

    最近也在用直接读取apk里的ab来减轻把ab复制出去的麻烦,但有很大的问题,loadasset内容时,最少卡半分钟以上。。。这种读法效率这么差?

  4. Turing说:

    那么不能用File进行操作了,那怎样读取Manifest里面的内容啊?怎样获取依赖关系呢?

  5. 小明说:

    Android 同步读取文件用 ZipFile

  6. 海游说:

    http://www.bkjia.com/Androidjc/972936.html像这种方式的 同步加载是不是 不科学。我测试是行不通的。 因为他把主线程卡死了,而WWW也用的是主线程,WWW就没机会执行了

  7. 刘麟说:

    AssetBundle.LoadFromFile 读取失败路径:Application.dataPath “!assets/”版本:5.4.1not find resouce:Background/background_4.assetbundle(/data/app/com.max.test-1/base.apk!assets/nico/background/background_4.assetbundle)

  8. 刘麟说:

    AssetBundle.LoadFromFile 读取失败路径:Application.dataPath + “!assets/”版本:5.4.1not find resouce:Background/background_4.assetbundle(/data/app/com.max.test-1/base.apk!assets/nico/background/background_4.assetbundle)

  9. 顺子说:

    AssetBundle.LoadFromFile 加载Application.persistentDataPath里的assetbundle路径是什么格式,我总是加载不成功

  10. 今朝愚公?珅佳说:

    “Application.streamingAssetsPath = jar:file:///data/app/com.xxx.xxx-1.apk!/assetsApplication.dataPath ”!assets” = /data/app/com.xxx.xxx-1.apk!assets区别在于有没有jar:file://”真的吗????难道不是在.apk!/assets和.apk!assets那个“/”的区别吗?

  11. 今朝愚公•珅佳说:

    “Application.streamingAssetsPath = jar:file:///data/app/com.xxx.xxx-1.apk!/assetsApplication.dataPath+”!assets” = /data/app/com.xxx.xxx-1.apk!assets区别在于有没有jar:file://”真的吗????难道不是在.apk!/assets和.apk!assets那个“/”的区别吗?

  12. Superboy说:

    bgm.unity3d 看下路径对不对 改成 string path = Application.streamingAssetsPath “/bgm”; 可以加载

  13. 谢志勇说:

    5.3.4版本调用LoadFromFile 路径里不能有感叹号直接用Application.dataPath /assets/xxx.ab

  14. 谢志勇说:

    5.3.4版本调用LoadFromFile 路径里不能有感叹号直接用Application.dataPath+/assets/xxx.ab

  15. 安卓安装包,安装在安卓上后streamingassets下的html文件怎样读取到

  16. 琉璃冰说:

    AssetBundle.LoadFromFile是不是5.3的API,我项目5.2.3,没有这个方法

  17. 张洁勇说:

    这个办法unity4.0可以用吗?

  18. 张洁勇说:

    请问 如果我第一个工程有stream asset. 打包apk. 下一次打包前工程删掉streamasset目录重新打一个apk包覆盖安装。原来的apk下streamasset的资源会被删除吗?

  19. 石叶说:

    string path = Application.dataPath “!/assets/bgm.unity3d”;AssetBundle asset = AssetBundle.LoadFromFile(path);这些代码我在安卓上测试根本不能去读。。。即便是txt文件,用LoadFromFile也无法读取,只能用www,不知道为什么。有代码例子吗

留下一个回复

你的email不会被公开。 必填项已用 * 标注。