Unity2019.2.9 安卓调用安装apk,安卓全版本适用
安卓7的三星s7,redmi k20 pro 安卓10测试通过
找到了个老机器,安卓5.1的某为,安装后居然粉屏了,但等待一会后也弹出了安装界面,功能应该是没问题了。
首先确定包名,我用的是com.chengdu.sichuan。以免混淆。
apk地址在是qq的下载地址
https://qd.myapp.com/myapp/qqteam/QQ_JS/qqlite_4.0.0.1025_537062065.apk
备用。
Android Studio创建工程,详细步骤参考上一篇制作aar包教程
修改AndroidManife.xml
使用provider和添加一些权限
image.png
xml/filepaths的意思是在main/res目录下创建xml文件夹,创建filepaths.xml文件
image.png
完整filepaths.xml如下
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external_storage_root"
path="." />
<files-path
name="files-path"
path="." />
<cache-path
name="cache-path"
path="." />
<!--/storage/emulated/0/Android/data/...-->
<external-files-path
name="external_file_path"
path="." />
<!--代表app 外部存储区域根目录下的文件 Context.getExternalCacheDir目录下的目录-->
<external-cache-path
name="external_cache_path"
path="." />
<!--配置root-path。这样子可以读取到sd卡和一些应用分身的目录,否则微信分身保存的图片,就会导致 java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/999/tencent/MicroMsg/WeiXin/export1544062754693.jpg,在小米6的手机上微信分身有这个crash,华为没有
-->
<root-path
name="root-path"
path="" />
</paths>
完整Androidmanife.xml如下
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.chengdu.sichuan">
<application
android:allowBackup="true"
android:label="@string/app_name"
android:supportsRtl="true"
>
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
<meta-data android:name="unityplayer.UnityActivity" android:value="true"/>
</activity>
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="com.chengdu.sichuan.fileprovider"
android:grantUriPermissions="true"
android:exported="false">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/filepaths" />
</provider>
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REPLACE_EXISTING_PACKAGE"/>
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
</manifest>
FileProvider报红的话,回到MainActivity.java
import androidx.core.content.FileProvider;
提供方法InstallApkUnity,参数是apk保存的路径
image.png
完整如下
package com.chengdu.sichuan;
//import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.widget.Toast;
//import androidx.core.content.FileProvider;
import androidx.core.content.FileProvider;
import com.unity3d.player.UnityPlayer;
import com.unity3d.player.UnityPlayerActivity;
import java.io.File;
public class MainActivity extends UnityPlayerActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
}
public void InstallApkUnity(String apkPath){
UnityPlayer.UnitySendMessage("_gameController", "CallUnity", apkPath);
//这一行的目的是调用unity的方法,可以不要
File file = new File(apkPath);
Intent intent = new Intent(Intent.ACTION_VIEW);
if(Build.VERSION.SDK_INT>=24) { //Android 7.0及以上
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
Uri apkUri = FileProvider.getUriForFile(getApplicationContext(), UnityPlayer.currentActivity.getPackageName() +".fileprovider", file);//记住修改包名
//对目标应用临时授权该Uri所代表的文件
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
}else{
intent.setDataAndType(Uri.fromFile(file), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
this.startActivity(intent);
}
//显示提示信息
public void ShowToast(final String msg){
runOnUiThread(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(),msg,Toast.LENGTH_SHORT).show();
}
});
}
}
androidx.core是安卓10相关的类库
按住Ctrl点击core,左边就会导航到这个文件
unity需要这个文件,右键Show in Explorer
image.png
这个文件备用。
Make Project 一下,找到到aar包与AndroidManifest.xml文件
新建unity工程,包名保持一致com.chengdu.sichuan
image.pngPlugins下创建Android/lib放入刚刚找到的classes.jar
放入aar包与AndroidManifest.xml文件
不要忘了打开aar文件,将里面的classes.jar里的BuildCongfig.class删除,关闭bandzip保存
创建Text用来显示内容
image.png
GameMama.cs内容如下
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEngine;
using UnityEngine.UI;
public class GameMana : MonoBehaviour
{
HTTPDownLoad downLoad;
AndroidJavaObject currentActivity;
public Text text;
private string action = "";
private void Start()
{
if (Application.platform == RuntimePlatform.Android)
{
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
}
string s = "https://qd.myapp.com/myapp/qqteam/QQ_JS/qqlite_4.0.0.1025_537062065.apk";
DownLoadFile(s);
}
public void DownLoadFile(string url)//下载指定文件
{
action = "";
downLoad = new HTTPDownLoad(url, Application.persistentDataPath + "/download/", 5000);
downLoad.loadedEvent = ((string fm) =>
{
action = fm;
Debug.Log(" 下载完成" + fm);
});
}
private void Update()
{
if (!string.IsNullOrEmpty(action))
{
if (currentActivity != null)
currentActivity.Call("InstallApkUnity", action);
action = "";
}
}
public void CallUnity(string s)
{
text.text = s;
}
}
HTTPDownLoad.cs内容如下
using System;
using System.Net;
using System.IO;
using System.Threading;
using UnityEngine;
/// <summary>
/// 文件下载
/// </summary>
public class HTTPDownLoad
{
public delegate void OnDownLoadedEvent(string fm);
public OnDownLoadedEvent loadedEvent;
private string postfix = ".temp";//临时文件后缀名
// 下载进度
public float Progress
{
get;
private set;
}
// 状态 0 正在下载 1 下载完成 -1 下载出错
public int Status
{
get;
private set;
}
// 错误信息
public string Error
{
get;
set;
}
// 总长度
public long TotalLength
{
get;
private set;
}
// 保存路径
private string savePath;
// url地址
private string url;
// 超时时间
private int timeout;
// 子线程
private Thread thread;
// 子线程是否停止标志
public bool isStop;
/// <summary>
/// 构造函数
/// </summary>
/// <param name="url">url地址</param>
/// <param name="timeout">超时时间</param>
/// <param name="callback">回调函数</param>
public HTTPDownLoad(string _url, string _savePath, int _timeout)
{
savePath = _savePath;
url = _url;
timeout = _timeout;
DownLoad();
}
/// <summary>
/// 开启下载
/// </summary>
void DownLoad()
{
Status = 0;
isStop = false;
// 开启线程下载
thread = new Thread(StartDownLoad);
thread.IsBackground = true;
thread.Start();
}
public int poo = -1;
/// <summary>
/// 开始下载
/// </summary>
private void StartDownLoad()
{
try
{
if (!Directory.Exists(savePath))
{
Directory.CreateDirectory(savePath);
}
string fileName = url.Split('/')[url.Split('/').Length - 1];
string pathName = savePath + "/" + ErasePostfix(fileName) + postfix;
// 构建文件流
FileStream fs = new FileStream(pathName, FileMode.OpenOrCreate, FileAccess.Write);
// 文件当前长度
long fileLength = fs.Length;
// 文件总长度
TotalLength = GetDownLoadFileSize();
// Debug.LogFormat("fileLen:{0}", TotalLength);
if (fileLength < TotalLength)
{
// 没有下载完成
fs.Seek(fileLength, SeekOrigin.Begin);
// 发送请求开始下载
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.AddRange((int)fileLength);
request.Timeout = timeout;
// 读取文件内容
Stream stream = request.GetResponse().GetResponseStream();
if (stream.CanTimeout)
{
stream.ReadTimeout = timeout;
}
byte[] buff = new byte[4096];
int len = -1;
while ((len = stream.Read(buff, 0, buff.Length)) > 0)
{
if (isStop)
{
break;
}
fs.Write(buff, 0, len);
fileLength += len;
Progress = fileLength * 1.0f / TotalLength;
// Debug.Log(Progress);
}
stream.Close();
stream.Dispose();
}
else
{
Progress = 1;
}
fs.Close();
fs.Dispose();
// 标记下载完成
if (Progress == 1)
{
Status = 1;
string filepathName = savePath + "/" + fileName;
if (File.Exists(filepathName))//删除原来的
File.Delete(filepathName);
File.Move(pathName, filepathName);
loadedEvent?.Invoke(filepathName);
}
}
catch (Exception e)
{
Error = e.Message;
Status = -1;
Debug.Log(Error);
}
}
/// <summary>
/// 获取下载的文件大小
/// </summary>
/// <returns>文件大小</returns>
public long GetDownLoadFileSize()
{
HttpWebRequest request = WebRequest.Create(url) as HttpWebRequest;
request.Method = "HEAD";
request.Timeout = timeout;
HttpWebResponse response = request.GetResponse() as HttpWebResponse;
return response.ContentLength;
}
/// <summary>
/// 停止下载
/// </summary>
public void Close()
{
isStop = true;
}
private string ErasePostfix(string filePath)
{
return filePath.Substring(0, filePath.LastIndexOf('.'));
}
}
image.png
下载完成后会调用InstallApkUnity方法来安装,安卓10验证通过。
安卓调用unity中的方法
public void CallUnity(string s)
{
text.text = s;
}
image.png
不要忘了在other Setting中设置读写外部存储
image.png
完整的安卓工程与unity资源包如下地址
链接:https://pan.baidu.com/s/106HfAKDCMj0ABPCLtDD2-g
提取码:3zt0