A, interaction,

1. Create an Android project

Using Android Studio as an example, create an Empty project.

Extract mono’s compiled classes.jar package from Unity. The path is {Unity installation location}\Editor\Data\PlaybackEngines\AndroidPlayer\Variations\ Mono \Release\Classes\.

If you can’t find this path, please check whether Android Build Support was not selected when installing Unity. For higher versions, you can add modules directly from Unity Hub.

If you don’t have this option, you can download UnityDownloadAssistant from the official website and only install Android Build Support.

Add to your Android project, right click and set Library.

Modify the build. Gradle [the Module].

apply plugin: 'com.android.library' // Change from application to library
dependencies {
    compileOnly files('libs/classes.jar')}Copy the code

Create the MainActivity MainActivity inheriting from UnityPlayerActivity.

public class MainActivity extends UnityPlayerActivity{}Copy the code

Add meta-data to androidmanifest.xml

<activity android:name="com.zeroyiq.sdkdemolib.MainActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <meta-data
        android:name="unityplayer.UnityActivity"
        android:value="true" />
</activity>
Copy the code

2. Unity calls Android

Unity

Called by specifying Class

private const string JAVA_CLASS_Name = "com.unity3d.player.UnityPlayer"; // Unity specifies a Class

/// <summary>
///Calling Android methods
/// </summary>
/// <param name="javaFuncName">The name of the method that Android needs to execute</param>
/// <param name="args">Incoming parameters</param>
public static void CallJavaFunc(string javaFuncName, params object[] args)
{
    #ifUNITY_ANDROID && ! UNITY_EDITOR
        try
        {
            // Create a Java object
            using (AndroidJavaClass jc = new AndroidJavaClass(JAVA_CLASS_Name))
            {
                // Get the current Activity object
                using (AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity"))
                {
                    // Execute the method javaFuncName in the Activity
                    jo.Call(javaFuncName, args);
                }
            }
        }
    catch (System.Exception ex)
    {
        Debug.Log("callSdk error:" + ex.Message);
    }
    #endif
}

public static string CallJavaFuncWithReturnValue(string javaFuncName, params object[] args)
{
    string returnValue = string.Empty;
    #ifUNITY_ANDROID && ! UNITY_EDITOR
        try
        {
            using (AndroidJavaClass jc = new AndroidJavaClass(JAVA_CLASS_Name))
            {
                using (AndroidJavaObject jo = jc.GetStatic<AndroidJavaObject>("currentActivity"))
                {
                    returnValue = jo.Call<string>(javaFuncName, args);
                }
            }
        }
    catch (System.Exception ex)
    {
        Debug.Log("callSdk error:" + ex.Message);
    }
    #endif
        return returnValue;
}

public static AndroidJavaObject CreateJavaMapFromDictainary(IDictionary<string.string> parameters)
{
    AndroidJavaObject javaMap = new AndroidJavaObject("java.util.HashMap");
    IntPtr putMethod = AndroidJNIHelper.GetMethodID(javaMap.GetRawClass(), "put"."(Ljava/lang/Object; Ljava/lang/Object;) Ljava/lang/Object;");

    object[] args = new object[2];
    foreach (KeyValuePair<string.string> kvp in parameters)
    {
        Debug.LogFormat("CreateJavaMapFromDictainary k = {0}", kvp.Key);
        Debug.LogFormat("CreateJavaMapFromDictainary v = {0}", kvp.Value);
        if(kvp.Key ! =null&& kvp.Value ! =null)
        {
            using (AndroidJavaObject k = new AndroidJavaObject("java.lang.String", kvp.Key))
            {
                using (AndroidJavaObject v = new AndroidJavaObject("java.lang.String", kvp.Value))
                {
                    args[0] = k;
                    args[1] = v; AndroidJNI.CallObjectMethod(javaMap.GetRawObject(), putMethod, AndroidJNIHelper.CreateJNIArgArray(args)); }}}else
        {
            Debug.LogWarning("Invalid Dictainary paramters"); }}return javaMap;
}
Copy the code

Android

Add methods in MainActivity

public class MainActivity extends UnityPlayerActivity{
    // This is done in Unity with CallJavaFunc("login","inputString")
    public void login(String platformName) {
        Log.i(TAG, "[" + platformName + "]Begin Login by Unity."); . Log.i(TAG,"[" + platformName + "]End Login by Unity.");
    }

    public void logout(a) {... }}Copy the code

3. Android calls Unity

Android

Sends messages through the UnityPlayer interface. MessageHandler corresponds to the name of the GameObject used to receive information in the Unity scenario. ReceiveLogInfo is the execution method name, MSG is the transmission parameter, and “” is not passed.

public static void unityLogInfo(String tag, String msg) {
    Log.i(TAG, msg);
    Log.i(TAG, "UnityLogInfo: Begin");
    UnityPlayer.UnitySendMessage("MessageHandler"."ReceiveLogInfo", msg);
    Log.i(TAG, "UnityLogInfo: End");
}
Copy the code

Unity

Create the corresponding GameObject. Note that methods cannot be static.

m_MessageHandler = new GameObject("MessageHandler");
m_MessageHandler.AddComponent<MessageHandler>();
public class MessageHandler : MonoBehaviour
{
    public void ReceiveLogInfo(string log){ Debug.Log(log); }}Copy the code

Second, the out of the package

Implementation scheme: Build the Android project into a JAR or AAR and add it to the Unity project specified path (Assets\Plugins\Android\). You can call the methods in Class through the relevant Android API provided by Unity.

The difference between JAR and AAR is that THE JAR package is the bytecode archive after code compilation and needs to import resources by itself. Aar is the Android archive format and has its own resources. It is recommended to use AAR directly.

1. A Export JAR (two options)

Add an export Task to build.gradle[Module].

// Define the SDK package name
def SDK_BASENAME = "UnitySDKDemo"
// Define the SDK package version
def SDK_VERSION = "_V1. 0.0"
// SDK package generates address
def SDK_PATH = "build/libs"
// Delete the previous Jar package to ensure that the generated package is up to date each time
task deleteOldJar(type: Delete) {
    delete SDK_PATH + SDK_BASENAME + SDK_VERSION + '.jar'
}
task exportJar(type: Copy) {
    // Copy from the source address
    from('build/intermediates/packaged-classes/release/')
    / / store
    into(SDK_PATH)
    / / import
    include('classes.jar')
    / / renamed
    rename('classes.jar', SDK_BASENAME + SDK_VERSION + '.jar')}// Execute the script file
exportJar.dependsOn(deleteOldJar, build)
Copy the code

The execution.

Add the jar package generated in build/libs to Assets\Plugins\Android\ of the Unity project. If you can’t find the build folder, check whether the Project mode is set incorrectly and change it to Project Files.

In addition, the two directories in the Android project, [Module]\libs\ (except the classes.jar we added above) and [Module]\ SRC \main\res\, also need to be copied to Assets\Plugins\Android\.

1. B Export aar (two options)

Assemble.

Add the AAR package under Build \outputs\aar\ to Assets\Plugins\Android\ of the Unity project.

Note that you can open the AAR package with compression software and check for the presence of classes.jar in the LIBS. If so, remove it manually.

2. Configure Androidmanifest and Gradle

Androidmanifest and Gradle use the default configuration when Unity builds.

Androidmanifest

The default path is {Unity installation location}\Editor\Data\PlaybackEngines\AndroidPlayer\APK\

Androidmanifest.xml can be added to Assets\Plugins\Android\ instead of the default configuration. Details are provided in the official documentation.

Let’s copy the contents from the Android project’s Androidmanifest.xml.


      
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.zeroyiq.sdkdemolib"
    xmlns:tools="http://schemas.android.com/tools"
    android:installLocation="preferExternal">
        <supports-screens
        android:smallScreens="true"
        android:normalScreens="true"
        android:largeScreens="true"
        android:xlargeScreens="true"
        android:anyDensity="true"/>

    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

    <application
        android:theme="@style/UnityThemeSelector"
        android:resizeableActivity="true">
        <meta-data android:name="unityplayer.SkipPermissionsDialog" android:value="true" />
        <activity
            android:name="com.zeroyiq.sdkdemolib.MainActivity"
            android:label="@string/app_name">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
                <category android:name="android.intent.category.LEANBACK_LAUNCHER" />
            </intent-filter>
            <meta-data android:name="unityplayer.UnityActivity" android:value="true" />
        </activity>.</application>

</manifest>
Copy the code

Gradle configuration

The default path is {Unity installation location}\Editor\Data\PlaybackEngines\AndroidPlayer\Tools\GradleTemplates\

Instead of the default configuration, you can add a file with the same name to Assets\Plugins\Android\. The meaning of each file is specified in the official documentation.

Here we use mainTemplate. Gradle to add content based on the Android project.

// GENERATED BY UNITY. REMOVE THIS COMMENT TO PREVENT OVERWRITING WHEN EXPORTING AGAIN
buildscript {
    repositories {
        google()
        / / warehouse
    }
    dependencies {
        classpath 'com. Android. Tools. Build: gradle: 3.2.0'}}allprojects {
   repositories {
      google()
      flatDir {
        dirs 'libs'
      }
   }
}

apply plugin: 'com.android.library'

dependencies {
    implementation fileTree(dir: 'libs'.include: ['*.jar'])
**DEPS**
    / / rely on
}

android {
    compileSdkVersion **APIVERSION**
    buildToolsVersion '**BUILDTOOLS**'

    defaultConfig {
consumerProguardFiles 'proguard-unity.txt'**USER_PROGUARD**
        minSdkVersion **MINSDKVERSION**
        targetSdkVersion **TARGETSDKVERSION**

        ndk {
            abiFilters **ABIFILTERS**
        }
        versionCode **VERSIONCODE**
        versionName '**VERSIONNAME**'
    }

    lintOptions {
        abortOnError false
    }

    aaptOptions {
        noCompress '.unity3d'.'.ress'.'.resource'.'.obb'.'.mp4'.'.png'
    }
}

**SOURCE_BUILD_SETUP**
Copy the code

3. A apk is derived

Build apK directly in File\Build Settings.

3. B Export the project

Select “Export Project” in “File\Build Settings”. The exported Project can be opened directly with Android Studio, which can easily solve some configuration conflicts. It is also more convenient to compile and debug through Android Studio.

4. (Support conflict)

Android Support and Androidx libraries do not coexist, but conflict occurs when adding dependencies. There are many solutions.

If 3.a exports apK, run gradlew -q {Module name}:dependencies in Android Studio.

Then add the following code to the mainTemplate.gradle dependency.

dependencies{... implementation ('com. Twitter. SDK. The android: twitter: 3.1.1') {exclude group: 'com.android.support' // Exclude the support dependency}... }Copy the code

If the project is 3.b, you can directly migrate the project to AndroidX in Android Studio.