欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Unity实现安卓游戏自动下载更新、安装。

程序员文章站 2022-03-12 08:16:08
很久没有写博客了,最近公司要求游戏要做自动更新功能。所以今天来总结一下,过程种踩的种种神坑。有些小伙伴要说了,现在Unity不是都用热更吗?资源用AB包,代码可以用lua,或者ILRunTime?没错,目前主流Unity游戏更新解决方案,是这样的。但是由于我们的游戏包体很小只有25M左右,而且属于小游戏,上线后不会长期高频的迭代,只会修复修复bug,所以没有考虑采用热更的方式。我是Unity开发者,对于Java不熟悉,安卓开发也不是很熟,所以踩了不少坑,大家可以直接使用我的代码来实现。首先需要检查版本...

很久没有写博客了,最近公司要求游戏要做自动更新功能。所以今天来总结一下,过程种踩的种种神坑。有些小伙伴要说了,现在Unity不是都用热更吗?资源用AB包,代码可以用lua,或者ILRunTime?没错,目前主流Unity游戏更新解决方案,是这样的。但是由于我们的游戏包体很小只有25M左右,而且属于小游戏,上线后不会长期高频的迭代,只会修复修复bug,所以没有考虑采用热更的方式。
我是Unity开发者,对于Java不熟悉,安卓开发也不是很熟,所以踩了不少坑,大家可以直接使用我的代码来实现。

  1. 首先需要检查版本号。我们需要在服务器存放版本文件,安卓通过读取该文件来对比判断是否游戏需要更新。
    代码附上。
private static String getVersion() throws IOException, JSONException {
        URL url = new URL("这里填写服务器版本文件的Url");
        HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
        httpURLConnection.setRequestMethod("GET");
        httpURLConnection.setDoInput(true);
        httpURLConnection.setDoOutput(false);
        httpURLConnection.setReadTimeout(8 * 1000);
        httpURLConnection.connect();
        if (httpURLConnection.getResponseCode() == 200) {
            // 获取返回的数据
            String result = readResourceAsString(httpURLConnection.getInputStream());
            //对json数据进行解析
            JSONObject jsonObject = new JSONObject(result);
            String strings = jsonObject.getString("version");
            Log.d("Unity", "请求成功,result--->" + result);
            return strings;
        } else {
            Log.d("Unity", "请求失败:" + httpURLConnection.getResponseCode());
        }
        return null;
    }
    
	/**
     * 读取数据流为字符串
     */
    private static String readResourceAsString(InputStream in) throws IOException {
        StringBuilder builder = new StringBuilder();
//        InputStream in = resource.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(in);
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String str ;
        while ((str = bufferedReader.readLine())!=null){
            builder.append(str);
        }
        //关闭流
        bufferedReader.close();
        inputStreamReader.close();
        in.close();
        return builder.toString();
    }

这里,踩到了第一个坑。GET方法,一定要设置 httpURLConnection.setDoOutput(false); 否则即便你setRequestMethod写GET,他依然会发送POST。

  1. 请求到版本号以后,我们来对比版本号。
    我这里是将Unity中的persistentDataPath路径传递过来了,会将下载的安装包存放到该路径下,顺便提一下,我对比版本号的方法,是判断只要版本号与服务器的不相同就下载更新。这样可以通过配置服务器版本文件来更新,或者回退版本。

public static void CheckUpdate(String persistentDataPath) throws IOException, JSONException {
        Log.d("Unity", "---> 进入版本更新逻辑");
        PersistentDataPath = persistentDataPath;
        String version = getVersion();
        if (version == null)
            return;
        ConfigVersion  = version;
        String nowVersion = getVerName(UnityPlayer.currentActivity);
        Log.d("Unity", "当前版本号1:" + nowVersion + " 服务器版本号2:" + version);
        if (compareVersion(nowVersion,version) == 0){
            Log.d("Unity", "无新版本需要更新");
        }else{
            Log.d("Unity", "有新版本需要更新");
            showUpdataDialog();
        }
    }

    /**
     * 对比版本号
     * @param version1
     * @param version2
     * @return
     */
    public static int compareVersion(String version1, String version2) {
        String[] s1 = version1.split("\\."); //通过\\将.进行转义
        String[] s2 = version2.split("\\.");
        int len1 = s1.length;
        int len2 = s2.length;
        int i, j;
        for (i = 0, j = 0; i < len1 && j < len2; i++, j++) {
            if (Integer.parseInt(s1[i]) > Integer.parseInt(s2[j])) {
                return 1;
            } else if (Integer.parseInt(s1[i]) < Integer.parseInt(s2[j])) {
                return -1;
            }
        }
        while (i < len1) {
            if (Integer.parseInt(s1[i]) != 0) {
                return 1;
            }
            i++;
        }
        while (j < len2) {
            if (Integer.parseInt(s2[j]) != 0) {
                return -1;
            }
            j++;
        }
        return 0;
    }



    /**
     * 获取版本号名称
     *
     * @param context 上下文
     * @return
     */
    public static String getVerName(Context context) {
        String verName = "";
        try {
            verName = context.getPackageManager().
                    getPackageInfo(context.getPackageName(), 0).versionName;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return verName;
    }

  1. 对比完成后就需要进入下载逻辑。

private static void showUpdataDialog() {
        Log.d("Unity", "---> 创建对话框");
        AlertDialog.Builder builer = new AlertDialog.Builder(UnityPlayer.currentActivity) ;
        builer.setTitle("版本升级");
        builer.setMessage("软件更新");
        //当点确定按钮时从服务器上下载 新的apk 然后安装
        builer.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                downLoadApk();
            }
        });
        //当点取消按钮时不做任何举动
        builer.setNegativeButton("取消", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
            }
        });
        builer.create().show();
    }

    protected static void downLoadApk() {
        //进度条
        ApkDownLoadPath = PersistentDataPath +"/update.apk";
        Log.d("Unity","本地储存路径:" + ApkDownLoadPath );
        File file = new File(ApkDownLoadPath);
        if (file.exists()){
            Log.d("Unity","安装包存在,删除存在的安装包");
            file.delete();
        }
        final ProgressDialog pd;
        pd = new ProgressDialog(UnityPlayer.currentActivity);
        pd.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
        pd.setMessage("正在下载更新");
        pd.show();
        new Thread(){
            @Override
            public void run() {
                try {
                    File file = getFileFromServer("这里填写下载文件的Url", pd);
                    //安装APK
                    Log.d("Unity", "下载完成,准备安装");
                    pd.dismiss(); //结束掉进度条对话框
                    installAPK(ApkDownLoadPath);
                } catch (Exception e) {
                }
            }}.start();
    }
    
    public static File getFileFromServer(String path, ProgressDialog pd) throws Exception{
            URL url = new URL(path);
            HttpURLConnection conn = (HttpURLConnection) url.openConnection();
            conn.setConnectTimeout(5000);
            //获取到文件的大小
            pd.setMax(conn.getContentLength());
            InputStream is = conn.getInputStream();
            File file = new File(PersistentDataPath, "update.apk");
            FileOutputStream fos = new FileOutputStream(file);
            BufferedInputStream bis = new BufferedInputStream(is);
            byte[] buffer = new byte[1024];
            int len ;
            int total=0;
            while((len = bis.read(buffer))!=-1){
                fos.write(buffer, 0, len);
                total+= len;
                //获取当前下载量
                pd.setProgress(total);
            }
            fos.close();
            bis.close();
            is.close();
            return file;
    }
  1. 下载完成之后就需要提示安装。这里区分了安卓sdk版本在24以上需要请求安装权限。这里遇到了一个问题,下载后之后一直提示安装包解析失败。网上查找了很多原因,有说权限的,有说代码的。最终都没有解决,最后发现是因为版本号的问题,如果这里安装的版本号和当前应用的版本号相同,会提示解析错误,大家一定要注意,不要安装相同版本号的安装包。
public static void installAPK(String apkPath) {

        File apkFile = new File(apkPath);
        Log.d("Unity","安装包路径 " + apkPath +" 文件大小 " +apkFile.length());
        if (apkFile.exists()) {
            Intent intent = new Intent(Intent.ACTION_VIEW);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                Uri contentUri = FileProvider.getUriForFile(UnityPlayer.currentActivity, UnityPlayer.currentActivity.getPackageName()+".fileprovider", apkFile);
                Log.d("Unity","->>链接路径 " + contentUri.getPath()) ;
                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);
            //  }
        } else {
            Log.e("Unity","更新文件路径不存在");
        }
    }
  1. 代码到这里就结束了,剩下的是配置文件了。首先在AndroidManifest中加入以下权限。
    Unity实现安卓游戏自动下载更新、安装。
    然后在res/xml目录下添加provider_paths文件。
    Unity实现安卓游戏自动下载更新、安装。
    Unity实现安卓游戏自动下载更新、安装。
    这样基本就齐活了,现在就可以从服务器获取安装包并且下载安装了。Unity实现安卓游戏自动下载更新、安装。

Unity实现安卓游戏自动下载更新、安装。

本文地址:https://blog.csdn.net/zhangjinlong1234/article/details/108569607