在之前使用ASE解密ab包时,使用了FileStream
这个API,在安卓平台是无法读取的。这是因为安卓平台将streamAssets文件夹下的文件都压缩成了jar包。
当安卓平台下载大版本apk时,需要将StreamAssets目录下的ab包解压到PersistentDatapath再进行解密使用。
解压的流程是:
先读取Resource文件夹下的ABMD5配置文件,获得需要解压的ab包的信息。
验证persistentDatapath下的解压路径,看看有没有已经解压过的ab包,对比一下这些解压过的包有没有被修改,最后将需要解压的ab包名都放到一个list当中。
最后开启一个协程将这些包进行解压,解压利用的核心API是UnityWebRequest.Get
,和我们下载热更包一样,这个API也可以处理jar包路径。
我们之前在安卓端没有加密的时候,使用的是AssetBundle.LoadFromFile
,这个API也不用关心jar包问题。
修改HotPatchManger 对HotPatchManager
进行一些修改,添加一些全局变量和方法
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 146 147 148 149 150 151 public class HotPatchManager : SingletonPattern <HotPatchManager >{ private string m_UnpackPath = Application.persistentDataPath + "/Origin" ; private List<string > m_UnpackedList = new List<string >(); private Dictionary<string , ABMD5Base> m_PackedMd5 = new Dictionary<string , ABMD5Base>(); private bool m_IsStartUnpack = false ; public bool IsStartUnpack => m_IsStartUnpack; private float m_UnpackSumSize; public float UnpackSumSize => m_UnpackSumSize; private float m_AlreadyUnpackSize; public float AlreadyUnpackSize => m_AlreadyUnpackSize; public void Init (MonoBehaviour mono ) { m_Mono = mono; ReadMD5(); } void ReadMD5 () { m_PackedMd5.Clear(); TextAsset md5 = Resources.Load<TextAsset>("ABMD5" ); if (md5 == null ) { Debug.LogError("未读取到本地ABMD5" ); return ; } using (MemoryStream ms = new MemoryStream(md5.bytes)) { BinaryFormatter bf = new BinaryFormatter(); ABMD5 aBMD5 = bf.Deserialize(ms) as ABMD5; foreach (ABMD5Base aBMD5Base in aBMD5.ABMD5List) { m_PackedMd5.Add(aBMD5Base.Name, aBMD5Base); } } } public bool ComputeUnpackFile () { #if UNITY_ANDROID if (!Directory.Exists(m_UnpackPath)) { Directory.CreateDirectory(m_UnpackPath); } m_UnpackedList.Clear(); foreach (string fileName in m_PackedMd5.Keys) { string filePath = m_UnpackPath + "/" + fileName; if (File.Exists(filePath)) { string md5 = MD5Manager.Instance.BuildFileMD5(filePath); if (m_PackedMd5[fileName].MD5 != md5) { m_UnpackedList.Add(fileName); } } else { m_UnpackedList.Add(fileName); } } foreach (string fileName in m_UnpackedList) { if (m_PackedMd5.ContainsKey(fileName)) { m_UnpackSumSize += m_PackedMd5[fileName].Size; } } return m_UnpackedList.Count > 0 ; #else return false ; #endif } public float GetUnpackProgress () { return m_AlreadyUnpackSize / m_UnpackSumSize; } public void StartUnpackFile (Action callback ) { m_IsStartUnpack = true ; m_Mono.StartCoroutine(UnpackToPersistantDatapath(callback)); } IEnumerator UnpackToPersistantDatapath (Action callback ) { foreach (string fileName in m_UnpackedList) { UnityWebRequest unityWebRequest = UnityWebRequest.Get(Application.streamingAssetsPath + "/" + fileName); unityWebRequest.timeout = 30 ; yield return unityWebRequest.SendWebRequest(); if (unityWebRequest.result == UnityWebRequest.Result.ConnectionError || unityWebRequest.result == UnityWebRequest.Result.DataProcessingError || unityWebRequest.result == UnityWebRequest.Result.ProtocolError) { Debug.Log("Unpack Error" + unityWebRequest.error); } else { byte [] bytes = unityWebRequest.downloadHandler.data; FileTool.CreateFile(m_UnpackPath + "/" + fileName, bytes ); } if (m_PackedMd5.ContainsKey(fileName)) { m_AlreadyUnpackSize += m_PackedMd5[fileName].Size; } unityWebRequest.Dispose(); } callback?.Invoke(); m_IsStartUnpack = false ; } }
在BundleEditor
的ReadMD5Calc
方法中也读取了ABMD5.bytes文件,在ReadMD5Calc
方法中是使用FileStream
读取的
解压UI界面 修改HotFixWnd
的Awake
方法和OnUpdate
方法,这里我们加上宏,在编辑器环境下不解压
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 public override void OnAwake (params object [] args ) { m_SumTime = 0 ; m_Panel = GameObject.GetComponent<HotFixPanel>(); m_Panel.m_Image.fillAmount = 0 ; m_Panel.m_Text.text = string .Format("{0:F}M/S" , 0 ); HotPatchManager.Instance.ServerInfoError += ServerInfoError; HotPatchManager.Instance.ItemDownloadError += ItemError; #if UNITY_EDITOR StartOnFinish(); #else if (HotPatchManager.Instance.ComputeUnpackFile()) { m_Panel.m_ProcessText.text = "解压中......" ; HotPatchManager.Instance.StartUnpackFile(() => { HotFix(); m_SumTime = 0 ; }); } else { HotFix(); } #endif } public override void OnUpdate () { if (HotPatchManager.Instance.IsStartUnpack) { m_SumTime += Time.deltaTime; m_Panel.m_Image.fillAmount = HotPatchManager.Instance.GetUnpackProgress(); float speed = (HotPatchManager.Instance.AlreadyUnpackSize / 1024.0f ) / m_SumTime; m_Panel.m_Text.text = string .Format("{0:F}m/s" , speed); } if (HotPatchManager.Instance.IsStartDownload) { } }
可以根据需要给HotPatchManger
的解压阶段添加错误回调,这里省略了
修改加载路径 ab包解压后,就需要改变安卓的ab包加载路径了
修改AssetBundleManager
的ABLoadPath,加上宏,只在安卓平台下修改加载路径
1 2 3 4 5 6 7 8 9 10 11 protected string ABLoadPath { get { #if UNITY_ANDROID return Application.persistentDataPath + "/Origin/" ; #else return Application.streamingAssetsPath + "/" ; #endif } }
测试解压 将ResourceManager
的m_LoadFromAssetBundle
设为true。
选择Tools——打AB包,然后进入主工程文件夹,将AssetBundle——Android(这里使用的是安卓工程)下的ab包复制粘贴到——Assets——StreamingAssets文件夹下
打开wamp服务器,再打开游戏,就能看到解压过程,然后到persistentDataPath里面找到Origin文件夹,里面就有了解压后的AB包。
测试热更 可以尝试测试一下热更,要注意要重新打一下热更包,因为之前的热更包是没有加密过的。