Unity and Android interactive communication series (1)

  Native apps running on the Android platform directly call Android interfaces and can enjoy the advantage of being on a first-come-first-served basis. However, Android apps developed using Unity are like second-class citizens. It is much more troublesome to use Android's native features, such as WiFi, Bluetooth, etc., especially some advanced features, are not fully covered in Unity, and it is not enough to develop directly in Unity. Moreover, in order to adapt to the needs of cross-platform development and deployment, Unity’s engine architecture design is much more complex and flexible. Development based on the Unity engine App applications run in an independent VM (Virtual Machine, virtual machine) ( applications compiled with IL2CPP backend still require virtual machine support when running ), which brings difficulties to the interaction between App applications and Android native system code. In actual application development, there are often interaction requirements between Unity-based App applications and the underlying Android platform. In this series, we mainly study the interactive communication between the Unity engine and the Android platform.

(1) Communication principle between Android and Unity

  The biggest advantage and feature of the Unity engine is one-time production and multi-end deployment, which greatly reduces the development and maintenance costs of multi-platform games. The basis for the Unity engine to achieve powerful cross-platform capabilities is Mono/IL2CPP. Mono/IL2CPP is the cross-platform cross-platform capability of the Unity engine. The core and foundation of the platform.

  In 2001, the telecommunications standards organization ECMA formulated a cross-architecture runtime environment CLI (Common Language Infrastructure) standard specification that is independent of specific languages. As long as the high-level language defined by the specification is used for development, the application complies with the CLI specification. That is, it can ensure cross-platform operation on different computer architectures. On this basis, Microsoft implemented the .NET Framework Common Language Runtime (CLR) according to standards. Therefore, CLR is an implementation of CLI. .NET Framework is a framework that runs on the basis of CLR. It supports C#, VB.NET, C++, Python and other languages, but due to the deep relationship between the .NET Framework and Windows, the .NET Framework itself is not cross-platform.

  Mono is another CLI implementation led by Xamarin. It uses a built-in C# language compiler, CLR runtime and various basic class libraries to enable applications to run on various platforms such as Windows, Linux, FreeBSD, Android, and iOS. , Therefore, Android or iOS applications can be written using C# language through Mono. In the CLI specification, high-level languages ​​are not directly compiled into machine bytecodes, but into intermediate languages ​​(Intermediate Language, IL), which is a specific underlying language between high-level languages ​​​​and machine bytecodes. For hardware-independent languages, IL will be loaded into Mono VM when it is actually needed to be executed [Unity supports three script development languages: C#, Unity Script, and Boo, and all high-level languages ​​will be compiled into IL intermediate language. ], the VM dynamically compiles it into machine code and then executes it (Just In Time, JIT compilation). The execution process is shown in Figure 1(a).
Insert image description here

Figure 1 Schematic flow chart of Mono compilation and running code and IL2CPP compilation and running code
  Through Mono, the Unity engine can deploy applications to various system platforms to achieve cross-platform operation. However, since IL runs in Mono VM, and Mono VM is closely related to the system platform and cannot be cross-platform, Unity needs to maintain a large number of Mono VMs on various platforms, and the JIT compilation method also reduces the running speed of the application. , therefore, the Unity engine introduced the IL2CPP back-end compilation method, which improves code execution by recompiling IL into C++ code, which is then directly compiled into native assembly code (Ahead Of Time, AOT compilation) by C++ compilers on various platforms. Efficiency, and the C++ compiler of each platform can be used to perform optimization during compilation. However, due to the characteristics of high-level dynamic languages, memory management and recycling still require unified maintenance, that is, the IL2CPP VM is required to be responsible for GC, threads and other service work, but at this time IL2CPP VM is no longer responsible for IL loading and dynamic parsing, and the workload of maintaining VM is greatly reduced.


  Although IL2CPP is finally compiled directly into the native machine code of each platform using the AOT method, it inherits the mechanism of the IL intermediate language and has almost no impact on the upper-layer language ( because C++ is a static language, compiled using the AOT method, and some features of the JIT dynamic language will no longer be available ), but it also requires the help of VM for memory management and other work, as shown in Figure 1(b). Android native applications are written in Java language. Java language is first compiled into Bytecode IL intermediate language, and then relies on the dalvik/art virtual machine on Android for interpretation and execution. Therefore, the interactive communication between the Unity engine and Android native code is actually communication between two VMs, as shown in Figure 2. Although Unity code and Android native code are executed in different VMs, they are both in the same process and therefore can share data.

Insert image description here

Figure 2 Schematic diagram of interactive communication between Unity and Android
  In Figure 2, the Unity engine calls Android methods through the API provided by UnityEngine, and Android calls Unity methods through the API provided by the com.unity3d.player package. In this way, the Unity engine can directly call methods of Android classes and objects, while Android can only call methods of the script mounted on the specified GameObject in Unity, or call methods in the Unity engine through a dynamic proxy.

  At the execution level, UnityEngine encapsulates the AndroidJavaObject, AndroidJavaClass, and AndroidJavaProxy classes. Through these classes, you can obtain Android-side static classes or dynamic objects, so that you can execute their corresponding methods; Android communicates with the C# code through the mainActivity of the Unity application [ The interactive communication between Unity and Android described in this section essentially refers to the mutual calls between C# code and Java code, JAR packages, AAR packages, and SO packages, but it is customarily described as communication between the Unity engine and the Android operating system software. ].

  Activity is one of the most basic and important components in Android applications. Its function is similar to the page in the web and the form in winform. Therefore, only active Activity can respond to input. Therefore, the Android code must first pass com.unity3d. The currentActivity under the player.UnityPlayer package obtains the active Activity and uses it to communicate with the Unity code.

(2) Unity directly calls Java code

  Versions after Unity2018 uniformly use Gradle for compilation, building and packaging on the Android side, and Android Studio also uses Gradle for compilation, building and packaging, that is, they all use the same compilation and build tool, that is, both Java code and C# code can be Unity is correctly compiled to the Android side, which lays the foundation for using Java and C# languages ​​directly in Unity.

  In order to facilitate the mapping of Java data structures, several encapsulated classes are built into the UnityEngine class. The most important classes are: AndroidJavaClass, AndroidJavaObject, and AndroidJavaProxy. These classes are the basis for mutual calls between the Java side and the C# side. AndroidJavaClass is the expression of the java.lang.Class class in Unity. It is mainly used for class structure reflection, obtaining class static attributes or calling class static methods. Its public methods are shown in Table 1.

Table 1 AndroidJavaClass public method list
public method Calling object/property type Generic methods describe
Call non-static Call Call object non-static method
CallStatic static CallStatic Call class static method
Get non-static Get Get non-static properties of an object
GetStatic static GetStatic Get class static properties
Set non-static Set Set non-static properties of an object
SetStatic static SetStatic Set class static properties
GetRawClass - GetRawClass Get a pointer to a native Java type for use with JNI
GetRawObject - GetRawObject Get a pointer to a native Java object for use with JNI
Dispose - - IDisposable interface callback

  AndroidJavaObject is the expression of the java.lang.Object class in Unity, and the java.lang.Object class is the base class of all classes in Java, so it can accept all Java objects. Its public methods are shown in Table 2.

Table 2 AndroidJavaObject public method list
public method Calling object/property type Generic methods describe
Call non-static Call Call object non-static method
CallStatic static CallStatic Call class static method
Get non-static Get Get non-static properties of an object
GetStatic static GetStatic Get class static properties
Set non-static Set Set non-static properties of an object
SetStatic static SetStatic Set class static properties
GetRawClass - GetRawClass Get a pointer to a native Java type for use with JNI
GetRawObject - GetRawObject Get a pointer to a native Java object for use with JNI
Dispose - - IDisposable interface callback
  As can be seen from Table 1 and Table 2, the public methods of these two classes are exactly the same, which provides developers with a completely consistent appearance when using these two classes. In use, the object of the corresponding class is generated through the forName() method and .class attribute of the AndroidJavaClass class. Since the Class class method is often used for reflection, it is generally used to call the static properties or methods of the corresponding class; AndroidJavaObject represents the object through its getClass The () method can obtain the type of the object, so it is generally used to call instance methods or properties of the object. For example, there is a class com.davidwang.util, which has a static method StaticMethod() and an instance method InstanceMethod(), then new AndroidJavaClass("com.davidwang.util").callstatic("StaticMethod") is equivalent to calling util. StaticMethod(); and new AndroidJavaObject("com.davidwang.util").call("InstanceMethod") is equivalent to new Util().InstanceMethod().

Guess you like

Origin blog.csdn.net/yolon3000/article/details/135005458