赞
踩
现在手机很多都可以用插拔式sd卡用来扩充存储大小,但这些外接设备也会引发一些未知的app运行问题,先看一下sd类型
可见不同sd卡对io速度也不同,所以对app的影响也又所有不同,
在此送上一份可信的数据来看下影响层面
上图可以看出资源下载在不同的存储位置,反应的加载资源卡住的比例
在外置sd比内置存储要高出1倍。
对此我们做了玩家友情提示和资源搬家功能,主要是让玩家知道资源放在内置存储更好,废话不多,上代码
1:GetExternalSDCardRootPath 这个方法用来获取外置外置sd卡的路径,如果没有外置sd卡就返回null,可以用这个字符串来比对你的资源下载路径如果存在sd卡路径字符,那么你的资源就在外置sd卡上面了
private static AndroidJavaObject GetReflectMethod(string className, string methodName) { AndroidJavaObject method; using (AndroidJavaClass Class = new AndroidJavaClass("java.lang.Class")) using (AndroidJavaObject classObject = Class.CallStatic<AndroidJavaObject>("forName", className)) { method = classObject.Call<AndroidJavaObject>("getDeclaredMethod", methodName, null); method.Call("setAccessible", true); } return method; } public static string GetExternalSDCardRootPath() { AndroidJavaClass buildVersion = new AndroidJavaClass("android.os.Build$VERSION"); if (buildVersion.GetStatic<int>("SDK_INT") >= 23) { AndroidJavaObject method_getExternalDirs = GetReflectMethod("android.os.Environment$UserEnvironment", "getExternalDirs"); AndroidJavaObject method_myUserId = GetReflectMethod("android.os.UserHandle", "myUserId"); AndroidJavaClass class_UserHandle = new AndroidJavaClass("android.os.UserHandle"); AndroidJavaObject mUserId = method_myUserId.Call<AndroidJavaObject>("invoke", class_UserHandle, null); int userId = mUserId.Call<int>("intValue"); AndroidJavaObject env = new AndroidJavaObject("android.os.Environment$UserEnvironment", userId); AndroidJavaObject rawfilesArray = method_getExternalDirs.Call<AndroidJavaObject>("invoke", env, null); AndroidJavaObject[] fileArray = AndroidJNIHelper.ConvertFromJNIArray<AndroidJavaObject[]>(rawfilesArray.GetRawObject()); AndroidJavaClass Environment = new AndroidJavaClass("android.os.Environment"); for (int i = 0; i < fileArray.Length; i++) { AndroidJavaObject file = fileArray[i]; bool removable = Environment.CallStatic<bool>("isExternalStorageRemovable", file); if (removable) { string path = file.Call<string>("getAbsolutePath"); return path; } } } else { using (AndroidJavaClass system = new AndroidJavaClass("java.lang.System")) { string secondaryStoragePath = system.CallStatic<string>("getenv", "SECONDARY_STORAGE"); if (secondaryStoragePath != null) { AndroidJavaObject file = new AndroidJavaObject("java.io.File", secondaryStoragePath); bool canRead = file.Call<bool>("canRead"); if (canRead) { return secondaryStoragePath; } } } } return null; }
2:GetInternalPersisteDataPath这个方法用来,百分百返回手机的内置存储路径并判定是否可读写。
public static string GetInternalPersisteDataPath() { string storagePath = GetStoragePathCS(false); if (!string.IsNullOrEmpty(storagePath)) { string forceInternalForcePath = storagePath + "/Android/Data/" + Application.identifier + "/files"; AndroidJavaObject file = new AndroidJavaObject("java.io.File", forceInternalForcePath); if (!file.Call<bool>("exists")) { file.Call<bool>("mkdirs"); } if (file.Call<bool>("canRead") && file.Call<bool>("canWrite")) { return file.Call<string>("getAbsolutePath"); } } return null; } private static string GetStoragePathCS(bool is_removale) { using (AndroidJavaClass playerCls = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) using (AndroidJavaObject activity = playerCls.GetStatic<AndroidJavaObject>("currentActivity")) using (AndroidJavaObject mStorageManager = activity.Call<AndroidJavaObject>("getSystemService", "storage")) { AndroidJavaObject method_getVolumeList = GetReflectMethod("android.os.storage.StorageManager", "getVolumeList"); AndroidJavaObject method_getPath = GetReflectMethod("android.os.storage.StorageVolume", "getPath"); AndroidJavaObject method_isRemovable = GetReflectMethod("android.os.storage.StorageVolume", "isRemovable"); AndroidJavaObject rawVolumeList = method_getVolumeList.Call<AndroidJavaObject>("invoke", mStorageManager, null); AndroidJavaObject[] VolumeList = AndroidJNIHelper.ConvertFromJNIArray<AndroidJavaObject[]>(rawVolumeList.GetRawObject()); for (int i = 0; i < VolumeList.Length; i++) { AndroidJavaObject storageVolumeElement = VolumeList[i]; string path = method_getPath.Call<string>("invoke", storageVolumeElement, null); AndroidJavaObject java_removable = method_isRemovable.Call<AndroidJavaObject>("invoke", storageVolumeElement, null); bool removable = java_removable.Call<bool>("booleanValue"); if (is_removale == removable) { return path; } } } return null; }
3:进行资源搬家逻辑,根据上面1,2的逻辑,符合搬家标准的,我们就进行搬家
CopyDirectoryWorker类提供了资源搬家的方法和进度,参数只需要原文件路径和新文件夹路径即可
using System; using System.Collections; using System.Collections.Generic; using UnityEngine; public class CopyDirectoryWorker { public delegate void GetProgressEventHandler(int Index, int Count, string SourceFile, string AimFile); public event GetProgressEventHandler OnGetProgress;//定义一个事件,在Copy文件夹时触发 public delegate void CopyFileEventHandler(long lngHad, long lngCount, string SourceFile ,string AimFile); public event CopyFileEventHandler OnCopyFile;//定义一个事件,在Copy文件时触发 public delegate void WorkOverEventHandler();//定义一个委托 public event WorkOverEventHandler WorkOvered;//定义一个事件,在Copy文件完成时触发 public bool isDelete = false; public CopyDirectoryWorker(bool _isDelete, string _sourceDirectory, string _aimDirectory) { isDelete = _isDelete; SourceDirectory = _sourceDirectory; AimDirectory = _aimDirectory; } public string SourceDirectory; public string AimDirectory; /// <summary> /// 递归拷贝文件,把源目录下所有文件和文件夹拷贝到目标目录 /// </summary> /// <param name="sourceDirectory">源路径</param> /// <param name="aimDirectory">目标路径</param> public void CopyFiles() { if (SourceDirectory.Substring(SourceDirectory.Length - 1, 1) == "/") { SourceDirectory = SourceDirectory.Substring(0, SourceDirectory.Length - 1); } if (!System.IO.Directory.Exists(AimDirectory)) { System.IO.Directory.CreateDirectory(AimDirectory); } if (!System.IO.Directory.Exists(SourceDirectory)) return ; sourceAllFile.Clear(); RecursionCopyFiles(SourceDirectory, AimDirectory); RunCopyFiles(); if (isDelete) { DeleteFile(); } if (WorkOvered!=null) WorkOvered(); } public void DeleteFile() { for (int i = 0; i < sourceAllFile.Count; i++) { if (sourceAllFile[i] != null && sourceAllFile[i].sFile != "") try { System.IO.File.Delete(sourceAllFile[i].sFile); } catch { } } sourceAllFile.Clear(); } class FileMove { public string sFile = ""; public string aFile = ""; public FileMove(string _sFile, string _aFile) { sFile = _sFile; aFile = _aFile; } } List<FileMove> sourceAllFile = new List<FileMove>(); /// <summary> /// 二进制读取文件,任何文件 /// </summary> private void CopyFile(string SourceFile, string AimFile) { byte[] bytTemp = new byte[4096];//字节数组 long lngHad = 0; long lngCount; int z = 5000; System.IO.FileStream fsSource = new System.IO.FileStream(SourceFile, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read, 4); System.IO.BinaryReader bRead = new System.IO.BinaryReader(fsSource); bRead.BaseStream.Seek(0, System.IO.SeekOrigin.Begin); if (fsSource.Position > 0) fsSource.Position = 0; lngCount = fsSource.Length; if (System.IO.File.Exists(AimFile)) System.IO.File.Delete(AimFile); System.IO.FileStream fsAim = new System.IO.FileStream(AimFile, System.IO.FileMode.CreateNew, System.IO.FileAccess.Write, System.IO.FileShare.Write, 4); System.IO.BinaryWriter bWrite = new System.IO.BinaryWriter(fsAim); while (z >= 4096) { z = (int)bRead.Read(bytTemp, 0, bytTemp.Length); bWrite.Write(bytTemp, 0, bytTemp.Length); lngHad += z; if (OnCopyFile!=null) { OnCopyFile(lngHad, lngCount, SourceFile, AimFile); } } //清理缓存区 bWrite.Flush(); bWrite.Close(); bRead.Close(); fsAim.Close(); fsSource.Close(); } /// <summary> /// 递归拷贝文件,把源目录下所有文件和文件夹拷贝到目标目录 /// </summary> /// <param name="sourceDirectory">源路径</param> /// <param name="aimDirectory">目标路径</param> private bool RecursionCopyFiles(string sourceDirectory, string aimDirectory) { if (!System.IO.Directory.Exists(sourceDirectory) & !System.IO.Directory.Exists(aimDirectory))// return false; try { string[] directories = System.IO.Directory.GetDirectories(sourceDirectory); if (directories.Length > 0) { foreach (string dir in directories) { string dirNew = dir.Replace(@"\", "/"); string dirTemp = dirNew.Substring(dirNew.LastIndexOf(@"/")); RecursionCopyFiles(dirNew, aimDirectory + dirTemp); } } if (!System.IO.Directory.Exists(aimDirectory)) { System.IO.Directory.CreateDirectory(aimDirectory); } string[] files = System.IO.Directory.GetFiles(sourceDirectory); if (files.Length > 0) { foreach (string file in files) { string sourceFile = file.Replace(@"\", "/"); sourceAllFile.Add(new FileMove(sourceFile, aimDirectory)); } } return true; } catch { return false; } } private void RunCopyFiles() { if (sourceAllFile.Count > 0) { for (int i = 0; i < sourceAllFile.Count; i++) { if (sourceAllFile[i] != null && sourceAllFile[i].sFile != "") { try { string aimFile = sourceAllFile[i].aFile + sourceAllFile[i].sFile.Substring(sourceAllFile[i].sFile.LastIndexOf(@"/")); if (OnGetProgress != null) { OnGetProgress(i + 1, sourceAllFile.Count, sourceAllFile[i].sFile, aimFile); } CopyFile(sourceAllFile[i].sFile, aimFile); } catch { } } } } } }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。