Unity游戲內版本更新


最近研究了一下游戲內apk包更新的方法。

ios對於應用的管理比較嚴格,除非熱更新腳本,不太可能做到端內大版本包的更新。然而安卓端則沒有此限制。因此可以做到不跳到網頁或應用商店,就覆蓋更新apk包。

Unity最常用的腳本語言就是C#,不做斷點續傳的情況下,采用C#的網絡庫,還是比較簡單的。重點就是做好相應的異常處理。

C#用於網絡訪問的方法主要有兩種:WebRequest和封裝好的WebClient。為了將來能做更多的擴展,我采用更靈活的HttpWebRequest進行請求。為了不阻塞主線程,使用異步接口。

基本做法可參考官方文檔https://msdn.microsoft.com/zh-cn/library/system.net.httpwebrequest.begingetresponse(v=vs.110).aspx

然而我們知道,Unity4.X對於多線程的支持是很弱的,不推薦使用。因此,無法在下載線程中回調相應的事件。我將回調寫在主線程中,用Coroutine去輪詢當前的下載狀態和進度,並做相應的處理。

首先需要定義下載的狀態和傳入下載線程的請求狀態,然后是下載的路徑(可能還需要文件MD5碼)以及安裝路徑等必要的變量,最后為了顯示當前的下載進度、下載速度等,需要開啟一個Coroutine或者在Update中不斷查詢當前下載狀態,是否有異常,以及是否已經下載完畢。如果下載完畢,則校驗文件,並開始安裝。

  1 using UnityEngine;
  2 using System;
  3 using System.Collections;
  4 using System.Threading;
  5 using System.IO;
  6 using System.Net;
  7 using System.Security.Cryptography;
  8 using System.Text;
  9 using System;
 10 
 11 public class VersionUpdater : MonoBehaviour
 12 {
 13     public class RequestState
 14     {
 15         public const int BUFFER_SIZE = 1024;
 16         public byte[] BufferRead;
 17         public HttpWebRequest request;
 18         public HttpWebResponse response;
 19         public Stream responseStream;
 20     }
 21 
 22     public enum DownloadState
 23     {
 24         DOWNLOADING,
 25         FINISHED,
 26         FAILED
 27     }
 28 
 29     public delegate void ProgressCallback(long curr, long length, float rate, DownloadState state);
 30     public ProgressCallback progressCallback;
 31 
 32     string url = "";
 33     string installPath = "";
 34     string apkName = "";
 35     string errorMsg = "";
 36 
 37     private FileStream fileStream = null;
 38     private long length = 1;
 39     private long curr = 0;
 40     private long last = 0;
 41     private const float UpdateTime = 0.5f;
 42     private float rate = 0;
 43     private DownloadState downState = DownloadState.DOWNLOADING;
 44 
 45 
 46     public void DownloadApkAsync(string url, string md5, string path, string name)
 47     {
 48         this.url = url;
 49         this.installPath = path;
 50         this.apkName = name;
 51         this.errorMsg = "";
 52         downState = DownloadState.DOWNLOADING;
 53 
 54         DownloadApkAsync();
 55     }
 56 
 57     private void DownloadApkAsync()
 58     {
 59         if (string.IsNullOrEmpty(url)) return;
 60         if (string.IsNullOrEmpty(installPath)) return;
 61         if (string.IsNullOrEmpty(apkName)) return;
 62 
 63         string fullpath = installPath + "/" + apkName;
 64 
 65         IAsyncResult result = null;
 66         try
 67         {
 68             fileStream = new FileStream(fullpath, FileMode.Create, FileAccess.Write);
 69 
 70             Uri uri = new Uri(url);
 71             HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
 72 
 73             request.Method = "GET";
 74 
 75             RequestState requestState = new RequestState();
 76             requestState.BufferRead = new byte[RequestState.BUFFER_SIZE];
 77             requestState.request = request;
 78 
 79             curr = 0;
 80             length = 1;
 81             rate = 0.0f;
 82             downState = DownloadState.DOWNLOADING;
 83             result = (IAsyncResult)request.BeginGetResponse(new AsyncCallback(ResponeCallback), requestState);
 84         }
 85         catch (Exception e)
 86         {
 87             errorMsg = "Begin Create Exception!";
 88             errorMsg += string.Format("Message:{0}", e.Message);
 89             StopDownload(result);
 90             downState = DownloadState.FAILED;
 91         }
 92 
 93         StartCoroutine(updateProgress());
 94     }
 95 
 96     IEnumerator updateProgress()
 97     {
 98         while (curr <= length)
 99         {
100             yield return new WaitForSeconds(UpdateTime);
101 
102             rate = (curr - last) / UpdateTime;
103             last = curr;
104 
105             if (downState == DownloadState.FAILED)
106             {
107                 Debug.LogError(errorMsg);
108                 if (fileStream != null)
109                     fileStream.Close();
110                 if (progressCallback != null)
111                     progressCallback( curr, length, rate, DownloadState.FAILED);
112                 break;
113             }
114 
115             if (progressCallback != null)
116                 progressCallback( curr, length, rate, DownloadState.DOWNLOADING);
117 
118             if (downState == DownloadState.FINISHED)
119             {
120                 if (progressCallback != null)
121                     progressCallback( curr, length, rate, DownloadState.FINISHED);
122                 break;
123             }
124         }
125     }
126 
127     void StopDownload(IAsyncResult result)
128     {
129         if (result == null) return;
130         RequestState requestState = (RequestState)result.AsyncState;
131         requestState.request.Abort();
132     }
133 
134     void ResponeCallback(IAsyncResult result)
135     {
136         try
137         {
138             if (downState != DownloadState.FAILED)
139             {
140                 RequestState requestState = (RequestState)result.AsyncState;
141                 HttpWebRequest request = requestState.request;
142                 requestState.response = (HttpWebResponse)request.EndGetResponse(result);
143 
144                 Stream responseStream = requestState.response.GetResponseStream();
145                 requestState.responseStream = responseStream;
146 
147                 length = requestState.response.ContentLength;
148 
149                 IAsyncResult readResult = responseStream.BeginRead(requestState.BufferRead, 0, RequestState.BUFFER_SIZE, new AsyncCallback(ReadCallback), requestState);
150                 return;
151             }
152         }
153         catch (Exception e)
154         {
155             string msg = "ResponseCallback exception!\n";
156             msg += string.Format("Message:{0}", e.Message);
157             StopDownload(result);
158             errorMsg = msg;
159             downState = DownloadState.FAILED;
160         }
161     }
162 
163     void ReadCallback(IAsyncResult result)
164     {
165         try
166         {
167             if (downState != DownloadState.FAILED)
168             {
169                 RequestState requestState = (RequestState)result.AsyncState;
170                 Stream responseStream = requestState.responseStream;
171                 int read = responseStream.EndRead(result);
172                 if (read > 0)
173                 {
174                     fileStream.Write(requestState.BufferRead, 0, read);
175                     fileStream.Flush();
176                     curr += read;
177 
178                     IAsyncResult readResult = responseStream.BeginRead(requestState.BufferRead, 0, RequestState.BUFFER_SIZE, new AsyncCallback(ReadCallback), requestState);
179                     return;
180                 }
181                 else
182                 {
183                     Debug.Log("download end");
184                     responseStream.Close();
185                     fileStream.Close();
186 
187                     downState = DownloadState.FINISHED;
188                 }
189             }
190         }
191         catch (Exception e)
192         {
193             string msg = "ReadCallBack exception!";
194             msg += string.Format("Message:{0}", e.Message);
195             StopDownload(result);
196             errorMsg = msg;
197             downState = DownloadState.FAILED;
198         }
199     }
200 
201 
202     public void InstallApk()
203     {
204 #if UNITY_ANDROID && !UNITY_EDITOR
205         Debug.Log("begin install");
206         using (AndroidJavaObject jo = new AndroidJavaObject("com.kevonyang.androidhelper.AndroidHelper"))
207         {
208             if (jo == null)
209             {
210                 WMDebug.Debug.LogError("VersionUpdater: Failed to get com.kevonyang.androidhelper.AndroidHelper");
211                 return;
212             }
213             using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
214             {
215                 if (jc == null)
216                 {
217                     WMDebug.Debug.LogError("VersionUpdater: Failed to get com.unity3d.player.UnityPlayer");
218                     return;
219                 }
220                 AndroidJavaObject m_jo = jc.GetStatic<AndroidJavaObject>("currentActivity");
221                 if (m_jo == null)
222                 {
223                     WMDebug.Debug.LogError("VersionUpdater: Failed to get currentActivity");
224                     return;
225                 }
226 
227                 jo.CallStatic("InstallApk", m_jo, installPath, apkName);
228             }
229         }
230 #endif
231     }
232 }
View Code

在下載完畢后,需要寫一個java類,並在里面調用安裝接口。內容很簡單,只需要簡單的啟動一個安裝的Intent就可以了,隨后就會出現系統提示,是否覆蓋安裝。至此,游戲內的下載及安裝全部完成,等待覆蓋安裝完畢即可從新的客戶端啟動。

1     public static void InstallApk(Context context, String path, String name) {
2         Intent intent = new Intent(Intent.ACTION_VIEW);
3         intent.setDataAndType(Uri.fromFile(new File(path, name)), "application/vnd.android.package-archive");
4         context.startActivity(intent);
5     }
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM