zoukankan      html  css  js  c++  java
  • 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!
    ";
    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
  • 相关阅读:
    【MySQL】MySQL环境搭建
    【Linux】 工作中遇到并使用的命令
    k8s入门系列之guestbook快速部署
    k8s入门系列之扩展组件(二)kube-ui安装篇
    k8s入门系列之扩展组件(一)DNS安装篇
    k8s入门系列之集群安装篇
    爬虫抓取百度贴吧帖子内容
    爬虫抓取糗事百科的段子
    kibana使用操作部分
    elk实战分析nginx日志文档
  • 原文地址:https://www.cnblogs.com/kevonyang/p/5026690.html
Copyright © 2011-2022 走看看