一.让Unity支持AndroidX
1.1修改mainTemplate.gradle
dependencies中增加implementation 'androidx.core:core:1.0.1'
dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'androidx.core:core:1.6.0' }
1.2修改gradleTemplate.properties文件支持AndroidX
增加android.useAndroidX=true
org.gradle.jvmargs=-Xmx**JVM_HEAP_SIZE**M org.gradle.parallel=true android.useAndroidX=true android.enableR8=**MINIFY_WITH_R_EIGHT** unityStreamingAssets=.unity3d**STREAMING_ASSETS** **ADDITIONAL_PROPERTIES**
二.配置Manifest和Provider
2.1配置AndroidManifest.xml文件
1.配置安装权限
2.配置provider信息
<?xml version="1.0" encoding="utf-8"?>
<!-- GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN-->
<manifest
xmlns:android="http://schemas.android.com/apk/res/android"
package="com.unity3d.player"
xmlns:tools="http://schemas.android.com/tools">
<!--请求安装权限-->
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
<application>
<activity android:name="com.unity3d.player.UnityPlayerActivity"
android:theme="@style/UnityThemeSelector">
<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>
<!-- FileProvider 配置内容开始 -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.fileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths" />
</provider>
<!-- FileProvider 配置内容结束 -->
</application>
</manifest>
2.2配置 Provider文件
在Plugins/Android/res/下创建xml文件夹以及file_paths.xml文件,此处文件名要和AndroidManifest配置的文件名一一对应。
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<!--取决于你APK的缓存位置-->
<files-path name="files" path="."/>
</paths>
三.导入Java代码
将java代码放置Plugins/Android/Java中
3.1ApkUtility.java
用于给Unity暴露接口
package cn.ktgames.utility.android;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.FragmentManager;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.provider.Settings;
import androidx.core.content.FileProvider;
import java.io.File;
public class ApkUtility {
/**
* 安装失败Apk不存在
*/
private static final int InstallFailWithFileNon = 10001;
/**
* 安装失败用户取消授权
*/
private static final int InstallFailWithUserCancelInstallPermission = 10002;
/**
* Apk工具监听器
*/
public interface Listener{
void onInstallResult(int code,String path,String message);
}
private static String ApplyPermissionTitle = "请设置本应用安装权限";
private static String ApplyPermissionApplyButton = "前往设置";
private static String ApplyPermissionCancelButton = "取消设置";
private static Listener listener;
/**
* 初始化
*/
public static void Init(Listener lis)
{
listener = lis;
}
/**
* 设置权限申请信息
* @param title 标题
* @param applyButton 同意按钮文本
* @param cancelButton 取消按钮文本
*/
public static void SetApkPermissionInfo(String title,String applyButton,String cancelButton){
ApplyPermissionTitle = title;
ApplyPermissionApplyButton = applyButton;
ApplyPermissionCancelButton = cancelButton;
}
/**
* 发送安装结果
* @param code 代码
* @param path 文件路径
* @param message 消息内容
*/
private static void SendInstallResult(int code,String path,String message){
if (listener!=null)
{
listener.onInstallResult(code,path,message);
}
}
/**
* 通过路径安装APK
* @param activity Activity
* @param filePath 文件路径
*/
public static void InstallApk(Activity activity, String filePath){
File file = new File(filePath);
if (!file.exists()){
SendInstallResult(InstallFailWithFileNon,filePath,"File Non-Existent");
return;
}
//8.0+系统
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
//获取系统安装权限
boolean isInstallPermission = GetInstallPermission(activity);
if (isInstallPermission){
InstallApk(activity,file);
}else{
activity.runOnUiThread(new Runnable(){
public void run() {
ApplyPermission(activity,filePath);
}
});
}
}
else {
InstallApk(activity,file);
}
}
/**
* 安装Apk
* @param activity Activity
* @param file 目标文件
*/
private static void InstallApk(Activity activity,File file)
{
Intent intent = new Intent(Intent.ACTION_VIEW);
//7.0+系统,需要配置FileProvider应用文件共享
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
//这里注意fileProvider要和manifest的authorities保持一致
Uri apkUri = FileProvider.getUriForFile(activity, GetPackageName(activity)+".fileProvider", file);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
intent.setDataAndType(apkUri, "application/vnd.android.package-archive");
}
//7.0-系统
else {
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
Uri uri = Uri.fromFile(file);
intent.setDataAndType(uri, "application/vnd.android.package-archive");
}
activity.startActivity(intent);
}
/**
* 获取包名
* @param context Context
* @return 包名,这里返回的时applicationid
*/
private static String GetPackageName(Context context){
return context.getPackageName();
}
/**
* 获取安装权限
* @param activity Context
* @return 是否拥有安装权限
*/
public static boolean GetInstallPermission(Context activity){
return activity.getPackageManager().canRequestPackageInstalls();
}
private static AlertDialog.Builder permissionDialog;
/**
* 申请安装权限
* @param activity
* @param filePath
*/
private static void ApplyPermission(Activity activity, String filePath){
permissionDialog = new AlertDialog.Builder(activity);
permissionDialog.setTitle(ApplyPermissionTitle);
permissionDialog.setCancelable(false);
permissionDialog.setNegativeButton(ApplyPermissionApplyButton, (dialog, which) -> {
ApkUtilityFragment fragment = GetFragment(activity);
Uri packageUri = Uri.parse("package:"+ GetPackageName(activity));
Intent intent = new Intent(Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,packageUri);
fragment.StartActivityForResult(intent, ApkUtilityFragment.REQCODE_PERMISSION_INSTALL, (requestCode, resultCode, data) -> {
InstallApk(activity,filePath);
});
});
permissionDialog.setPositiveButton(ApplyPermissionCancelButton, (dialog, which) -> SendInstallResult(InstallFailWithUserCancelInstallPermission,filePath,"User Cancel Install Permission"));
permissionDialog.show();
}
private static ApkUtilityFragment apkUtilityFragment;
/**
* 获取或创建Fragment
* @param activity
* @return
*/
private static ApkUtilityFragment GetFragment(Activity activity)
{
if (apkUtilityFragment==null)
{
FragmentManager fragmentManager = activity.getFragmentManager();
apkUtilityFragment =(ApkUtilityFragment)fragmentManager.findFragmentByTag(ApkUtilityFragment.TAG);
if (apkUtilityFragment==null){
apkUtilityFragment = new ApkUtilityFragment();
apkUtilityFragment.SetActivity(activity);
}
}
return apkUtilityFragment;
}
}
3.2ApkUtilityFragment
用于状态接收,处理
package cn.ktgames.utility.android;
import android.app.Activity;
import android.app.Fragment;
import android.app.FragmentManager;
import android.content.Intent;
import android.util.SparseArray;
public class ApkUtilityFragment extends Fragment {
/**
*Fragment 标签
*/
public static final String TAG = "on_ktgames_action_apk_utility_fragment";
/**
* 安装权限请求码
*/
public static final int REQCODE_PERMISSION_INSTALL = 100411;
/**
* ActivityResult 返回回调接口
*/
public interface ActivityResult{
/**
* ActivityResult 返回回调
*/
void onActivityResult(int requestCode, int resultCode, Intent data);
}
/**
* 依赖的Activity
*/
private Activity activity;
/**
* 设置依赖的Activity
*/
public void SetActivity(Activity activity){
//获取Fragment管理器
FragmentManager fragmentManager = activity.getFragmentManager();
this.activity =activity;
fragmentManager
.beginTransaction()//开始操作
.add(this, ApkUtilityFragment.TAG)//添加fragment
.commitAllowingStateLoss();//提交(此方法会有丢失页面状态信息的风险。)
fragmentManager.executePendingTransactions();//立刻执行transaction的动作
}
/**
* 用于缓存跳转回调结果
*/
private SparseArray<ActivityResult> activityResults = new SparseArray<ActivityResult>();
/**
* 执行一个Activity跳转
*/
public void StartActivityForResult(Intent intent,int requestCode,ActivityResult result){
activityResults.put(requestCode,result);
startActivityForResult(intent,requestCode);
}
/**
* 接收Activity结果
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
ActivityResult callback = activityResults.get(requestCode);
activityResults.remove(requestCode);
if (callback != null) {
callback.onActivityResult(requestCode,resultCode,data);
}
}
}
四.编写C#接口
using System;
using UnityEngine;
namespace Cn.Ktgames.Util.Android
{
/// <summary>
/// APK工具
/// </summary>
public class ApkUtility
{
/// <summary>
/// 安装失败Apk不存在
/// </summary>
public const int InstallFailWithFileNon = 10001;
/// <summary>
/// 安装失败用户取消授权
/// </summary>
public const int InstallFailWithUserCancelInstallPermission = 10002;
/// <summary>
/// 安装结果
/// </summary>
public static Action<int, string, string> onInstallResult;
class ApkUtilityListener : AndroidJavaProxy
{
public ApkUtilityListener() : base(ClassName + "$Listener") { }
void onInstallResult(int code, string path, string message)
{
ApkUtility.onInstallResult?.Invoke(code, path, message);
}
}
private const string PackageName = "cn.ktgames.utility.android";
private const string ClassName = PackageName +".ApkUtility";
private static AndroidJavaObject activity;
private static AndroidJavaClass utillityClass;
private static AndroidJavaClass UtillityClass
{
get
{
if(utillityClass==null)
{
utillityClass = new AndroidJavaClass(ClassName);
}
return utillityClass;
}
}
/// <summary>
/// 初始化,如果想接收安装回调结果先调用此接口
/// </summary>
public static void Init()
{
#if UNITY_ANDROID && !UNITY_EDITOR
UtillityClass.CallStatic("Init",new ApkUtilityListener());
#endif
}
/// <summary>
/// 安装Apk
/// </summary>
/// <param name="filePath"></param>
public static void InstallApk(string filePath)
{
#if UNITY_ANDROID && !UNITY_EDITOR
UtillityClass.CallStatic("InstallApk", GetActivity(), filePath);
#endif
}
/// <summary>
/// 设置权限弹窗信息
/// </summary>
/// <param name="title"></param>
/// <param name="applyButton"></param>
/// <param name="cancelButton"></param>
public static void SetApkPermissionInfo(string title, string applyButton, string cancelButton)
{
#if UNITY_ANDROID && !UNITY_EDITOR
UtillityClass.CallStatic("SetApkPermissionInfo", title, applyButton, cancelButton);
#endif
}
/// <summary>
/// 获取是否有安装权限
/// </summary>
public static bool GetInstallPermission()
{
#if UNITY_ANDROID && !UNITY_EDITOR
return UtillityClass.CallStatic<bool>("GetInstallPermission", GetActivity());
#else
return false;
#endif
}
/// <summary>
/// 获取Activity
/// </summary>
/// <returns></returns>
private static AndroidJavaObject GetActivity()
{
if (activity==null)
{
var UnityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
activity = UnityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
}
return activity;
}
}
}