• 现在遇到一个问题,unity5.4中打包ios的时候Plugins/Android也被复制到了iOS工程的Libraries/Plugins/Android下面,并且Plugins/iOS下的framework没有被包含进来。这个问题暂时没有解决,我在stackoverflow上面看到...

    现在遇到一个问题,unity5.4中打包ios的时候Plugins/Android也被复制到了iOS工程的Libraries/Plugins/Android下面,并且Plugins/iOS下的framework没有被包含进来。

    这个问题暂时没有解决,我在stackoverflow上面看到了别人的提问http://stackoverflow.com/questions/37257453/why-does-unity-5-4-include-the-contents-of-plugins-android-to-ios-project,并顶了一下这个问题,另外我也在answer.unity.com上提问了。

    问题所在:Plugins/Android下面有的文件设置了Any Platform,所以会被复制进iOS工程,我们只需要正确设置每个文件对应的平台就行了

    这里写图片描述

    展开全文
  • Android studio 安装Plugins

    2019-06-11 21:42:37
    解决插件不能下载问题:File->Settings->Apparence & Behavior->System Settings->Updates->use secure connnection 勾去掉 转载于:https://juejin.im/post/5b46bafee51d4519634fa2c8...

    解决插件不能下载问题:

    File->Settings->Apparence & Behavior->System Settings->Updates->use secure connnection 勾去掉


    转载于:https://juejin.im/post/5b46bafee51d4519634fa2c8

    展开全文
  • https://www.cnblogs.com/u3ddjw/p/6800542.html
    展开全文
  • 本文将介绍如何在Unity工程中使用Android或者Java的库,包括: 如何在Unity项目中使用Android Plugin Unity-Android相互调用 Unity接口设计的最佳实践 如何构建Unity-Android混合项目 如何调试Unity和Android...

    本文将介绍如何在Unity工程中使用Android或者Java的库,包括:

    如何在Unity项目中使用Android Plugin
    Unity-Android相互调用
    Unity接口设计的最佳实践
    如何构建Unity-Android混合项目
    如何调试Unity和Android代码

    附录:跨虚拟机调用的实现
    如何在Unity项目中使用Android Plugin

    Android Plugin需要包含一个jar和对应的封装代码。后者用来封装Android代码,提供给Unity项目使用。

    jar放在Unity项目的/Assets/Plugins/Android中,Android插件的其他依赖也放在此处。

    封装代码可以是C#文件,或者dll文件,都放在/Assets中,若是dll,需在Unity C#工程中添加此dll依赖。

    此外,如果Android插件包含资源,按照原有目录结构放到/Assets/Plugins/Android中即可。

    如果需要额外的系统权限,需要在AndroidManifest.xml中添加,这个文件默认是没有的,如果要修改的话,必须手动添加一份Unity可用的manifest,可参考【附录】中提供的模板。

    最后,工程的结构类似这样:

    Assets
    └── Plugins
    ├── Android
    │ ├── AndroidManifest.xml
    │ ├── android_sdk.jar
    │ └── res
    │ ├── values
    │ └── drawable
    └── unity_wrapper.dll

    Unity 从5.2.0b3版本开始很好地支持aar格式的文件,可以将资源打包进aar中,不必再放置到该目录下。


    Unity与Android之间相互调用

    准确来说,应该是两个VM之间的相互调用:mono/il2cpp 和 dalvik/art,分别运行Unity应用和Android应用,这两个虚拟机运行在同一个进程中。

    为了方便起见,后文将前者称为Unity,后者称为Android


    如上图所示,Unity通过UnityEngine提供的API调用Android的方法;Android借助com.unity.player包提供的API调用Unity的方法。

    前者可以直接调用Android对象或者类的方法,而后者只能调用Unity中指定GameObject所挂载的脚本的方法,或者通过动态代理的方式调用Unity的方法。

    Unity调用Java方法

    UnityEngine提供了两个类来分别访问Java的实例对象以及类对象:
    AndroidJavaObject与AndroidJavaClass

    前者表示java.lang.Object或其子类,后者表示java.lang.Class。他们提供相同的实例方法:

    方法
    返回值
    说明
    Call
    void
    调用实例方法
    Call<T>
    T
    调用实例方法
    CallStatic
    void
    调用类方法
    CallStatic<T>
    T
    调用实例方法
    Get<T>
    T
    获取成员变量
    GetStatic<T>
    T
    获取类的成员变量
    Set(T)
    void
    设置成员变量
    SetStatic(T)
    void
    设置类的成员变量

    注意:

    T的类型只能为原始值类型(int、long、string等等),或者AndroidJavaObject、AndroidJavaClass,或者内容为原始值类型或AndroidJavaObject的数组
    Get和Set方法直接操作成员变量,而不是通过getter或setter
    下面将通过一段代码来演示:如何获取一个AndroidJavaClass实例,并且调用其getInstance方法获取其对象,然后调用此对象的方法。

    在开始之前,先看一下我们用到的Java类

        package example;
        public class Player {
            private final static Player instance = new Player();
            public static Player getInstance() {
                return instance;
            }
            public float volume = 1.0f;
            public int getDuration() {}
            public void setDataSource(String dataSource) {}
            public AudioInfomation getAudioInfomation() {}
        }

    首先,在工程中添加UnityEngine.dll依赖,该文件位于Unity安装目录下的Editor/Data/Managed目录中,注意,添加依赖后,将其设置为不拷贝到本地。

    现在,我们来获取Player这个类并获取其单例:


    AndroidJavaObject player = new AndroidJavaClass("example.Player").CallStatic<AndroidJavaObject>("getInstance");

    然后对player对象调用其Java方法:
    <span style="white-space:pre">	</span>player.Set("volume", 0.8f);
            player.Call("setDataSource", "http://example.com/stream.m4a");
            int duration = player.Call<int>("getDuration");
            AndroidJavaObject info = player.Call<AndroidJavaObject>("getAudioInfomation");

    注意,返回值类型为AndroidJavaObject的方法有个共同的缺陷:如果Android侧返回null,该方法将报错:JNI: Init'd AndroidJavaObject with null ptr!。

    这是因为,在AndroidJavaObject的构造函数中,遇到IntPtr.Zero(即null)会报错:


    internal AndroidJavaObject(IntPtr jobject) : this(){    if (jobject == IntPtr.Zero){      throw new Exception("JNI: Init'd AndroidJavaObject with null ptr!");    }    // ...  }
    该缺陷存在于5.3.1f1版本之前的UnityEngine,一个可行的办法是:先获取Android方法返回结果的指针,如果指针为空就返回null,否则返回指针的对象。
    
    
    

    Android调用Unity方法

    在Android中,有两种方式调用Unity的方法:

    通过AndroidJavaProxy进行无感知调用

    • 通过com.unity3d.player.UnityPlayer.UnitySendMessage方法显式调用

      AndroidJavaProxy
      AndroidJavaProxy常用于在Unity中实现Java的interface,比如有这么一个java interface:

      package demo;
      interface PlayStateListener {
          void onBufferFinished(SongInfo songInfo);
          void onBufferProgress(String songId, long buffered, long total);
      }

      对应的C#类就是这样:

      class PlayStateChangedHandler : AndroidJavaProxy
      {
          internal PlayStateChangedHandler() : base(new AndroidJavaClass("demo.PlayStateListener")) { }
          public void onBufferFinished(AndroidJavaObject songInfoObject) { }
          public void onBufferProgress(string songId, long buffered, long total) { }
      }


      有几点需要注意:

      Unity侧的方法必须为public,且有相同的名称和类似的签名
      如果Android侧方法的传参或返回值为类类型,对应Unity侧只能为AndroidJavaObject
      4.6.8f1版本的UnityEngine有BUG,无法在AndroidJavaProxy中传递long类型的值,该问题在Unity 5中已经修复

      有关AndroidJavaProxy的实现,在附录中有详细介绍

      UnityPlayer.UnitySendMessage

      这需在Android工程中添加Unity提供的jar依赖,它位于Unity安装目录下:
      /Editor/Data/PlaybackEngines/AndroidPlayer/Viariations/{backend}/{buildType}/Classes/classes.jar

      其中,backend是Unity项目脚本执行器的类型,有mono和il2cpp两种,与Unity项目的”Script Backend”一致。

      然后通过以下代码来访问挂载在TGameObj对象上的脚本的OnButtonClick方法:

      UnityPlayer.UnitySendMessage("TGameObj", "OnButtonClick", "Greetings from Java");

      Unity接口设计的最佳实践
      本节将介绍一个用于封装Java代码的通用设计方式,可以高效地将Java代码的API“移植”到C#,同时保持可扩展性。该设计将Java代码中的类及其结构反射到C#代码中,至于该类的细节(比如继承关系、接口实现等)将被忽略,因为需要反射的都是暴露给用户的API接口,用户不应该关心这些细节。

      如下图所示:


    • Java中的demo.Foo类通过Reflection反射到C#的Mirrored.Foo中,demo.Foo中的公共字段和方法都按照原有结构被反射。

      注意,这里的反射只是单向地从Java反射到C#。如果要从C#反射到Java,可以参考本节进行扩展。

      反射的实现

      在开始之前,我们需要明确哪些类需要反射。对于int, long, double等原始类型以及string类型,UnityEngine已经帮我们处理好了,只剩下java.lang.Object的派生类需要我们反射。

      反射基类的设计

      我们使用AndroidObjectMirror作为反射类的父类。

      public abstract class AndroidObjectMirror : System.IDisposable
      {
          protected AndroidJavaObject AJObject { get; private set; }
          internal void FromJava(AndroidJavaObject ajo)
          {
              AJObject = ajo; InitFromJava(ajo);
          }
          public virtual void Dispose()
          {
              AJObject?.Dispose();
          }
          protected virtual void InitFromJava(AndroidJavaObject ajo)
          {
      
          }
      }

      在AJObject这个反射对象被创建时,被反射对象的引用计数将会增加(AndroidJNISafe.NewGlobalRef),在Dispose方法中,其引用计数将会减少(AndroidJNISafe.DeleteGlobalRef)。

      之后,子类通过覆写InitFromJava方法来进行成员变量的初始化:

      子类可以创建和被反射类“一样的”方法,并将所有的调用委托给成员变量AJObject即可。例如:


        int Add(int a, int b)
        {
            return AJObject.Call<int>("add", a, b);
        }
    总结一下,反射的逻辑如下图所示:


    反射的实现

    借助于AndroidObjectMirror,我们可以这样来定义上文提及的example.Player的反射类:

        class Player : AndroidObjectMirror {
            public static Player Instance {
                get {
                    var javaObject = AJObject.CallStatic("getInstance");
                    return Reflection.Reflect(javaObject);
                }
            }
            public float Volume {
                get { return AJObject.Get("volume"); }
                set { AJObject.Set("volume", value); }
            }
        public void Start() { AJObject.Call("start"); }
    // ..
    }

    注意,在获取单例时,我们用了这样一行代码:

    return Reflection.Reflect(javaObject);

    Reflection这个工具类用来反射Java的对象,即将AndroidJavaObject的对象反射为派生自AndroidObjectMirror的类的对象。

    其中的Reflect方法是这样实现的:

        public static T Reflect(AndroidJavaObject ajo) where T : AndroidObjectMirror, new()
        {
            if (ajo == null)
            {
                return default(T);
            }
            try
            {
                var result = new T();
                result.FromJava(ajo);
                return result;
            }
            catch (System.Exception e)
            {
                Debug.LogError("failed to reflect from Java : " + e.Message);
                return default(T);
            }
        }

    这里的逻辑很直接:创建一个AndroidJavaObject对象ajo,然后在InitFromJava方法中通过ajo来初始化这个对象的成员变量。注意,这里约束了类型T必须提供无参公共构造函数,因此AndroidJavaMirror必须通过InitFromJava(AndroidJavaObject)来初始化,而没有将AndroidJavaObject放在构建函数中。

    至于InitFromJava方法,它可以是这样:


        protected override void InitFromJava(AndroidJavaObject ajo)
        {
            title = ajo.Get("title");
        }
        //或者类似反序列化的方式在运行时进行解析:
        protected virtual void InitFromJava(AndroidJavaObject androidJavaObject)
        {
            var namingConverter = DefaultNamingConverter.Instance;
            var publicFields = GetType().GetFields();
            foreach (var field in publicFields)
            {
                var javaFieldName = namingConverter.GetJavaFieldName(field.Name);
                var value = androidJavaObject.Get(field.GetType(), javaFieldName);
                field.SetValue(this, value, System.Reflection.BindingFlags.SetField, null, null);
            }
        }

    如何构建Unity-Android混合项目

    本节将介绍如何使用Gradle来构建混合了不同平台项目的工程。

    以一个SDK类型的工程为例,我们来看一下工程的内容:

    Android SDK
    Android Demo (快速调试)
    Unity Bridge (封装Android SDK)
    Unity Demo (演示并调试Unity Bridge)
    目录结构如下:

    RootDir (工程根目录)
    |
    |-- Android (Android相关模块)
    | |-- Demo_Android
    | |-- SDK_Android
    |
    |-- Unity (Unity相关模块)
    |-- Demo_Unity
    |-- Bridge_Unity
    其中:

    Android的两个模块可以用Android gradle插件进行编译与打包
    Bridge_Unity可以用msbuild(windows)或者xbuild(linux)构建
    Demo_Unity需要购买了Unity Pro之后才能自动化构建。
    接下来,我们将在各自模块的构建脚本中添加构建任务,分别构建这些模块,最后,在工程的根构建脚本中,创建自动化的构建脚本。

    Android SDK的构建

    Jar包构建任务

    SDK将以Jar的形式提供给Unity Bridge使用,因此需要添加打包成jar的构建任务。我们利用已有的Android构建任务链,创建Jar构建任务。

    已有的构建脚本位于RootDir/Android/SDK_Android/build.gradle,在其中加入Jar构建任务:


    android.libraryVariants.all { v ->
        def type = v.name.capitalize()
        task("jar$type", type: Jar) {
            archiveName "sdk-$type.jar"
            dependsOn v.javaCompile
            from v.javaCompile.destinationDir, configurations.compile.collect {
                it.isDirectory() ? it : zipTree(it)
            }
        }
    }

    task后面的闭包会在gradle脚本构建时运行,用来定义此任务的属性:

    archiveName: 输出Jar包的文件名,默认为模块名称
    dependsOn: 此任务的依赖
    from: 要打包的class
    这里需要注意:

    依赖
    dependsOn: v.javaCompile
    此任务必须在v.javaCompile完成之后运行,即java文件被编译成class文件之后再将这些class打包成Jar。

    要打包的class

    from v.javaCompile.destinationDir, configurations.compile.collect {
    it.isDirectory() ? it : zipTree(it)
    }

    这里说明要打包的class有两处:模块自身的和依赖的Jar包。

    Proguard构建任务

    对外发布时,通常需要对代码进行混淆。对于我们自定义的Jar任务,必须手动添加混淆任务:

    task("proguardJar$type", type: ProGuardTask) {
        dependsOn "jar$type"
        configuration android.getDefaultProguardFile('proguard-android.txt')
        configuration 'proguard-rules.pro'
        injars "build/libs/sdk-$type.jar"
        outjars "$outputFolder/sdk-$suffix.jar"
        dontshrink
        ignorewarnings
    }.doFirst {
        delete outputFolder
    }

    注意其中的dependsOn "jar$type",这样就将混淆任务和jar任务串联了起来。

    发布任务

    为了便于其他构件脚本获取此模块的最新构建结果,我们将输出的Jar拷贝到latest目录中。


    task("buildJar$type", type: Copy, group: 'build') {
        dependsOn "cleanBuildJar$type"
        from outputFolder
        into "build/outputs/libs/latest/$type
        if (type.equals("Release")) {
            dependsOn "proguardJar$type"
        } else {
            dependsOn "jar$type"
        }
    }

    至此,Android SDK构建任务添加完成。

    Android Demo的构建

    Andriod Gradle Plugin已经提供Demo的构建任务。

    Unity Bridge的构建

    在开始之前,我们需要配置好构建环境:对于Windows系统,需要用到msbuild,它会随着Visutal Studio一同安装;对于linux/unix系统,可以使用xbuild,它是Mono里面的一个工具。下文将使用xbuild。

    准备工作完成后,我们来创建CSharpBuildTask这个构建任务。该任务其实就是封装了对xbuild的调用:


    package demo
    class CSharpBuildTask extends DefaultTask
    {
        @Input File solutionFile;
        @Input String configuration;
        @Input String builderCmd = "/usr/local/bin/xbuild"
        @TaskAction def compile() {
            def cmd = "$builderCmd $solutionFile"
    if (configuration != null)
            {
                cmd += " /p:Configuration=$configuration"
    }
            def proc = cmd.execute()
    proc.waitFor()
    if (proc.exitValue() != 0)
            {
                throw new BuildException("failed to build csharp project: ${proc.getErrorStream()}", null)
    }
        }
    }

    将CSharpBuildTask.groovy放在RootDir/buildSrc/src/main/groovy/demo下,这样就可以在所有子模块中使用该任务。

    之后,在RootDir/Unity/Bridge_Unity目录下创建build.gradle文件,作为此模块的构建脚本。内容为:

    import demo.CSharpBuildTask
    def buildTypes = ["Release", "Debug"]
    def localProps = new Properties()
    localProps.load(project.file('local.properties').newDataInputStream())
    buildTypes.each { v ->
    task("buildLib$v", type: CSharpBuildTask) {
    builderCmd = localProps["msbuild.dir"].toString()
    solutionFile = new File("Unity_Bridge")
    configuration = v
    }
    }

    local.properties文件位于RootDir/Unity/Bridge_Unity,内容为:

    # local.properties
    msbuild.dir=/usr/local/bin/xbuild
    至此,我们用gradle查看一下是否成功创建了此构建任务:
    $ gradlew tasks



    
    可以看到,buildLib构建任务已经创建。
    

    Unity Demo的构建

    受限于Unity,只有Unity Pro及以上版本才支持代码或者命令行的方式进行构建。

    首先,我们需要在/Asset/Editor中创建一个脚本,通过BuildPipeLine来构建Unity工程:


    public class BuildScript : MonoBehaviour
    {
        static void BuildAndroid()
        {
            string[] scenes = { "Assets/Demo.unity" };
            BuildPipeline.BuildPlayer(scenes, "AndroidBuild", BuildTarget.Android, BuildOptions.None);
        }
    }

    然后,在RootDir/buildSrc中创建UnityBuildTask:
    class UnityBuildTask extends DefaultTask {
    @Input String unityExePath
    @TaskAction def compile() {
    def cmd = "$unityExePath -quit -batchmode -executeMethod BuildScript.BuildAndroid"
    def proc = cmd.execute()
    proc.waitFor()
    if (proc.exitValue() != 0) {
    throw new BuildException("failed to build unity project", null)
    }
    }
    }

    最后,在RootDir/Unity/Demo_Unity中创建build.gradle,并在其中创建一个构建任务:

    // 有关localProps见前文
    task("buildUnityDemo",type: UnityBuildTask, group: 'build') {
    unityExePath = localProps["unitybuild.dir"].toString();
    }

    BuildPipeLine以及Unity的命令行调用可以参考官方文档:http://docs.unity3d.com/Manual/CommandLineArguments.html

    混合构建

    上面已经介绍了各个模块各自的构建方法,现在,我们将在根模块的构建脚本中将他们串联起来。

    首先来梳理一下所有构建任务之间的依赖关系,已有的构建任务有:



    
    
    

    其中,箭头表示依赖关系,Unity的Demo同时依赖于Unity和Android的SDK,同时还要将生成的SDK拷贝到Unity Demo项目中的特定位置,这样Demo才能正常运行。

    这些构建任务的依赖关系如下图所示:

    我们在根模块中创建这些构建任务:

    copyUnitySDKToDemo:将生成的Unity SDK拷贝到Unity Demo
    copyAndroidSDKToDemo:将生成的Android SDK拷贝到Unity Demo
    buildUnitySDK:buildLib的马甲
    buildAndroidSDK:buildJar的马甲
    buildUnityDemo:构建Unity demo
    buildAndroidDemo: 构建Android demo
    我们可以在根模块的build.gradle中添加这些任务,但会使得build.gradle变得非常混乱。因此我们采用Plugin的方式,来进行这些任务的创建。

    现在我们来创建SDKBuildPlugin,在RootDir/buildSrc/src/main/groovy/demo中新建SDKBuildPlugin.groovy:

    package demo
    class SDKBuildPlugin implements Plugin{
        def buildTypes = ["Release", "Debug"]
        @Override
        void apply(Project project) {
        }
    }

    接下来,为每个Build Type创建构建任务。在apply方法中,添加如下代码:

    buildTypes.each { v ->
        project.task("buildAndroidSDK$v",
                dependsOn: ":sdk_android:buildJar$v")
        project.task("buildUnityDemo$v") {
            dependsOn "cleanUnityDemo", "copyUnitySDKToDemo$v", "copyAndroidSDKToDemo$v"
        }
        project.task("copyAndroidSDKToDemo$v", type: Copy) {
            dependsOn "buildAndroidSDK$v"
            from "$androidSDKProjectDir/build/outputs/libs/latest/$v/"
            into "$unityDemoProjectDir/Assets/Plugins/Android"
            include "*.jar"
        }
    // ...
    }

    这里创建了三个典型的任务,其中buildAndroidSDK仅声明其依赖于sdk_android模块的buildJar任务,相当于为buildJar任务创建了一个别名。

    其他的构建任务的创建不做赘述。

    最后在build.gradle中应用此插件:

    // build.gradle
    import com.tencent.qqmusic.MusicUnitySDKBuildPlugin
    // 中间略
    apply plugin: MusicUnitySDKBuildPlugin
    SDK的发布任务

    SDK对外提供的内容比较繁杂,包括:

    SDK的库文件(dll与jar)
    Demo APP或工程
    Demo 工程
    接口文档
    Change log
    这些内容都可以通过gradle的构建任务来自动完成。发布目录的结构如下:

    Publish
    |-- 1.0
    | |-- 1.0.0.0
    | | |-- Debug
    | | |-- Release
    | | |-- ChangeLog.md
    | | |-- 接口文档.md
    | | |-- Demo
    | |
    | |-- 1.0.0.1
    |
    |-- 2.0
    .
    .
    构建任务的结构如下图:



    这里的构建任务都很简单,不做详述。注意拷贝Demo工程的时候,需要过滤掉build结果。

    至此,我们完成了SDK的构建系统。

    如何调试

    C#和Java的调试都只能通过adb远程调试来进行。

    首先用USB连接手机,在命令行中输入adb tcpip 5555

    然后进入adb shell,用ifconfig查看手机的ip地址,之后通过adb connect xxx.xxx.xxx.xxx:5555连接手机

    连接成功之后就可以通过MonoDevelop或者Android Studio的【Attach to process】进行调试了。

    注意:
    如果使用Xamarian进行C#代码的调试,可能无法找到【Attach to process】,这时候需要下载这个插件:
    http://forum.unity3d.com/threads/unity-add-ins-for-monodevelop-xamarin-s...

    如果在Android Studio中无法看到程序的进程,请确保包含Java代码的Android工程已经被正确载入

    附录

    AndroidJavaObject.Call的实现

    这里分C#和Java两部分讲解。

    C#部分

    整个调用序列如下图:



    简单来说,整个流程为:

    通过GetMethodId找到方法对应的内存地址
    创建入参,同时处理AndroidJavaObject、AndroidJavaProxy等特殊类型的参数
    通过内存地址调用目标方法
    其中,最关键的部分在于1.1.1 AndroidJNI.CallStaticObjectMethod,这个方法用于调用Android侧对象或者类的方法,其中:

    ReflectionHelper_classPtr : 指向Java类com.unity.player.ReflectionHelper的指针
    getMethodId_ptr : 指向上述Java类的getMethodID方法的指针
    methodInfo : 包括方法名、签名、是否静态方法等信息
    意思就是,调用Android侧的ReflectionHelper.getMethodID方法,先在Android侧获取到methodInfo描述的Method实例,然后将其指针传回给Unity侧。

    Java部分

    这部分主要是ReflectionHelper这个类,负责获取Android侧类的成员(变量、方法、构造函数),以及创建用于AndroidJavaProxy的Android侧proxy对象。

    AndroidJavaProxy的实现

    首先,我们来看一下如何从AndroidJavaProxy生成一个java.lang.Proxy。

    在上一节中,我们知道,所有的AndroidJavaObject.Call方法都会调用AndroidJNIHelper.CreateJNIArgArray方法,该方法就由AndroidJavaProxy实例生成了一个Proxy实例:

    class _AndroidJNIHelper {
    public static jvalue[] CreateJNIArgArray(object[] args) {
    // ...
    else if (obj is AndroidJavaProxy) {
    array[num].l = AndroidJNIHelper.CreateJavaProxy((AndroidJavaProxy)obj);
    }
    // ...
    }
    }
    虽然AndroidJNIHelper.CreateJavaProxy(AndroidJavaProxy)这个方法是native的,无法分析其实现,但是我们可以参考_AndroidJNIHelper.CreateJavaProxy(int,AndroidJavaProxy)方法:

    // _AndroidJNIHelper
    public static IntPtr CreateJavaProxy(int delegateHandle, AndroidJavaProxy proxy){
    return AndroidReflection.NewProxyInstance(delegateHandle, proxy.javaInterface.GetRawClass());
    }
    两者的实现应该是类似的,最终都是调用Android侧的ReflectionHelper.newProxyInstance方法,用来在Android侧创建一个动态代理:

    // ReflectionHelper
    protected static Object newProxyInstance(int paramInt, final Class[] paramArrayOfClass){
    // ..
    return Proxy.newProxyInstance(ReflectionHelper.class.getClassLoader(), paramArrayOfClass, new InvocationHandler()
    {
    public final Object invoke(Object proxy, Method method, Object[] args)
    {
    return ReflectionHelper.nativeProxyInvoke(this.a, method.getName(), args);
    }
    // ..
    });
    }
    可以看到,Android侧通过ReflectionHelper.nativeProxyInvoke将该侧的方法调用代理到了Unity侧,而Unity侧对应的方法为:

    // UnityEngine._AndroidJNIHelper
    public static IntPtr InvokeJavaProxyMethod(AndroidJavaProxy proxy, IntPtr jmethodName, IntPtr jargs){
    // ...
    IntPtr result;
    using (AndroidJavaObject androidJavaObject = proxy.Invoke(AndroidJNI.GetStringUTFChars(jmethodName), array)){
    if (androidJavaObject == null) {
    result = IntPtr.Zero;
    } else {
    result = AndroidJNI.NewLocalRef(androidJavaObject.GetRawObject());
    }
    }
    return result;
    }



    展开全文
  • Building Plugins for Android This page describes Native Code Plugins for Android. Building a Plugin for Android To build a plugin for Android, you should first obtain the Android NDK ...


    Building Plugins for Android

    This page describes Native Code Plugins for Android.

    Building a Plugin for Android

    To build a plugin for Android, you should first obtain the Android NDK and familiarize yourself with the steps involved in building a shared library.

    If you are using C++ (.cpp) to implement the plugin you must ensure the functions are declared with C linkage to avoid name mangling issues.

    extern "C" {
      float FooPluginFunction ();
    }
    
    

    Using Your Plugin from C#

    Once built, the shared library should be copied to the Assets->Plugins->Android folder. Unity will then find it by name when you define a function like the following in the C# script:-

    [DllImport ("PluginName")]
    private static extern float FooPluginFunction ();
    
    

    Please note that PluginName should not include the prefix (‘lib’) nor the extension (‘.so’) of the filename. You should wrap all native code methods with an additional C# code layer. This code should check Application.platform and call native methods only when the app is running on the actual device; dummy values can be returned from the C# code when running in the Editor. You can also use platform defines to control platform dependent code compilation.

    Android Library Projects

    You can drop pre-compiled Android library projects into the Assets->Plugins->Android folder. Pre-compiled means all .javafiles must have been compiled into jar files located in either the bin/ or the libs/ folder of the project. AndroidManifest.xml from these folders will get automatically merged with the main manifest file when the project is built.

    See Android Library Projects for more details.

    Deployment

    For cross platform deployment, your project should include plugins for each supported platform (ie, libPlugin.so for Android, Plugin.bundle for Mac and Plugin.dll for Windows). Unity automatically picks the right plugin for the target platform and includes it with the player.

    For specific Android platform (armv7, x86), the libraries (lib*.so) should be placed in the following:

    Assets/Plugins/Android/libs/x86/

    Assets/Plugins/Android/libs/armeabi-v7a/

    Using Java Plugins

    The Android plugin mechanism also allows Java to be used to enable interaction with the Android OS.

    Building a Java Plugin for Android

    There are several ways to create a Java plugin but the result in each case is that you end up with a .jar file containing the .class files for your plugin. One approach is to download the JDK, then compile your .java files from the command line with javac. This will create .class files which you can then package into a .jar with the jar command line tool. Another option is to use the EclipseIDE together with the ADT.

    Note: Unity expects Java plugins to be built using JDK v1.6. If you are using v1.7, you should include “-source 1.6 -target 1.6” in the command line options to the compiler.

    Using Your Java Plugin from Native Code

    Once you have built your Java plugin (.jar) you should copy it to the Assets->Plugins->Android folder in the Unity project. Unity will package your .class files together with the rest of the Java code and then access the code using the Java Native Interface (JNI). JNI is used both when calling native code from Java and when interacting with Java (or the JavaVM) from native code.

    To find your Java code from the native side you need access to the Java VM. Fortunately, that access can be obtained easily by adding a function like this to your C/C++ code:

    jint JNI_OnLoad(JavaVM* vm, void* reserved) {
      JNIEnv* jni_env = 0;
      vm->AttachCurrentThread(&jni_env, 0);
      return JNI_VERSION_1_6;
    }
    
    

    This is all that is needed to start using Java from C/C++. It is beyond the scope of this document to explain JNI completely. However, using it usually involves finding the class definition, resolving the constructor (<init>) method and creating a new object instance, as shown in this example:-

    jobject createJavaObject(JNIEnv* jni_env) {
      jclass cls_JavaClass = jni_env->FindClass("com/your/java/Class");         // find class definition
      jmethodID mid_JavaClass = jni_env->GetMethodID (cls_JavaClass, "<init>", "()V");      // find constructor method
      jobject obj_JavaClass = jni_env->NewObject(cls_JavaClass, mid_JavaClass);     // create object instance
      return jni_env->NewGlobalRef(obj_JavaClass);                      // return object with a global reference
    }
    
    

    Using Your Java Plugin with helper classes

    AndroidJNIHelper and AndroidJNI can be used to ease some of the pain with raw JNI.

    AndroidJavaObject and AndroidJavaClass automate a lot of tasks and also use cacheing to make calls to Java faster. The combination of AndroidJavaObject and AndroidJavaClass builds on top of AndroidJNI and AndroidJNIHelper, but also has a lot of logic in its own right (to handle the automation). These classes also come in a ‘static’ version to access static members of Java classes.

    You can choose whichever approach you prefer, be it raw JNI through AndroidJNI class methods, or AndroidJNIHelper together with AndroidJNI and eventually AndroidJavaObject/AndroidJavaClass for maximum automation and convenience.

    UnityEngine.AndroidJNI is a wrapper for the JNI calls available in C (as described above). All methods in this class are static and have a 1:1 mapping to the Java Native Interface. UnityEngine.AndroidJNIHelper provides helper functionality used by the next level, but is exposed as public methods because they may be useful for some special cases.

    Instances of UnityEngine.AndroidJavaObject and UnityEngine.AndroidJavaClass have a one-to-one mapping to an instance of java.lang.Object and java.lang.Class (or subclasses thereof) on the Java side, respectively. They essentially provide 3 types of interaction with the Java side:-

    • Call a method
    • Get the value of a field
    • Set the value of a field

    The Call is separated into two categories: Call to a ‘void’ method, and Call to a method with non-void return type. A generic type is used to represent the return type of those methods which return a non-void type. The Get and Set always take a generic type representing the field type.

    Example 1

    //The comments describe what you would need to do if you were using raw JNI
     AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string"); 
     // jni.FindClass("java.lang.String"); 
     // jni.GetMethodID(classID, "<init>", "(Ljava/lang/String;)V"); 
     // jni.NewStringUTF("some_string"); 
     // jni.NewObject(classID, methodID, javaString); 
     int hash = jo.Call<int>("hashCode"); 
     // jni.GetMethodID(classID, "hashCode", "()I"); 
     // jni.CallIntMethod(objectID, methodID);
    
    
    

    Here, we’re creating an instance of java.lang.String, initialized with a string of our choice and retrieving the hash value for that string.

    The AndroidJavaObject constructor takes at least one parameter, the name of class for which we want to construct an instance. Any parameters after the class name are for the constructor call on the object, in this case the string “some_string”. The subsequent Call to hashCode() returns an ‘int’ which is why we use that as the generic type parameter to the Call method.

    Note: You cannot instantiate a nested Java class using dotted notation. Inner classes must use the $ separator, and it should work in both dotted and slashed format. So \[android.view.ViewGroup$LayoutParams orandroid/view/ViewGroup$LayoutParams can be used, where a LayoutParams class is nested in a ViewGroup\] class.

    Example 2

    One of the plugin samples above shows how to get the cache directory for the current application. This is how you would do the same thing from C# without any plugins:-

     AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer"); 
     // jni.FindClass("com.unity3d.player.UnityPlayer"); 
     AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity"); 
     // jni.GetStaticFieldID(classID, "Ljava/lang/Object;"); 
     // jni.GetStaticObjectField(classID, fieldID); 
     // jni.FindClass("java.lang.Object"); 
    
     Debug.Log(jo.Call<AndroidJavaObject>("getCacheDir").Call<string>("getCanonicalPath")); 
     // jni.GetMethodID(classID, "getCacheDir", "()Ljava/io/File;"); // or any baseclass thereof! 
     // jni.CallObjectMethod(objectID, methodID); 
     // jni.FindClass("java.io.File"); 
     // jni.GetMethodID(classID, "getCanonicalPath", "()Ljava/lang/String;"); 
     // jni.CallObjectMethod(objectID, methodID); 
     // jni.GetStringUTFChars(javaString);
    
    
    

    In this case, we start with AndroidJavaClass instead of AndroidJavaObject because we want to access a static member ofcom.unity3d.player.UnityPlayer rather than create a new object (an instance is created automatically by the Android UnityPlayer). Then we access the static field “currentActivity” but this time we use AndroidJavaObject as the generic parameter. This is because the actual field type (android.app.Activity) is a subclass of java.lang.Object, and any non-primitive typemust be accessed as AndroidJavaObject. The exceptions to this rule are strings, which can be accessed directly even though they don’t represent a primitive type in Java.

    After that it is just a matter of traversing the Activity through getCacheDir() to get the File object representing the cache directory, and then calling getCanonicalPath() to get a string representation.

    Of course, nowadays you don’t need to do that to get the cache directory since Unity provides access to the application’s cache and file directory with Application.temporaryCachePath and Application.persistentDataPath.

    Example 3

    Finally, here is a trick for passing data from Java to script code using UnitySendMessage.

    using UnityEngine; 
    public class NewBehaviourScript : MonoBehaviour { 
    
        void Start () { 
            AndroidJNIHelper.debug = true; 
            using (AndroidJavaClass jc = new AndroidJavaClass("com.unity3d.player.UnityPlayer")) { 
                jc.CallStatic("UnitySendMessage", "Main Camera", "JavaMessage", "whoowhoo"); 
            } 
        } 
    
        void JavaMessage(string message) { 
            Debug.Log("message from java: " + message); 
        }
    } 
    
    
    

    The Java class com.unity3d.player.UnityPlayer now has a static method UnitySendMessage, equivalent to the iOSUnitySendMessage function on the native side. It can be used in Java to pass data to script code.

    Here though, we call it directly from script code, which essentially relays the message on the Java side. This then calls back to the native/Unity code to deliver the message to the object named “Main Camera”. This object has a script attached which contains a method called “JavaMessage”.

    Best practice when using Java plugins with Unity

    As this section is mainly aimed at people who don’t have comprehensive JNI, Java and Android experience, we assume that theAndroidJavaObject/AndroidJavaClass approach has been used for interacting with Java code from Unity.

    The first thing to note is that any operation you perform on an AndroidJavaObject or AndroidJavaClass is computationally expensive (as is the raw JNI approach). It is highly advisable to keep the number of transitions between managed and native/Java code to a minimum, for the sake of performance and also code clarity.

    You could have a Java method to do all the actual work and then use AndroidJavaObject / AndroidJavaClass to communicate with that method and get the result. However, it is worth bearing in mind that the JNI helper classes try to cache as much data as possible to improve performance.

    //The first time you call a Java function like 
    AndroidJavaObject jo = new AndroidJavaObject("java.lang.String", "some_string"); // somewhat expensive
    int hash = jo.Call<int>("hashCode"); // first time - expensive
    int hash = jo.Call<int>("hashCode"); // second time - not as expensive as we already know the java method and can call it directly
    
    
    

    The Mono garbage collector should release all created instances of AndroidJavaObject and AndroidJavaClass after use, but it is advisable to keep them in a using(){} statement to ensure they are deleted as soon as possible. Without this, you cannot be sure when they will be destroyed. If you set AndroidJNIHelper.debug to true, you will see a record of the garbage collector’s activity in the debug output.

    //Getting the system language with the safe approach
    void Start () { 
        using (AndroidJavaClass cls = new AndroidJavaClass("java.util.Locale")) { 
            using(AndroidJavaObject locale = cls.CallStatic<AndroidJavaObject>("getDefault")) { 
                Debug.Log("current lang = " + locale.Call<string>("getDisplayLanguage")); 
    
            } 
        } 
    }
    
    
    

    You can also call the .Dispose() method directly to ensure there are no Java objects lingering. The actual C# object might live a bit longer, but will be garbage collected by mono eventually.

    Extending the UnityPlayerActivity Java Code

    With Unity Android it is possible to extend the standard UnityPlayerActivity class (the primary Java class for the Unity Player on Android, similar to AppController.mm on Unity iOS).

    An application can override any and all of the basic interaction between Android OS and Unity Android. You can enable this by creating a new Activity which derives from UnityPlayerActivity (UnityPlayerActivity.java can be found at/Applications/Unity/Unity.app/Contents/PlaybackEngines/AndroidPlayer/src/com/unity3d/player on Mac and usually at C:\Program Files\Unity\Editor\Data\PlaybackEngines\AndroidPlayer\src\com\unity3d\player on Windows).

    To do this, first locate the classes.jar shipped with Unity Android. It is found in the installation folder (usually C:\Program Files\Unity\Editor\Data (on Windows) or /Applications/Unity (on Mac)) in a sub-folder calledPlaybackEngines/AndroidPlayer/bin. Then add classes.jar to the classpath used to compile the new Activity. The resulting .class file(s) should be compressed into a .jar file and placed in the Assets->Plugins->Android folder. Since the manifest dictates which activity to launch it is also necessary to create a new AndroidManifest.xml. The AndroidManifest.xml file should also be placed in the Assets->Plugins->Android folder (placing a custom manifest completely overrides the default Unity Android manifest).

    The new activity could look like the following example, OverrideExample.java:

    package com.company.product;
    
    import com.unity3d.player.UnityPlayerActivity;
    
    import android.os.Bundle;
    import android.util.Log;
    
    public class OverrideExample extends UnityPlayerActivity {
    
      protected void onCreate(Bundle savedInstanceState) {
    
        // call UnityPlayerActivity.onCreate()
        super.onCreate(savedInstanceState);
    
        // print debug message to logcat
        Log.d("OverrideActivity", "onCreate called!");
      }
    
      public void onBackPressed()
      {
        // instead of calling UnityPlayerActivity.onBackPressed() we just ignore the back button event
        // super.onBackPressed();
      }
    }
    
    

    And this is what the corresponding AndroidManifest.xml would look like:

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.product">
      <application android:icon="@drawable/app_icon" android:label="@string/app_name">
        <activity android:name=".OverrideExample"
                 android:label="@string/app_name"
                 android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
      </application>
    </manifest>
    
    

    UnityPlayerNativeActivity

    It is also possible to create your own subclass of UnityPlayerNativeActivity. This will have much the same effect as subclassing UnityPlayerActivity but with improved input latency. Be aware, though, that NativeActivity was introduced in Gingerbread and does not work with older devices. Since touch/motion events are processed in native code, Java views would normally not see those events. There is, however, a forwarding mechanism in Unity which allows events to be propagated to the DalvikVM. To access this mechanism, you need to modify the manifest file as follows:-

    <?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.company.product">
      <application android:icon="@drawable/app_icon" android:label="@string/app_name">
        <activity android:name=".OverrideExampleNative"
                 android:label="@string/app_name"
                 android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen">
      <meta-data android:name="android.app.lib_name" android:value="unity" />
      <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" />
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
      </application>
    </manifest> 
    
    
    

    Note the “.OverrideExampleNative” attribute in the activity element and the two additional meta-data elements. The first meta-data is an instruction to use the Unity library libunity.so. The second enables events to be passed on to your custom subclass of UnityPlayerNativeActivity.

    Examples

    Native Plugin Sample

    A simple example of the use of a native code plugin can be found here

    This sample demonstrates how C code can be invoked from a Unity Android application. The package includes a scene which displays the sum of two values as calculated by the native plugin. Please note that you will need the Android NDK to compile the plugin.

    Java Plugin Sample

    An example of the use of Java code can be found here

    This sample demonstrates how Java code can be used to interact with the Android OS and how C++ creates a bridge between C# and Java. The scene in the package displays a button which when clicked fetches the application cache directory, as defined by the Android OS. Please note that you will need both the JDK and the Android NDK to compile the plugins.

    Here is a similar example but based on a prebuilt JNI library to wrap the native code into C#.


    From: http://docs.unity3d.com/Manual/PluginsForAndroid.html


    展开全文
  • Found plugins with same names and architectures
  • unity for android 开发

    2016-08-24 11:38:36
    1. 官方参考文档 http://docs.unity3d.com/Manual/PluginsForAndroid.html
  • 从spring下载了一个springboot初始化项目,gradle-wrapper.properties 中使用了 gradle 6.4(如下) distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists ...zipStoreBase=GRADLE_USER_HOME ...
  • 出现错误:Found plugins with same names and architectures     设置一下CPU的选项就可以了
  • C:/Me/JetBrains/IntelliJ IDEA 2018.3.3/plugins/android/lib/builder-test-api-3.1.2.jar;C:/Me/JetBrains/IntelliJ IDEA 2018.3.3/plugins/android/lib/ddmlib-26.1.2.jar;C:/Me/JetBrains/IntelliJ IDEA 2018.3....
  • Android的东西做成插件导出给Unity调用,可以将res中的资源文件一起打包导出!
  • D:/ideaIU-2017.3.3.win/plugins/android/lib/android-common.jar;D:/ideaIU-2017.3.3.win/plugins/gradle/lib/gradle-tooling-api-4.0.jar;D:/ideaIU-2017.3.3.win/lib/gson-2.8.2.jar;D:/ideaIU-2017.3.3.win/...
  • Android Studio 高级配置

    2016-04-17 17:51:13
    Android Studio 提供了一个非常方便的功能帮助我们导入或者导出设置。因此我们在安装一个新的 Android Studio 的时候通常会导入一个之前的设置。但是,最近发生了一件悲剧的事情-我丢失了我的设置文件(幸灾乐祸点...
  • Android Studio提供的代码模板可帮助我们减少重复编写同一段代码的负担,而且可以遵循优化后的设计和标准。AS采用的是Apache FreeMarker模板引擎。 在网上,关于模板开发的资料比较少,而且Studio版本较低,也缺少...
  • ●unity读取不到jar的assets目录,所以要手动用压缩工具打开jar包,把assets里面的文件都拷贝到unity项目的Android/assets目录下。
  • android Studio打开 报错 https://code.google.com/p/android/issues Internal error please report to https://code.google.com/p/android/issues
  • 一、Unity如何调用c#之外的语言的内容 1、c/c++可以编译成.so动态... 把编译好的东西放在Plugins目录下的指定平台目录下即可。比如iOS平台就放在iOS文件夹下面。Android平台根据cpu架构不同放在对应文件夹下面,比如A
  • 解决方法 Player Settings Device Filter设置为ARMv7
  • Android中添加思源字体/NotoSansCJK/SourceHanSans,使得中文字显示更为清晰。
  • Android Studio 中如何引入 layoutlib.jar?https://www.zhihu.com/question/31811664**def getLayoutLibPath() { return “${android.getSdkDirectory().getAbsolutePath()}” + “/platforms/android-25/data/...
1 2 3 4 5 ... 20
收藏数 29,434
精华内容 11,773
热门标签
关键字:

plugins/android