栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > Unity3D

Unity与Android通信实现安装本地目录apk

Unity3D 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Unity与Android通信实现安装本地目录apk

  1. Android原生库aar导出
1.1 创建Android工程 1.1.1 基本配置

使用Androidstudio创建一个空项目,如下,

将项目模式切换成:project模式,方便查看工程结构,如下:

导入unity相关依赖文件

classes.jar

从当前的unity引擎版本目录下找到classes.jar文件,至于用mono 还是 用il2cpp模式的classes.jar,可以根据你最后打包的模式来选,我最后用的打包模式是mono,所有我选的jar包也是mono模式的。

我的电脑下路径:

/Applications/Unity/Hub/Editor/2019.4.30f1c1/PlaybackEngines/AndroidPlayer/Variations/mono/Release/Classes/classes.jar

将classes.jar文件拷贝到Android工程lib目录下面,如下:

然后右键选:Add As Library,弹框中选OK,如下:

此时build文件dependencies节点下会自动添加:implementation files('libs/classes.jar')

注意:我们打包的时候只需要打需要的类文件,其余的杂七杂八的东西不需要打进去,所以,需要将:implementation files('libs/classes.jar') 修改成:compileOnly files('libs/classes.jar'),二者的区别自行百度!!!

添加依赖库: implementation 'androidx.core:core:1.2.0',这个是内容提供者需要的类库

修改后如下:

导入UnityPlayerActivity.java类文件

因为2019.4版本的unity,classes.jar并不包含UnityPlayerActivity.java类,因此需要从安装目录中手动拷贝,这个需要特别注意。我的电脑目录如下:

/Applications/Unity/Hub/Editor/2019.4.30f1c1/PlaybackEngines/AndroidPlayer/Source/com/unity3d/player/UnityPlayerActivity.java

将UnityPlayerActivity.java文件拷贝到如下目录下:

修改MainActivity.java文件如下:

对AndroidManifest.xml文件做修改,MainActivity采用全名称注册,不要用.MainActivity方式,否则后面unity打出的包会提示找不到类文件,这点非常重要!

对build.gradle文件(app目录下)做如下修改,目的是为了能编译出aar包,而不是打出apk文件:

确保两个地方配置保持一致

1.1.2 移除文件及配置

移除掉不需要的文件和引用库,箭头所指的目录都移除

将该文件下报红色的配置都移除掉

移除后如下:

1.1.2 实现业务逻辑(安装apk)

注册权限:

为MainActivity 设置属性,让其能被unity调用,如下:

注册内容提供者:

特别注意,authorities 必须对应实际包名,我最后用unity打包的包名是:com.test.cc,而不是:com.test.bb ,这个尤其要注意!

对应目录下创建file_paths.xm文件(如果没有目录自行创建),如下:

file_paths.xm文件内容如下:



    

MainActivity.java代码片段:

package com.test.bb;


import android.Manifest;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;


import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.core.content.FileProvider;


import java.io.File;
import java.util.List;


public class MainActivity extends UnityPlayerActivity {


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        requestPermission();
    }


    private static final int REQUEST_CODE = 1024;
    //动态申请权限
    private void requestPermission() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            // 先判断有没有权限
            if (Environment.isExternalStorageManager()) {
                writeFile();
            } else {
                Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
                intent.setData(Uri.parse("package:" + this.getPackageName()));
                startActivityForResult(intent, REQUEST_CODE);
            }
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            // 先判断有没有权限
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
                    ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                writeFile();
            } else {
                ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
            }
        } else {
            writeFile();
        }
    }


    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        if (requestCode == REQUEST_CODE) {
            if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED &&
                    ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) {
                writeFile();
            } else {
                Log.d("test","---失败");
            }
        }
    }


    @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == REQUEST_CODE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
            if (Environment.isExternalStorageManager()) {
                writeFile();
            } else {
                Log.d("test","---失败");
            }
        }
    }


    
    private void writeFile() {
        Log.d("test","---成功");
        PersimmionsHelper.getPersimmions(this,100);
    }


    //供unity调用的接口
    public  void toInstall(){
        Log.d("test","---unity 调用java开始安装...");
        String apkPath = Environment.getExternalStorageDirectory()+"/Download/aa.apk";
        File apkFile = new File(apkPath);


        Intent intent = new Intent(Intent.ACTION_VIEW);
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        if (Build.VERSION.SDK_INT >= 26){//8.0以上
            intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            intent.addCategory("android.intent.category.DEFAULT");
            Uri contentUri = FileProvider.getUriForFile(this, "com.test.cc.fileprovider", apkFile);//注意,这里必须是实际包名
            intent.setDataAndType(contentUri, this.getContentResolver().getType(contentUri));
            List resInfoList = this.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
            for (ResolveInfo resolveInfo : resInfoList) {
                this.grantUriPermission(resolveInfo.activityInfo.packageName, contentUri,Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }
        }else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N){
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            Uri contentUri = FileProvider.getUriForFile(this, "com.test.cc.fileprovider",apkFile);
            intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
        } else {
            intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive");
        }
        startActivity(intent);
    }


}

PersimmionsHelper.java代码片段

package com.test.bb;


import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.pm.PackageManager;
import android.os.Build;


import java.util.ArrayList;


public class PersimmionsHelper {
    private PersimmionsHelper(){}


    public static void getPersimmions(Activity activity,int permissionRequestCode) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
            ArrayList permissions = new ArrayList();
            
            // 定位精确位置
//            if (activity.checkSelfPermission(Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//                permissions.add(Manifest.permission.ACCESS_FINE_LOCATION);
//            }
//            if (activity.checkSelfPermission(Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
//                permissions.add(Manifest.permission.ACCESS_COARSE_LOCATION);
//            }
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q){
                if (activity.checkSelfPermission(Manifest.permission.ACCESS_BACKGROUND_LOCATION) != PackageManager.PERMISSION_GRANTED) {
                    permissions.add(Manifest.permission.ACCESS_BACKGROUND_LOCATION);
                }
            }
            // 读写权限
            if (PersimmionsHelper.addPermission(activity,permissions, Manifest.permission.WRITE_EXTERNAL_STORAGE)) {
                System.out.println("=Manifest.permission.WRITE_EXTERNAL_STORAGE Deny==");
            }
            if (PersimmionsHelper.addPermission(activity,permissions, Manifest.permission.READ_EXTERNAL_STORAGE)) {
                System.out.println("=Manifest.permission.READ_EXTERNAL_STORAGE==");
            }
            if (permissions.size() > 0) {
                activity.requestPermissions(permissions.toArray(new String[permissions.size()]), permissionRequestCode);
            }
        }
    }
    @TargetApi(23)
    private static boolean addPermission(Activity activity,ArrayList permissionsList, String permission) {
        // 如果应用没有获得对应权限,则添加到列表中,准备批量申请
        if (activity.checkSelfPermission(permission) != PackageManager.PERMISSION_GRANTED) {
            if (activity.shouldShowRequestPermissionRationale(permission)) {
                return true;
            } else {
                permissionsList.add(permission);
                return false;
            }
        } else {
            return true;
        }
    }
}


1.1.3 编译aar文件

编译前先同步一下,如下:

选择rebuild选项,如下:

如果一切顺利,可在如下目录找到aar文件

  1. unity调用aar接口
2.1 创建工程

新建一个unity普通工程

2.2 引入aar等文件

将上面得到的aar文件以及项目中的AndroidManifest.xml 两个文件拷贝到unity的如下目录(如果没有该目录则自行创建):

2.3 修改配置

勾选如下选项,

从而自动生成如下文件:

用编辑器打开该文件,添加一行依赖,如下:

2.4 测试按钮

UI上创建一个测试按钮

canvas上挂上脚本GameManager(自行创建),按钮添加监听函数,如下:

GameManager脚本代码如下:

using UnityEngine;


public class GameManager : MonoBehaviour
{
    /// 
    /// 按钮监听函数,调用Android端接口
    /// 
    public void ToInstall() {
        print("---》开始安装");
        AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject jo = jc.GetStatic("currentActivity");
        jo.Call("toInstall");
        print("---->执行完毕");
    }


}


2.5 打包apk

一定要注意包名设置,设置如下:

  1. 真机测试
3.1 准备待安装文件

首先将任意一个apk文件(也可以是当前软件的低版本apk)移动到手机sd卡Download目录下,并且命名为:aa.apk

3.2 安装工具包

安装工具软件cc.apk,如果在Android6.0以上手机上运行,第一次会提示授权窗口,点击:始终允许,有的Android手机可能需要手动设置cc.apk软件:允许安装未知来源应用 选项。

3.3 测试

cc.apk启动之后,点击:测试按钮,如果一切正常,此时系统会调起安装aa.apk的操作界面,此时表示整个功能已实现。

4、开发及测试环境 4.1 开发平台

mac pro(苹果笔记本)

4.2 android stuido版本

版本:4.1

4.3 unity版本

2019.4.30f1c1

4.4 移动设备 4.4.1 设备1

品牌及型号:小米5,系统:Android8.0

4.4.2 设备2

品牌及型号:红米note8,系统:Android10

4.4.3 设备3

品牌及型号:红米11,系统:Android11

5、注意事项 5.1 UnityPlayerActivity文件

导入UnityPlayerActivity.java类文件时:

因为2019.4版本的unity,classes.jar并不包含UnityPlayerActivity.java类,因此需要从安装目录中手动拷贝,这个需要特别注意。

5.2 动态权限

Android 6.0之后引入了动态权限机制,所以所有的敏感权限都需要动态申请,而不是仅仅在清单文件中配置,这一点非常重要。

5.3 Api差异化

不同Android版本,实现相同功能提供的方案不一样,有时候需要对目标系统版本进行判断,这一点在业务逻辑代码中体现得尤为明显。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/899946.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号