Interaction between Unity and Android (3) - the basic knowledge of Andorid that needs to be understood

【Preface】

In the last article, I just talked about how to realize the interaction between unity and android. To understand the principle, you must first understand some basic knowledge of Android. After understanding, you can also figure out how to access the SDK or write Native plug-ins.

(The following is just a brief introduction, for detailed content, you need to go to the link yourself)

Android Four Components

Activitiy

It provides a window that can contain user interface related components and is mainly used to interact with the user. This window usually fills the entire screen ( Activity can be understood as Canvas in Unity ). Android applications are usually composed of multiple loosely bound to each other . Usually, there will be an Activity designated as MainActivity . When the application is started, the Activity will be activated to present the screen to the user (similar to the main interface in the game). Each Activity can Start another Activity (similar to opening another Canvas in one Canvas ), the switching between Activities is based on the stack mechanism (this mechanism is implemented by the Android system, and the game is generally implemented in UIManager by itself), and the activities communicate through Intent . These activities need to be in AndroidMActivity

In Unity games, there is usually only one Activity, UnityPlayerActivity. This Activity will be the main interface of the game, created and displayed when the game is started, and remains active while the game is running. UnityPlayerActivity is responsible for loading the Unity engine and coordinating the interaction between the game interface and game logic. In Unity, all UI elements, game scenes, special effects, etc. are rendered and displayed through the Unity engine, so there is no need to create multiple Activities to manage multiple windows or interfaces like native Android applications.

Activity has its own life cycle . Calls in this life cycle are associated with parts of Unity's life cycle to make the game run.

Service

Service is very suitable for performing tasks that do not need to interact with the user and have to run in the background for a long time , such as playing sound effects, downloading files, and so on. In Android, the background operation is completely independent of the UI . Service is generally in the background by default, and it does not rely on any user interface when it runs. Even if the Activity is destroyed , the program is switched to the background or another application is opened, the Service can still run normally (the Activity has stopped running at this time). Only when the application process is killed, all services that depend on the process will stop running.

Note that the Service runs in the main thread, that is, the life cycle is controlled by the main thread, so you cannot do time-consuming operations in the Service, which will cause the main thread to block and cause an ANR ( Application Not Responding ) exception . Time-consuming operations are usually completed by opening sub-threads in the Service .

In the Android system, Service has a lower priority. When the system has insufficient memory, it is possible to recycle the Service that is running in the background. If you want the Service to keep running all the time without being recycled due to insufficient system memory, you can consider using the foreground Service. Foreground Service will always have a running icon displayed in the status bar of the system. After pulling down the status bar, you can see more detailed information, which is very similar to the effect of notification.

Content Provider

There are a total of five data storage methods for Android, namely: Shared Preferences, network storage, file storage, external storage, and SQLite. But generally these storages only achieve a data sharing in a single application. Sometimes we need to operate some data of other applications, and we will use ContentProvider. It encapsulates data (table) in a relatively safe way and provides a simple processing mechanism and a unified access interface for other programs to call. And Android provides a default ContentProvider for some common data (including audio, video, pictures and contacts, etc.).

If the data in your own application needs to be accessed by other applications, you need to implement ContentProvider yourself , and use uri (Uniform Resource Locator, Universal Resource Identifier ) ​​to identify which data other applications can access. Each resource has a uri . Other applications access the data provided by ContentProvider through ContentResolver , locate the data they want to access through URI , and monitor the data changes of ContentProvider through ContentProvider . ( Unity 's Addressable has a similar concept to this mechanism)

Broadcast Receiver

Seeing the broadcast, no matter where it is, we all know that it is used for cross-module communication. In Android, it is used for message communication between multiple different components in the application and between different application components. Broadcasts are divided into ordered broadcasts, sticky broadcasts, local broadcasts, system broadcasts, etc. System broadcasts will be sent out such as when the phone is turned on, the network status changes, charging starts, the screen is turned on and off, etc., and the app that registers the relevant broadcasts will know it. The broadcast is encapsulated in the Intent , which will contain necessary information such as the broadcast type and broadcast parameters.

In Unity games published to the Android platform, whether to use Service, ContentProvider, and broadcast receiver depends on the specific needs of the game. Unity provides the Android Java plug-in mechanism, which allows us to use Java code to create and manage these components to achieve more complex functions.

【AndroidManifest.xml】

Every Android application must contain an AndroidManifest.xml, and the file name is fixed and cannot be modified. The application needs to provide some necessary information to the Android system through it, and it needs to be provided to the system before running. This information includes:

  1. The application's package name, which usually matches the code's namespace .
  2. The components of the application, that is, the four major components that need to be used. Each Activity, Service, and Content Provider needs to be configured in the file. If it is not configured, it will not start, that is, it will not be used. The registration of Broadcast Receiver is divided into static registration (configured in the AndroidManifest file) and dynamic creation through code and registration to the system by calling Context.registerReceiver(). Static registration will always be active as the system starts, even if the program is not running, it will be triggered as long as an interested broadcast is received.
  3. Permissions an app needs to access protected parts of the system or other apps .
  4. The hardware and software features required by the application .

AndroidManifest file structure

The official website of the file results is very detailed. If you don’t understand it, you can just search it on the official website. This is a must-see

How Unity generates AndroidManifest

First, Unity has a default AndroidManifest file located at:

Unity\Editor\Data\PlaybackEngines\AndroidPlayer\Apk

Secondly, you can rewrite the AndroidManifest file as needed:

Again, all connected plug-ins or SDKs may have their own AndroidManifest files in the future

Finally, when building the application, Unity will combine all the above files into one file, modify the manifest, automatically add permissions, configuration options, used features and other information to the manifest, and generate the final AndroidManifest file.

Instance AndroidManifest analysis

Take the apk file packaged in the previous example and add some other things as an example for analysis

<?xml version="1.0" encoding="utf-8" standalone="no"?>                                                                                      <!-- 安装位置是外部存储 -->                 <!-- 包名,在unity中填写的 --> 
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:compileSdkVersion="32" android:compileSdkVersionCodename="12" android:installLocation="preferExternal" package="com.test.UnityTest" platformBuildVersionCode="32" platformBuildVersionName="12">
    <supports-screens android:anyDensity="true" android:largeScreens="true" android:normalScreens="true" android:smallScreens="true" android:xlargeScreens="true"/>
    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/> <!-- 读取外部存储权限 -->
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- 写入外部存储权限 -->
    <uses-permission android:name="android.permission.MOUNT_UNMOUNT_FILESYSTEMS"/> <!-- 挂载文件系统权限 -->
    <uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE"/> <!-- 写入媒体存储权限 -->
    <uses-feature android:glEsVersion="0x00030000"/>
    <uses-feature android:name="android.hardware.vulkan.version" android:required="false"/>  <!-- uses-feature表示需要使用的硬件,使用vulkan渲染 --> 
    <uses-permission android:name="android.permission.INTERNET"/> <!-- 网络权限 -->
    <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <!-- 定位权限 -->
    <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION"/> <!-- 定位权限 -->
    <uses-feature android:name="android.hardware.location.gps" android:required="false"/> <!-- 需要使用gps定位 -->
    <uses-feature android:name="android.hardware.location" android:required="false"/> <!-- 使用定位 -->
    <uses-feature android:name="android.hardware.touchscreen" android:required="false"/> <!-- 使用触摸屏 -->
    <uses-feature android:name="android.hardware.touchscreen.multitouch" android:required="false"/> <!-- 使用多点触摸 -->
    <uses-feature android:name="android.hardware.touchscreen.multitouch.distinct" android:required="false"/>   <!-- 使用多点触摸 -->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/> <!-- 读取媒体图片权限 -->
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO"/>   <!-- 读取媒体视频权限 -->
    <uses-permission android:name="android.permission.READ_MEDIA_AUDIO"/>   <!-- 读取媒体音频权限 -->
    <application android:debuggable="true" android:extractNativeLibs="true" android:icon="@mipmap/app_icon" android:label="@string/app_name" android:requestLegacyExternalStorage="true">
        <activity android:configChanges="density|fontScale|keyboard|keyboardHidden|layoutDirection|locale|mcc|mnc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|touchscreen|uiMode" android:exported="true" android:hardwareAccelerated="false" android:launchMode="singleTask" android:name="com.unity3d.player.UnityPlayerActivity" android:resizeableActivity="false" android:screenOrientation="fullSensor" android:theme="@style/UnityThemeSelector"> 
            <intent-filter>
                <action android:name="android.intent.action.MAIN"/> <!-- activity中有这一行表示其被设置为Main Activity 启动应用时首先显示哪一个Activity --> 
                <category android:name="android.intent.category.LAUNCHER"/><!-- 表示activity应该被列入系统的启动器(launcher)(允许用户启动它)。Launcher是安卓系统中的桌面启动器,是桌面UI的统称。 -->
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true"/> <!-- meta-data是一种键值对,用于向组件提供配置信息,这里表示该activity是unity的activity -->
            <meta-data android:name="android.notch_support" android:value="true"/> <!-- notch_support表示是否支持刘海屏 -->
        </activity>
        <meta-data android:name="unity.splash-mode" android:value="0"/>     <!-- 0表示不显示splash screen -->
        <meta-data android:name="unity.splash-enable" android:value="true"/> <!-- true表示显示splash screen -->
        <meta-data android:name="unity.allow-resizable-window" android:value="false"/> <!-- false表示不允许改变窗口大小 -->
        <meta-data android:name="notch.config" android:value="portrait|landscape"/> <!-- notch.config表示刘海屏的配置,portrait表示竖屏,landscape表示横屏 -->
        <meta-data android:name="unity.build-id" android:value="2dd52062-f22a-4914-98d2-35cfe8574afc"/>     <!-- build-id表示构建id -->
    </application>
</manifest>

[UI thread]

In Android, the UI thread is the main thread of an application, which is created when the app starts (that is, each app has a UI thread), and the update of the UI components in the Activity must be performed in the main thread (this It is the same reason that UnityAPI can only be used in the main thread). Calls from other threads will report errors or even crash if they end up involving UI components. The components that other threads call the UI thread are actually the communication between multiple threads. Android provides several ways to access the UI thread from other threads . In Unity, only Activity.runOnUiThread(Runnable) is generally used.

The runOnUiThread() method is a method of the Activity class, which can be called through the current Activity object. This method accepts a Runnable object as a parameter, and the run() method in the Runnable object will be executed in the main thread. If the current thread is the UI thread, it will be executed immediately, if not, it will be sent to an event queue of the UI thread to wait for execution. Its source code is as follows:

public final void runOnUiThread(Runnable action) {
    if(Thread.currentThread() != mUiThread) {
        mHandler.post(action);
    } else {
        action.run();
    }
}

As mentioned earlier, Unity does not use Android to render the UI interface, so Unity has its own main thread, which is not the same thread as Android's UI thread, but we can send messages to the Unity main thread through UnitySendMessage in the UI thread (as in the previous article) shown in this article). Such as playing advertisements, browsing web pages, etc., are generally implemented by Android UI components. Note that some processing must be performed in the UI thread, and runOnUiThread is required.

arr and jar

In the Android project, the app is finally compiled and packaged into an APK file that can run on the Android device. The directory structure of the Android library is the same as that of the Android App.

It contains everything needed to build an APP (such as source code, resource files, and Android Manifest), but it is compiled into an Android Archive (AAR) file that other Android apps depend on when building, that is, multiple apps can use the same arr File (similar to Package in Unity).

An arr file contains source code, resource files, Android Manifest, etc. After decompressing the arr file, a Java Archive (JAR) file containing only source code can be obtained.

[ Static link library and dynamic link library

Static libraries and dynamic libraries are relocatable object files built from other projects, which contain binary codes that can be linked to generate executable files.

In the linking step of building an executable file, your own code and the code copied from the static link library (you only need to copy the part of the library code used by your own code, and do not copy the useless one) form a whole code, For dynamic link libraries, only some relocation and symbol table information will be copied.

When the program is running, the code in the static link library and its own code are loaded into the memory as a whole, and the dynamic link library loads the library according to the relocation and symbol table information when it is used, and finds the function that needs to be called.

Therefore, for the same piece of code, the executable file built using the static library is larger than the one using the dynamic library. If multiple different executable files use the same static library, there will be multiple copies of the static library code in memory. , and the dynamic library will only have one copy.

In Linux/Unix systems, the extension of the static library is generally .a (archive), and the extension of the dynamic library is generally .so (share object)

The static library extension used by the VC compiler in the Windows system is generally .lib, and the dynamic library extension is generally .dll (dynamic link library)

Android so file

Android is based on Linux Kernl. When developing Android applications, sometimes the coding of the Java layer cannot meet the implementation requirements, so the so file generated after the C/C++ implementation needs to be loaded and called by System.loadLibrary(). Common scenarios such as: encryption and decryption algorithms, audio and video codecs, etc. Different CPU architectures have different specifications for the executed binary files, so when generating the so file, it is necessary to consider adapting to different mobile phone CPU architectures on the market.

【SDK generation】

  1. Write source code: generally use C/C++ to write the function code that needs to be implemented, which is convenient for cross-platform
  2. Compile source code: use a compiler to compile source code into library files, such as .so or .a files
  3. Create a call interface: If it is for the Android platform, Java Native Interface (JNI) is required to create a Java interface so that Java code can use the functions and methods in the library file
  4. Packaging SDK: If it is for the Android platform, package the library files and written Java interfaces into an Android SDK for other developers to use
  5. Test SDK: Test the generated SDK to ensure that it can run normally
  6. Write SDK development, API interface description and other documents
  7. Publish SDK

Guess you like

Origin blog.csdn.net/enternalstar/article/details/130999970