参考链接:Android Studio打包arr包并导入Unity中
参考链接:Unity中调用Android方法
参考链接:Unity中获取本地应用图标Icon
有的需求,App中需要一个应用商店,那就涉及到了应用下载、更新、安装、卸载、启动等。
一 、Android 中java代码,提供安装、启动、卸载、删除APK包功能:
package com.android.util;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
import java.io.File;
import androidx.core.content.FileProvider;
public class UseForUnity {
/// 设置一个 Activity 参数
private static Activity _unityActivity;
// 通过反射获取 Unity 的 Activity 的上下文
public static Activity getActivity() {
if (null == _unityActivity) {
try {
Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");
Activity activity = (Activity) classtype.getDeclaredField("currentActivity").get(classtype);
_unityActivity = activity;
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
return _unityActivity;
}
// 获取安卓版本
public static int getAndroidVersion() {
return android.os.Build.VERSION.SDK_INT;
}
// 判断应用是否安装成功
public boolean isAppInstalled(String packageName) {
return isAppExist(packageName);
}
// 判断应用是否卸载成功
public boolean isAppRemoved(String packageName) {
return !isAppExist(packageName);
}
// 判断应用是否存在
private boolean isAppExist(String packageName) {
PackageInfo packageInfo;
try {
packageInfo = getActivity().getPackageManager().getPackageInfo(packageName, 0);
} catch (PackageManager.NameNotFoundException e) {
packageInfo = null;
e.printStackTrace();
}
if (packageInfo == null) {
return false;
} else {
return true;
}
}
// 安装apk,兼容SDK24(安卓7.0)以上和以下
public void installPackageByName(String pakageName) {
File file = new File(pakageName);
if(!file.exists()){
return ;
}
// 文件uri
Uri uri = null;
Intent intent = new Intent(Intent.ACTION_VIEW);
if(android.os.Build.VERSION.SDK_INT>= android.os.Build.VERSION_CODES.N) {
// SDK版本>=24, 安卓7.0以上
uri = FileProvider.getUriForFile(getActivity(),"com.tvbox.yoostore"+".fileprovider",file);
// 添加读写权限
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}else{
// SDK版本<24, 安卓7.0以下
uri= Uri.fromFile(file);
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
intent.setDataAndType(uri, "application/vnd.android.package-archive");//设置intent的数据类型
getActivity().startActivity(intent);
}
// 启动apk
public void openPackageByName(String pakageName) {
PackageManager pm = getActivity().getPackageManager();
Intent i = pm.getLaunchIntentForPackage(pakageName);//获取启动的包名
getActivity().startActivity(i);
}
// 卸载apk
public void deletePackageByName(String pakageName) {
Uri uri = Uri.fromParts("package", pakageName, null);
Intent intent = new Intent(Intent.ACTION_DELETE, uri);
getActivity().startActivity(intent);
}
/**
* 删除单个文件
*
* @param filePath 被删除文件的文件名
* @return 文件删除成功返回true,否则返回false
*/
public boolean deleteFile(String filePath) {
File file = new File(filePath);
if (file.isFile() && file.exists()) {
return file.delete();
}
return false;
}
}
二、然后Unity工程中去下载,调用写好的安卓方法:
下载功能:
/// <summary>
/// 下载安装包
/// </summary>
public class DownloadApk
{
public enum DownloadApkState
{
HeadRequest,
BodyRequest,
FinishRequest,
Completed,
}
public bool isDone { get; private set; }
public DownloadApkState state { get; private set; }
/// <summary>
/// 下载进度回调
/// </summary>
private Action<string, float> _onProgress;
/// <summary>
/// 下载完成回调
/// </summary>
private Action _onCompleted;
/// <summary>
/// 下载进度
/// </summary>
private float _progress;
/// <summary>
/// 要下载的应用包名
/// </summary>
private string _packageName;
/// <summary>
/// 下载url
/// </summary>
private string _url;
// 如:Application.persistentDataPath + "/" + appName + versionCode + ".apk";
/// <summary>
/// 保存路径
/// </summary>
private string _savePath;
private UnityWebRequest request;
private FileStream fs;
private long _len;
private int _index;
private long _maxlen;
private string _error;
public DownloadApkState(string url, string packageName, string savePath, string onProgress, string onCompleted)
{
_url = url;
_packageName = packageName;
_savePath = savePath;
_onProgress = onProgress;
_onCompleted - onCompleted;
}
/// <summary>
/// 开始下载
/// </summary>
public void Start()
{
request = UnityWebRequest.Head(_url);
request.SendWebRequest();
_progress = 0;
isDone = false;
}
/// <summary>
/// 取消下载
/// </summary>
public void Cancle()
{
if (fs != null)
{
fs.Close();
fs.Dispose();
}
isDone = true;
}
void WriteBuffer()
{
var buff = request.downloadHandler.data;
if (buff != null)
{
var length = buff.Length - _index;
fs.Write(buff, _index, length);
_index += length;
_len += length;
_progress = _len / (float)_maxlen;
_onProgress?.Invoke(_packageName, _onProgress);
}
}
public void Update()
{
if (isDone)
{
return;
}
switch (state)
{
case DownloadApkState.HeadRequest:
if (request._error != null)
{
_error = request._error;
}
if (request.isDone)
{
_maxlen = long.Parse(request.GetResponseHeader("Content-Length"));
request.Dispose();
request = null;
var dir = Path.GetDirectoryName(_savePath);
if (!Directory.Exists(dir))
{
Directory.CreateDirectory(dir);
}
if (FileTool.ExistsFile(_savePath))
{
FileTool.DeleteFile(_savePath);
}
fs = new FileStream(_savePath, FileMode.OpenOrCreate, FileAccess.Write);
_len = fs.Length;
if (_len < _maxlen)
{
fs.Seek(_len, SeekOrigin.Begin);
request = UnityWebRequest.Get(_url);
request.SetRequestHeader("Range", "bytes=" + _len + "-" + _maxlen);
request.SendWebRequest();
_index = 0;
state = DownloadApkState.BodyRequest;
}
else
{
// 下载完成
state = DownloadApkState.FinishRequest;
}
}
break;
case DownloadApkState.BodyRequest:
if (request._error != null)
{
_error = request._error;
LWDebug.Log(_error);
}
if (!request.isDone)
{
WriteBuffer();
}
else
{
WriteBuffer();
if (fs != null)
{
fs.Close();
fs.Dispose();
}
request.Dispose();
// 下载完成
state = DownloadApkState.FinishRequest;
}
break;
case DownloadApkState.FinishRequest:
// 下载完成,调用完成回调
isDone = true;
_onCompleted?.Invoke();
state = DownloadApkState.Completed;
break;
}
}
}
下载完成后,调用刚刚写好的Android安装方法,也可以直接调用Android原生方式安装:
/// <summary>
/// 安装apk
/// </summary>
public static bool InstallApp(string apkPath)
{
//判断安卓7.0以下
if (AndroidSDKVersion() < 24)
{
LWDebug.Log("低版本24以下(安卓7.0)安装");
return InstallAppLowAPI(apkPath);
}
else
{
LWDebug.Log("高版本24以上(安卓7.0)安装");
return InstallAppHighAPI(apkPath);
}
}
/// <summary>
/// 获取安卓SDK版本号
/// </summary>
/// <returns></returns>
private static int GetAndroidSDKVersion()
{
int version = 0;
AndroidJavaObject androidJavaObject = new AndroidJavaObject("com.android.util.UseForUnity");
version = androidJavaObject.CallStatic<int>("getAndroidVersion");
return version;
}
/// <summary>
/// 低版本安装
/// </summary>
/// <param name="apkPath"></param>
/// <returns></returns>
private static bool InstallAppLowAPI(string apkPath)
{
bool success = true;
try
{
AndroidJavaClass intentObj = new AndroidJavaClass("android.content.Intent");
string ACTION_VIEW = intentObj.GetStatic<string>("ACTION_VIEW");
int FLAG_ACTIVITY_NEW_TASK = intentObj.GetStatic<int>("FLAG_ACTIVITY_NEW_TASK");
AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent", ACTION_VIEW);
AndroidJavaObject fileObj = new AndroidJavaObject("java.io.File", apkPath);
AndroidJavaClass uriObj = new AndroidJavaClass("android.net.Uri");
AndroidJavaObject uri = uriObj.CallStatic<AndroidJavaObject>("fromFile", fileObj);
intent.Call<AndroidJavaObject>("setDataAndType", uri, "application/vnd.android.package-archive");
intent.Call<AndroidJavaObject>("addFlags", FLAG_ACTIVITY_NEW_TASK);
intent.Call<AndroidJavaObject>("setClassName", "com.android.packageinstaller", "com.android.packageinstaller.PackageInstallerActivity");
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
currentActivity.Call("startActivity", intent);
//安装成功
}
catch (System.Exception e)
{
Debug.LogError(e.Message);
//安装失败
success = false;
}
return success;
}
/// <summary>
/// 24以上(安卓7.0)安装
/// </summary>
/// <param name="apkPath"></param>
/// <returns></returns>
private static bool InstallAppHighAPI(string apkPath)
{
bool success = true;
try
{
//Get Activity then Context
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject unityContext = currentActivity.Call<AndroidJavaObject>("getApplicationContext");
//Get the package Name
string packageName = unityContext.Call<string>("getPackageName");
string authority = packageName + ".fileprovider";
AndroidJavaClass intentObj = new AndroidJavaClass("android.content.Intent");
string ACTION_VIEW = intentObj.GetStatic<string>("ACTION_VIEW");
AndroidJavaObject intent = new AndroidJavaObject("android.content.Intent", ACTION_VIEW);
int FLAG_ACTIVITY_NEW_TASK = intentObj.GetStatic<int>("FLAG_ACTIVITY_NEW_TASK");
int FLAG_GRANT_READ_URI_PERMISSION = intentObj.GetStatic<int>("FLAG_GRANT_READ_URI_PERMISSION");
AndroidJavaObject fileObj = new AndroidJavaObject("java.io.File", apkPath);
AndroidJavaClass fileProvider = new AndroidJavaClass("android.support.v4.content.FileProvider");
AndroidJavaObject uri = fileProvider.CallStatic<AndroidJavaObject>("getUriForFile", unityContext, authority, fileObj);
intent.Call<AndroidJavaObject>("setDataAndType", uri, "application/vnd.android.package-archive");
intent.Call<AndroidJavaObject>("addFlags", FLAG_ACTIVITY_NEW_TASK);
intent.Call<AndroidJavaObject>("addFlags", FLAG_GRANT_READ_URI_PERMISSION);
currentActivity.Call("startActivity", intent);
//安装成功
}
catch (System.Exception e)
{
Debug.LogError(e.Message);
//安装失败
success = false;
}
return success;
}
检查App是否存在,可以调用刚刚写好的Android方法,也可以直接调用Android原生方法:
/// <summary>
/// 检查应用是否存在
/// </summary>
/// <param name="packageName"></param>
/// <returns></returns>
public static bool IsAppExits(string packageName)
{
AndroidJavaClass up = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
AndroidJavaObject currentActivity = up.GetStatic<AndroidJavaObject>("currentActivity");
AndroidJavaObject packageManager = currentActivity.Call<AndroidJavaObject>("getPackageManager");
AndroidJavaObject packageInfos = packageManager.Call<AndroidJavaObject>("getInstalledPackages", 0);
AndroidJavaObject[] packages = packageInfos.Call<AndroidJavaObject[]>("toArray");
for (int i = 0; i < packages.Length; i++)
{
AndroidJavaObject applicationInfo = packages[i].Get<AndroidJavaObject>("applicationInfo");
// 判断是不是系统应用 0:不是系统应用,1:是系统应用
if ((applicationInfo.Get<int>("flags") & applicationInfo.GetStatic<int>("FLAG_SYSTEM")) == 0)
{
string pkgName = applicationInfo.Get<string>("packageName");
if (pkgName == packageName)
{
return true;
}
}
}
return false;
}
三、项目设置:
包名设置,以com.chen.ttt为例:
1、Plugins/Android/res/xml下的provider_paths.xml中path的后面com.chen.ttt换成包名;
2、BulidConfig.xml中的applicationIdentifier的com.chen.ttt换成包名;
3、xml设置,示例:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.chen.ttt" xmlns:tools="http://schemas.android.com/tools" android:installLocation="preferExternal" android:versionName="1.0" android:versionCode="1">
<supports-screens android:smallScreens="true" android:normalScreens="true" android:largeScreens="true" android:xlargeScreens="true" android:anyDensity="true" />
<application android:theme="@style/UnityThemeSelector" android:icon="@drawable/app_icon" android:label="@string/app_name" android:debuggable="true">
<activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name">
<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="android.support.v4.content.FileProvider" android:authorities="com.chen.ttt.fileprovider" android:exported="false" android:grantUriPermissions="true">
<meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_paths" />
</provider>
<meta-data android:name="platform_high" android:value="1" />
</application>
<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-sdk android:minSdkVersion="16" android:maxSdkVersion="29" />
</manifest>
网友评论