收藏

Unity2020以后版本调用aar包安装Apk

2022-01-12  本文已影响0人  Walk_In_Jar

由于unity调整,已经无法继承UnityPlayerActivity


已过时

所以更新一下以前的文章

使用as,新建工程Empty Activity 包名com.join.testar

image.png

修改MainActivity:
继承Activity,测试方法add

package com.join.testar;

import android.app.Activity;
import android.content.Context;
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 java.io.File;

public class MainActivity extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);
    }

    public int Add(int a,int b){
        return a+b;
    }public void InstallApk(Context context, String apkPath){

        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(context,  context.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);
        }
        context.startActivity(intent);

    }
    /***
     * 安装apk
     * @param
     * @param apkPath
     */
    public void InstallApk2(String apkPath){

        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(getActivity(),  getActivity().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);
        }
        getActivity().startActivity(intent);
    }


//也可以通过安卓方法获取context,此MainActivity没能生效
private Activity _unityActivity;
    /**
     * 获取unity项目的上下文
     * @return
     */
    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) {
                Log.d("Unity2Android", "getActivity: "+e);
            } catch (IllegalAccessException e) {
                Log.d("Unity2Android", "getActivity: "+e);
            } catch (NoSuchFieldException e) {
                Log.d("Unity2Android", "getActivity: "+e);
            }
        }
        return _unityActivity;
    }

    /**
     * 调用Unity的方法
     * @param gameObjectName    调用的GameObject的名称
     * @param functionName      方法名
     * @param args              参数
     * @return                  调用是否成功
     */
    public boolean callUnity(String gameObjectName, String functionName, String args){
        try {
            Class<?> classtype = Class.forName("com.unity3d.player.UnityPlayer");
            Method method =classtype.getMethod("UnitySendMessage", String.class,String.class,String.class);
            method.invoke(classtype,gameObjectName,functionName,args);
            return true;
        } catch (ClassNotFoundException e) {
            Log.d("Unity2Android", "callUnity: "+e);
        } catch (NoSuchMethodException e) {
            Log.d("Unity2Android", "callUnity: "+e);
        } catch (IllegalAccessException e) {
            Log.d("Unity2Android", "callUnity: "+e);
        } catch (InvocationTargetException e) {
            Log.d("Unity2Android", "callUnity: "+e);
        }
        return false;
    } 
}

project视图下删除选中文件夹,


image.png

values文件夹下的xml内容都可以删除


image.png
image.png
image.png

res文件夹下新建xml目录、filepaths.xml


image.png

filepaths文件内容

<?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>

修改AndroidManifest

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.join.testar">

    <application
        android:allowBackup="true">
        <provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="${applicationId}.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 声明读写目录、相关权限

修改build.grade

plugins {
    id 'com.android.library'//改为library输出
}

android {
    compileSdk 30

    defaultConfig {
      //  applicationId "com.join.testar"//注释
        minSdk 21
        targetSdk 30
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
    }

    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
        }
    }
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
}

dependencies {

    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
   // testImplementation 'junit:junit:4.+'
  //  androidTestImplementation 'androidx.test.ext:junit:1.1.2'
   // androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

新建class第三方类


image.png
JavaCTest.java
package com.join.testar;
import android.content.Context;
public class JavaCTest {
    private static JavaCTest instance;
    public static JavaCTest Instance(){
        if(instance==null)
            instance=new JavaCTest();
        return instance;
    }
    public int Sub(int a,int b){
        return a-b;
    }
}

as工程施工完毕
sync一下,就可以build输出了


image.png

找到输出arr文件app-debug.aar


image.png
image.png
复制到Unity的 Assets/Plugins/Android目录下
unity中设置自定义gradle image.png

如下修改添加androidx支持

android.useAndroidX=true
android.enableJetifier=true 
image.png image.png
allprojects { repositories { mavenCentral() }}
dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'androidx.appcompat:appcompat:1.2.0'
**DEPS**}

unity代码部分

新建mono脚本
编写代码调用MainActivity中的方法

AndroidJavaObject Main = new AndroidJavaObject("com.join.testar.MainActivity");
int s = Main.Call<int>("Add", 2, 8);

调用javactest中的方法

AndroidJavaObject jc = new AndroidJavaObject("com.join.testar.JavaCTest");
var jo = jc.CallStatic<AndroidJavaObject>("Instance");
int s = jo.Call<int>("Sub", 3, 2);

可以打包运行看是否调用到了

安装apk

首先是下载apk到Application.persistentDataPath 目录

        string remote = "http://192.168.1.1:5569/Root/3.apk";
        string down = Application.persistentDataPath + "/3.apk";
        UnityWebRequest request = UnityWebRequest.Get(remote);
        yield return request.SendWebRequest();
        byte[] d = request.downloadHandler.data;
        File.WriteAllBytes(down, d);

//调用安卓方法
       AndroidJavaObject activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
        AndroidJavaObject context = activity.GetStatic<AndroidJavaObject>("currentActivity");
        string downapk = Application.persistentDataPath + "/3.apk";
        AndroidJavaObject Main = new AndroidJavaObject("com.join.testar.MainActivity");
        Main.Call("InstallApk", context, downapk); 

       或
       Main.Call("InstallApk2",  downapk); 

至此完毕
区别就是需要传入获取到的context

上一篇 下一篇

猜你喜欢

热点阅读