当有大的版本更新时候我们会有重新下载安装包的需求
首先需要定义下载的状态和传入下载线程的请求状态,然后是下载的路径(可能还需要文件MD5码)以及安装路径等必要的变量,最后为了显示当前的下载进度、下载速度等,需要开启一个Coroutine或者在Update中不断查询当前下载状态,是否有异常,以及是否已经下载完毕。如果下载完毕,则校验文件,并开始安装。
using UnityEngine; using System; using System.Collections; using System.Threading; using System.IO; using System.Net; using System.Security.Cryptography; using System.Text; using System; namespace LuaFramework { public class VersionUpdateManager : Manager { public class RequestState { public const int BUFFER_SIZE = 1024; public byte[] BufferRead; public HttpWebRequest request; public HttpWebResponse response; public Stream responseStream; } public enum DownloadState { DOWNLOADING, FINISHED, FAILED } public delegate void ProgressCallback(long curr, long length, float rate, DownloadState state); public ProgressCallback progressCallback; string url = ""; string installPath = ""; string apkName = ""; string errorMsg = ""; private FileStream fileStream = null; private long length = 1; private long curr = 0; private long last = 0; private const float UpdateTime = 0.5f; private float rate = 0; private DownloadState downState = DownloadState.DOWNLOADING; void Awake() { Debug.LogError("版本更新检测初始化"); //暂时再此调用 DownloadApkAsync("安装包地址", "" , Application.persistentDataPath , "test.apk"); } public void DownloadApkAsync(string url, string md5, string path, string name) { this.url = url; this.installPath = path; this.apkName = name; this.errorMsg = ""; downState = DownloadState.DOWNLOADING; DownloadApkAsync(); } private void DownloadApkAsync() { if (string.IsNullOrEmpty(url)) return; if (string.IsNullOrEmpty(installPath)) return; if (string.IsNullOrEmpty(apkName)) return; string fullpath = installPath + "/" + apkName; IAsyncResult result = null; try { fileStream = new FileStream(fullpath, FileMode.Create, FileAccess.Write); Uri uri = new Uri(url); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri); request.Method = "GET"; RequestState requestState = new RequestState(); requestState.BufferRead = new byte[RequestState.BUFFER_SIZE]; requestState.request = request; curr = 0; length = 1; rate = 0.0f; downState = DownloadState.DOWNLOADING; result = (IAsyncResult)request.BeginGetResponse(new AsyncCallback(ResponeCallback), requestState); } catch (Exception e) { errorMsg = "Begin Create Exception!"; errorMsg += string.Format("Message:{0}", e.Message); StopDownload(result); downState = DownloadState.FAILED; } StartCoroutine(updateProgress()); } IEnumerator updateProgress() { while (curr <= length) { yield return new WaitForSeconds(UpdateTime); rate = (curr - last) / UpdateTime; last = curr; if (downState == DownloadState.FAILED) { Debug.LogError(errorMsg); if (fileStream != null) fileStream.Close(); if (progressCallback != null) progressCallback(curr, length, rate, DownloadState.FAILED); break; } if (progressCallback != null) progressCallback(curr, length, rate, DownloadState.DOWNLOADING); if (downState == DownloadState.FINISHED) { if (progressCallback != null) progressCallback(curr, length, rate, DownloadState.FINISHED); Debug.LogError("下载完成"); this.InstallApk(); break; } } } void StopDownload(IAsyncResult result) { if (result == null) return; RequestState requestState = (RequestState)result.AsyncState; requestState.request.Abort(); } void ResponeCallback(IAsyncResult result) { try { if (downState != DownloadState.FAILED) { RequestState requestState = (RequestState)result.AsyncState; HttpWebRequest request = requestState.request; requestState.response = (HttpWebResponse)request.EndGetResponse(result); Stream responseStream = requestState.response.GetResponseStream(); requestState.responseStream = responseStream; length = requestState.response.ContentLength; IAsyncResult readResult = responseStream.BeginRead(requestState.BufferRead, 0, RequestState.BUFFER_SIZE, new AsyncCallback(ReadCallback), requestState); return; } } catch (Exception e) { string msg = "ResponseCallback exception!\n"; msg += string.Format("Message:{0}", e.Message); StopDownload(result); errorMsg = msg; downState = DownloadState.FAILED; } } void ReadCallback(IAsyncResult result) { try { if (downState != DownloadState.FAILED) { RequestState requestState = (RequestState)result.AsyncState; Stream responseStream = requestState.responseStream; int read = responseStream.EndRead(result); if (read > 0) { fileStream.Write(requestState.BufferRead, 0, read); fileStream.Flush(); curr += read; IAsyncResult readResult = responseStream.BeginRead(requestState.BufferRead, 0, RequestState.BUFFER_SIZE, new AsyncCallback(ReadCallback), requestState); return; } else { Debug.Log("download end"); responseStream.Close(); fileStream.Close(); downState = DownloadState.FINISHED; Debug.LogError("下载完成111"); } } } catch (Exception e) { string msg = "ReadCallBack exception!"; msg += string.Format("Message:{0}", e.Message); StopDownload(result); downState = DownloadState.FAILED; } } private AndroidJavaObject _GetCurrentAndroidJavaObject() { AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); return jc.GetStatic<AndroidJavaObject>("currentActivity"); } public void InstallApk() { Debug.LogError("开始安装"); #if UNITY_ANDROID && !UNITY_EDITOR AndroidJavaObject jo = _GetCurrentAndroidJavaObject(); string path = Application.persistentDataPath + "/test.apk"; Debug.LogError("begin install" + path); bool b = jo.CallStatic<bool>("installAPK", new object[] { path }); if (b == true) { Debug.LogError("安装成功"); }else Debug.LogError("安装失败"); //using (AndroidJavaObject jo = new AndroidJavaObject("com.kevonyang.androidhelper.AndroidHelper")) //{ // if (jo == null) // { // Debug.LogError("VersionUpdater: Failed to get com.kevonyang.androidhelper.AndroidHelper"); // return; // } // using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) // { // if (jc == null) // { // Debug.LogError("VersionUpdater: Failed to get com.unity3d.player.UnityPlayer"); // return; // } // AndroidJavaObject m_jo = jc.GetStatic<AndroidJavaObject>("currentActivity"); // if (m_jo == null) // { // Debug.LogError("VersionUpdater: Failed to get currentActivity"); // return; // } // jo.CallStatic("InstallApk", m_jo, installPath, apkName); // } //} #endif } } }
在下载完毕后,需要写一个java类,并在里面调用安装接口。内容很简单,只需要简单的启动一个安装的Intent就可以了,随后就会出现系统提示,是否覆盖安装。至此,游戏内的下载及安装全部完成,等待覆盖安装完毕即可从新的客户端启动。
public static boolean installAPK(String apkPath) { File apkFile = new File(apkPath); if (apkFile.exists()) { Intent intent = new Intent(Intent.ACTION_VIEW); if (Build.VERSION.SDK_INT >= 24) { intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri contentUri = FileProvider.getUriForFile(UnityPlayer.currentActivity, UnityPlayer.currentActivity.getPackageName() + ".fileprovider", apkFile); intent.setDataAndType(contentUri, "application/vnd.android.package-archive"); } else { intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive"); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); } // if (UnityPlayer.currentActivity.getPackageManager().queryIntentActivities(intent, 0).size() > 0) { UnityPlayer.currentActivity.startActivity(intent); // } return true; } else { return false; } }
AndroidManifest文件添加权限
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.INTERNET" />