Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice

AndroidJavaObject and Null References

Discussion in 'Android' started by danly, Nov 17, 2012.

  1. danly

    danly

    Joined:
    Nov 15, 2012
    Posts:
    3
    At the moment I am trying to hook in a dynamically-generated IntentFilter object against a 'sticky' or, in some cases, ignorable broadcast. As such, I need to pass in a Null BroadcastReceiver object into the first parameter of Context.registerReceiver. This is the expected usage of the method.

    The problem is I can't determine how to create such a thing.

    The AndroidJavaObject binding isn't intelligent enough to turn a C# null into a Java null of the correct type. So, my bet was to cast the Java null reference to the correct type. The problem with that is that 0xFFFFFFFF is interpreted as a weak reference by the JNI and exhibits an exception.

    So, I tried building the correct object via first creating a new AndroidJavaObject of type java.lang.Object, which appears to default to the Java null object reference, then casting it with the java.lang.Class static method fromName. But that generates a null reference exception.

    The casting itself is necessary because a java.lang.Object is not of the correct type and the Unity AndroidJavaObject can't find the correct method to invoke via Call if that's the case.

    Is there no way to pass a null reference to a Java method as a parameter? :confused:
     
  2. danly

    danly

    Joined:
    Nov 15, 2012
    Posts:
    3
    Bump, no one?
     
  3. danly

    danly

    Joined:
    Nov 15, 2012
    Posts:
    3
    Figured it out:

    Code (csharp):
    1.  
    2.     public static void RegisterFilter(AndroidJavaObject filter) {
    3.         System.IntPtr clazz = Activity.GetRawClass();
    4.         // http://www.rgagnon.com/javadetails/java-0286.html
    5.         System.IntPtr method = AndroidJNI.GetMethodID(clazz, "registerReceiver", "(Landroid/content/BroadcastReceiver;Landroid/content/IntentFilter;)Landroid/content/Intent;");
    6.         UnityEngine.jvalue[] args = AndroidJNIHelper.CreateJNIArgArray(new [] { null, filter });
    7.         AndroidJNI.CallObjectMethod(Activity.GetRawObject(), method, args);
    8.     }
    9.  
     
  4. supernat

    supernat

    Joined:
    May 3, 2011
    Posts:
    45
    I just spent some time trying to figure out the opposite problem, getting a null value from a java object into Unity. Using null doesn't work, because the object itself is valid. What I did was test the raw object's integral value for 0 to find out if it was null.

    C# code:
    Code (csharp):
    1. AndroidJavaClass cls = new AndroidJavaClass("com.mycompany.myproduct.myclass");
    2. AndroidJavaObject obj = cls.CallStatic<AndroidJavaObject>("getStringArray");
    3. if (obj.GetRawObject().ToInt32() != 0)
    4. {
    5.   // String[] returned with some data!
    6.   String[] result = AndroidJNIHelper.ConvertFromJNIArray<String[]>(obj.GetRawObject());
    7.   foreach (String str in result)
    8.   {
    9.     // Do something with the strings
    10.   }
    11. }
    12. else
    13. {
    14.   // null String[] returned
    15. }
    16. obj.Dispose();
    17. cls.Dispose();
    18.  
    java code:
    Code (csharp):
    1.  
    2. public static String[] getStringArray()
    3. {
    4.   String[] strs = { "Test1", "Test2" };
    5.   return strs;   // Or alternatively, return null;
    6. }
    7.  
    The reason I point this out is that 1) I couldn't find an example of getting String[] from Android through the Unity JNI (and this works), so hopefully someone will stumble on it. and 2) I'll bet, but I can not promise, that if you sent a JNIObject with it's Raw Object (i.e. the IntPtr) set to 0, that would be an alternative to sending null to the java function. Unfortunately I don't need to do that, so I can't verify right now.
     
    electric_jesus likes this.
  5. MFen

    MFen

    Joined:
    Sep 25, 2013
    Posts:
    13
    @supernat, I am trying to implement your solution and keep coming up with:

    JNI: Unable to find method id for 'getStringArray' (static)
    UnityEngine.AndroidJavaObject:CallStatic(String, Object[])
    inAppBillingHandler:Instantiate(Int32) (at Assets/customScripts/inAppBillingHandler.cs:14)
    inAppBillingManager:Start() (at Assets/customScripts/inAppBillingManager.cs:10)

    and

    JNI: Init'd AndroidJavaObject with null ptr!
    UnityEngine.AndroidJavaObject:CallStatic(String, Object[])
    inAppBillingHandler:Instantiate(Int32) (at Assets/customScripts/inAppBillingHandler.cs:14)
    inAppBillingManager:Start() (at Assets/customScripts/inAppBillingManager.cs:10)

    First, are you able to test this in editor or must you move to android device?
    Second, I changed the (new AndroidJavaClass("com.mycompany.myproduct.myclass");) to what it is in my package identifier as well as myclass to IabHelper (trying to implement in-app billing) IabHelper.java is located in Plugins/Android folder.

    Does all of the above sound correct or am I missing some step?
     
  6. supernat

    supernat

    Joined:
    May 3, 2011
    Posts:
    45
    @MFen getStringArray was just an example method that would return a string[]. In other words, if you have a method that is returning a string[], you would use the name of your method in place of getStringArray. You have to test on the device, so you should wrap this in a #if ANDROID (or whatever constant there is for Android, I forget now). If you're only doing Android, you could also just say if (!Application.isEditor).

    You are correct in setting the class name. It should be your fully qualified package name. I should have chosen a better name for the getStringArray method, as that isn't a real method, but it sounds like one, lol. Sorry.
     
  7. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,559
    I wonder how this call seems to work for you.

    The internal constructor for AndroidJavaObject is set to throw in case it's initialized with a "null pointer" (IntPtr.Zero):

    upload_2015-4-16_19-48-38.png
     
  8. supernat

    supernat

    Joined:
    May 3, 2011
    Posts:
    45
    Sorry, I don't follow your question. Could you describe the problem in more detail?
     
  9. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,559
    I am using code that may return null from Java, for example:
    Code (CSharp):
    1. var intent = AndroidUtil.Activity.Call<AndroidJavaObject>("getIntent");
    2. var extras = intent.Call<AndroidJavaObject>("getExtras"); // can return null
    The second line will throw an exception since the AndroidJavaObject constructor will throw in case it's initialized as "null" (with IntPtr.Zero).

    I asked - how did that code work for you ?
     
  10. supernat

    supernat

    Joined:
    May 3, 2011
    Posts:
    45
    Oh I see, yes that part is out of date, I get an exception now. I ended up not sending a null but a single string with a unique entry to identify null. Maybe you could put a try/catch block and catch it.
     
  11. cgulliver

    cgulliver

    Joined:
    Aug 12, 2013
    Posts:
    2
    Not specific to JNI, but when calling methods on AndroidJavaObjects in C#, which require passing a null parameter you will need to pass in a null referenced AndroidJavaObject.

    Example:
    Code (CSharp):
    1. AndroidJavaObject nullObject = null;
    2. AndroidJavaClass javaClass = new AndroidJavaClass("com.my.class")
    3. AndroidJavaObject returnObject = javaClass.CallStatic<AndroidJavaObject>("staticFunctionNameWithNullParameter", "parameter1", "parameter2", nullObject );
    Passing a C# null, or IntPtr.Zero both get filtered out before binding with the Java method, which causes an incorrect number of parameters to be passed along to Java, which ultimately results in a method signature mismatch.

    Edit: Not to resurrect an old thread. However, when trying to solve this problem for myself this is the thread that kept being the top pick from Google. I figured it was a good enough place to store the solution to my problem in hopes that it helps others.
     
    liortal likes this.
  12. liortal

    liortal

    Joined:
    Oct 17, 2012
    Posts:
    3,559
    Have you tested that ? Seems very unlikely that would work to me.
    Looking at the code in Unity 5.1 beta 5, the arguments passed to JNI are parsed like this (peeked using MonoDevelop's decompilation):

    upload_2015-5-2_19-42-44.png
    As you can see, a null value passed does not have any "type" associated to it at all, so it wouldn't make any different whether you have a null string or a null AndroidJavaObject.

    I also think that at the C# language level, a null reference does not have any type, so again, this shouldn't make any difference.