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不熟悉,安卓开发也不是很熟,所以踩了不少坑,大家可以直接使用我的代码来实现。
- 首先需要检查版本号。我们需要在服务器存放版本文件,安卓通过读取该文件来对比判断是否游戏需要更新。
代码附上。
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。
- 请求到版本号以后,我们来对比版本号。
我这里是将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;
}
- 对比完成后就需要进入下载逻辑。
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;
}
- 下载完成之后就需要提示安装。这里区分了安卓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","更新文件路径不存在");
}
}
- 代码到这里就结束了,剩下的是配置文件了。首先在AndroidManifest中加入以下权限。
然后在res/xml目录下添加provider_paths文件。
这样基本就齐活了,现在就可以从服务器获取安装包并且下载安装了。
本文地址:https://blog.csdn.net/zhangjinlong1234/article/details/108569607