From time to time, its handy or necessary to communicate with the underlying OS (Android, iOS, Windows, etc) from Unity.  You may want to integrate a third party Android SDK, or you may want to access device data that is exposed by the OS, but is not exposed by Unity.  For example, I mentioned in a previous post on save data that Unity exposes a single data path variable, whereas Android actually has two, one for internal storage and one for external (such as an SD card).  You can write a simple plugin that gives you access to both pieces of data (which is exactly what we’ll do here).

The methods used to communicate with the OS depend on the OS’s native language, tools, SDKs, and requirements imposed by Unity. For Android, plugins are written either in Java using the Android SDK and Eclipse, or in C/C++.  This post will explain the process of building an Android plugin in Java, from scratch, including it in a Unity project, and calling the methods from your C# or Javascript code.  C/C++ is only beneficial if you need to access Android NDK features, which is another topic entirely.

Installing the Tools

In order to develop and use Android plugins, you’ll need to install the following tools:

  1. Android SDK
  2. Eclipse + ADT Plugin
  3. Unity + Unity Android

Google currently offers a download bundle that has the Android SDK, Eclipse, and the ADT plugin in a single installer.  Though the bundle comes with the latest Android platform included, it is best to use the earliest version of the Android platform that your Unity game supports – usually 2.2 or 2.3 at the time of writing.  You can get instructions on downloading different Android platforms here.

Setting up Eclipse

Setting up Eclipse isn’t too tricky.  First, make sure that Eclipse knows where the Android SDK is.  To do this, go to Window > Preferences > Android and point to the SDK location on your drive. Once you hit Apply, you should see the Android platforms that you’ve got installed on your machine:

android_prefs

Also, you’ll want to choose an appropriate workspace location.  By default, Eclipse will choose your Home or Users folder.  If you are writing a plugin for a specific Unity project, I’d suggest putting the workspace in the project folder, next to the “Assets” and “Library” folders.  You can create a “Plugins/Android” subfolder or something similar. If the plugin is shared between many Unity projects, it might be better to put the workspace in a shared location.

You can then switch the Eclipse workspace to that location using the File > Switch Workspace > Other… command in Eclipse. Eclipse will save all your project files in the workspace directory you choose.

Creating an Eclipse Android Project

Next, create a new Android project by going to File > New > Android Application Project.  In the app wizard, you can specify a name for your project, which is not important; it is for your convenience only.  What is important is that you choose a reasonable package name because you’ll be needing to type it out later.  I’d suggest using your game’s bundle ID as a reference.  For example, if your bundle ID is com.mycompany.supercoolgame, then use the package name com.mycompany or com.mycompany.utils.

Finally, I’d recommend setting the “Minimum Required SDK” option to the same value you use for your Unity project.  If your game requires Android 2.2 or higher, set this to 2.2 as well. This should help you avoid using incompatible Android APIs, though I’m not totally sure if Eclipse detects that for you.

Here are some example values:

android_newapp

I’d also suggest that you don’t create an Activity class as part of your plugin. Remember, since this is a plugin to your Unity game, an Activity class called UnityPlayerActivity already exists.  It is actually possible to extend UnityPlayerActivity with your own version, which I’ll explain briefly at the end of this post.

Writing Some Code

Now comes the fun part – you write some code!  Plugin writing can seem daunting because it requires you to be familiar not only with Unity and C#/Javascript, but also the Android SDK, Eclipse, and Java.  Writing a plugin will require that you know how to write Java and that you know your way around the SDK well enough to accomplish what you’d like.

As an example and companion to my save data post, here is some code that you could use for a hypothetical class that allows you to get the internal and external save data locations from the Android OS; put this in your eclipse project, in a file called Storage.java:

package com.mycompany;
import android.os.Environment;
import android.app.Activity;
import android.util.Log;

public class Storage
{
    public static String GetInternalStoragePath(Activity pActivity)
    {
    	String path = "";
    	try
    	{
	    path = pActivity.getApplicationContext().getFilesDir().getAbsolutePath();
    	}
    	catch(Error error)
    	{
    	    Log.i("GetLocalPath: ", " Error");
    	}

    	return path;
    }

    public static String GetExternalStoragePath(Activity pActivity)
    {
    	String path = "";
    	try
    	{
    		String state = Environment.getExternalStorageState();

    		// Ensure that the external storage is mounted.
    		if ((Environment.MEDIA_MOUNTED.equals(state)) || (Environment.MEDIA_MOUNTED_READ_ONLY.equals(state)))
    		{

    	    	    // Get external storage path.
    	    	    path = Environment.getExternalStorageDirectory().getAbsolutePath();

    	    	    // If the path is invalid, use "default" external path (and hope it works!)
    	    	    if(path.length() > 0)
    	    	    {
    	    	        path += "/Android/data/" +  pActivity.getApplicationContext().getPackageName() + "/files/";
    	    	    }
    		}
    	}
    	catch(Error error)
    	{
    		Log.i("GetExternalPath:", " Error");
    	}
    	return path;
    }
}

Granted, the error messages could be a bit better, but it gets the job done.  GetInternalStoragePath simply calls the OS method to retrieve a valid internal storage path.  GetExternalStoragePath first queries the environment to see whether an SD card is mounted available to read, then gets the path if it is available.

Compile the Plugin Code to a JAR File

This step is extremely easy: just right-click on your project in Eclipse and select Export.  Select Java > JAR File.  You will be presented with several JAR export options; I have never had a need to change any from the defaults.  The only change you’ll need to make is to set the export destination to a valid location.  Hit the Finish button, and you’ve got your JAR plugin.

Integrate the JAR Plugin into Unity

This is another easy step; simply copy your JAR file to your Assets/Plugins/Android folder (or create said folder if needs be).  When you create an Android build in Unity, it will automatically include all JAR and lib files in that directory for your game to use at runtime.

Call Plugin Code from Unity

Calling into a native Android Java plugin requires you to make use of the Unity helper classes AndroidJavaClass and AndroidJavaObject.  This isn’t particularly difficult, but the syntax is a bit unusual.  Here is an example of calling one of the methods we defined in the plugin from C# code:

private void Awake()
{
#if (UNITY_ANDROID) && (!UNITY_EDITOR)
    using (AndroidJavaClass unity = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
    {
        using (AndroidJavaObject activity = unity.GetStatic<AndroidJavaObject>("currentActivity"))
	{
	    using (AndroidJavaClass storage = new AndroidJavaClass("com.mycompany.Storage"))
	    {
	        Debug.Log(storage.CallStatic<string>("GetInternalStoragePath", activity));
	    }
        }
    }
#endif
}

The AndroidJavaClass and AndroidJavaObject classes allow us to grab either classes or instances of classes from native and call methods on them – here, we get our own Storage class and call a static method on it, but we also get the UnityPlayer class and grab the “currentActivity” object out of it.  Provided you know your way around the Android SDK, you can actually call a number of SDK methods directly using this method, though it is somewhat clunky.

Note that you must combine your plugin’s package name with the name of the class you created when creating the AndroidJavaClass object.  In our example, we created the plugin in the com.mycompany package and named the class Storage, so the complete path to the class becomes com.mycompany.Storage.  Because we are indirectly accessing native classes, you will unfortunately not see errors until runtime, if they exist.

Deeper Unity Integration

Those are the basics of plugin development; you can see that after your initial creation of the plugin project in Eclipse, iteration can be a pretty tight loop of writing Java code -> exporting the JAR -> writing C# code -> running on device -> repeating.  And while this plugin integrates with the Android SDK, it doesn’t integrate with Unity too closely.  However, this is pretty easily done.

Unity ships with a collection of Java bindings in a JAR file which is in the Unity install directory – it is called classes.jar.  On Windows, you can find the file at C:\Program Files (x86)\Unity\Editor\Data\PlaybackEngines\androidplayer\bin.  On Mac, you can find it \Applications\Unity\Unity.app\Editor\Data\PlaybackEngines\androidplayer\bin.

To integrate your Android plugin with Unity’s classes.jar file, right-click on the project in Eclipse and select Properties.  Then, go to Java Build Path > Libraries.  Here, you can use the Add External JARs button to reference the classes.jar file from your plugin.  If you want to include the JAR, but also want this to work across computers (say, if you are using SVN), then copy classes.jar into your workspace, and then use Add JARs instead (this will use relative paths instead of absolute paths).  You can also use this method to include JAR files for any external SDKs you might need to use.

Finally, after integrated classes.jar, it is possible to extend the UnityPlayerActivity and either add to or overwrite Unity’s default implementations of the Android lifecycle.  See http://docs.unity3d.com/Documentation/Manual/PluginsForAndroid.html for many, many, many more details.

Share →

3 Responses to Writing Android Plugins for Unity

  1. Lior Tal says:

    nice article, thanks !

  2. David says:

    just what i wanted, find a solution and also know the method to make an android plugin for unity. Clark you rocks!

  3. Ben says:

    Just wanted to say thanks for taking the time to write this and the Android save data pitfall articles, much appreciated!

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>