1

Rather long post follows. It's fairly complex, needs someone who has worked with Unity Plugins before (creating them, not using them) and I'm not looking to get downvoted because a few common search terms show up. So, please read the entire post.

So, I've been having a rather weird issue happening when I try to call native functions through Unity's AndroidJavaObject

I've built native plugins in the past before, I'm trying to remove my dependancies on having to update multiple JARs each time I need to update my plugins. So, I'm moving a lot of my native Java code from plugins, into Unity itself.

Specifically, there's a way to call native Android functions (i.e. system functions) straight from C#, rather than creating a plugin and calling a function in the plugin that invokes the native function.

For example, earlier, if I had to create a Toast, I'd have to write some native code in Java, export the plugin and then call the function. These days, one can just call the Toast.makeText straight from C# as follows.

Here's how I make the call for a Toast.

    public static void Toast (string text, int duration) {
        #if UNITY_ANDROID && !UNITY_EDITOR
        CreateActivity();

        activity.Call("runOnUiThread", new AndroidJavaRunnable ( () => {
            new AndroidJavaObject("android.widget.Toast", activity)
                .CallStatic<AndroidJavaObject>("makeText", activity, text, duration)
                    .Call("show");
        }) );
        #endif
    }

and here's the CreateActivity method

private static void CreateActivity () {
    #if UNITY_ANDROID && !UNITY_EDITOR
    if(activity == null)
        activity = new AndroidJavaClass("com.unity3d.player.UnityPlayer").
            GetStatic<AndroidJavaObject>("currentActivity");
    #endif

}

This works perfectly. However, when I try the same approach for creating an AlertDialog, I get an androidjavaexception java.lang.classnotfoundexception

Here's the relevant code

public static void ShowAlert (string title, string message, string buttonText) {

    #if UNITY_ANDROID && !UNITY_EDITOR

    CreateActivity();

    activity.Call("runOnUiThread", new AndroidJavaRunnable ( () => {
        var builder = new AndroidJavaObject("android.app.AlertDialog.Builder", activity);
        ***//Exception happens in the above line***

        builder.Call<AndroidJavaObject>("setMessage", message)  
            .Call<AndroidJavaObject>("setTitle", title)
                .Call<AndroidJavaObject>("setCancelable", false)
                .Call<AndroidJavaObject>("setNeutralButton", buttonText)
                .Call("show");
    }) );
    #endif
}

What I've tried

  1. Used android.support.v7.app.AlertDialog.Builder instead of android.app.AlertDialog.Builder
  2. Added the support JARs for v7 & v4 (Specifically android-support-v4 & android-support-v7-appcompat)
  3. Tried creating my own implementation for Unity's JNI and invoking that

All the above failed. (The third one, rather spectacularly)

Now, I tried other classes as well, and most of them work (such as ProgressDialog, which is a subclass of AlertDialog), and those work!

At this point, I'm rather stumped and I can't think of a VALID reason for certain functions to work, and the others to fail.

Any thoughts are appreciated.

EDIT 1: Added LogCat

I/Unity: AndroidJavaException: java.lang.ClassNotFoundException: android.app.AlertDialog.Builder
                                            at UnityEngine.AndroidJNISafe.CheckException () [0x00000] in <filename unknown>:0 
                                            at UnityEngine.AndroidJNISafe.CallStaticObjectMethod (IntPtr clazz, IntPtr methodID, UnityEngine.jvalue[] args) [0x00000] in <filename unknown>:0 
                                            at UnityEngine.AndroidJavaObject._CallStatic[AndroidJavaObject] (System.String methodName, System.Object[] args) [0x00000] in <filename unknown>:0 
                                            at UnityEngine.AndroidJavaObject.CallStatic[AndroidJavaObject] (System.String methodName, System.Object[] args) [0x00000] in <filename unknown>:0 
                                            at UnityEngine.AndroidJavaObject.FindClass (System.String name) [0x00000] in <filename unknown>:0 
                                            at UnityEngine.AndroidJavaObject._AndroidJavaObject (System.String className, System.Object[] args) [0x00000] in <filename unknown>:0 
                                            at UnityEngine.AndroidJavaObject..ctor (System.String className, System.Object[] args) [0x00000] in <filename unknown>:0 
                                            at AndroidNative+<ShowAlert>c__AnonSto
1
  • Interesting issue! Is it the actual android.app.AlertDialog.Builder class that is triggering the ClassNotFoundException (i.e. and not one of the implicit classes used in the applied method calls)? A logcat excerpt might provide useful extra information. Commented Mar 5, 2016 at 8:12

1 Answer 1

3

The problem you are running into, is that AlertDialog.Builder is a nested class of AlertDialog, and creating nested classes through reflection (which is what AndroidJavaObject is trying to do) requires a slightly different syntax in Java:

android.app.AlertDialog$Builder

There are some other tiny mistakes in your code (that you couldn't know about because your constructor wasn't working):

  • setNeutralButton requires a callback argument (which can be null)
  • show has to be called on the AlertDialog itself, not on the builder.

Taking these tiny corrections into consideration, a working code sample looks like this:

public static void ShowAlert(string title, string message, string buttonText)
{
    CreateActivity();

    activity.Call("runOnUiThread", new AndroidJavaRunnable(() =>
    {
        var builder = new AndroidJavaObject("android.app.AlertDialog$Builder", activity);

        builder.Call<AndroidJavaObject>("setMessage", message)
            .Call<AndroidJavaObject>("setTitle", title)
                .Call<AndroidJavaObject>("setCancelable", false)
                .Call<AndroidJavaObject>("setNeutralButton", buttonText, null)
                .Call<AndroidJavaObject>("create")
                .Call("show");
    }));
}
Sign up to request clarification or add additional context in comments.

2 Comments

If any of the other classes you couldn't access still can't be instantiated using this approach, please add them to your question (or start a new question) and I'll see if I can figure it out. It's good to get some more documentation out there on the whole Unity-Android interop process!
Wow. I have no idea how I missed that. It's not the first time I'm working with nested classes, and now I'm laughing at myself going, "Oh how did you miss that?" Yeah, I know there's some inconsistencies on the rest of it that I'd have to fix. I took some code out (the OnClickListeners) for the purpose of this question. Thanks a bunch.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.