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).
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.
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.
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.
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 |