Search Unity

  1. Unity 6 Preview is now available. To find out what's new, have a look at our Unity 6 Preview blog post.
    Dismiss Notice
  2. Unity is excited to announce that we will be collaborating with TheXPlace for a summer game jam from June 13 - June 19. Learn more.
    Dismiss Notice
  3. Dismiss Notice

Bug "Unity Exception: JNI: Init'd AndroidJavaObject with null ptr!" AndroidJavaObject + System.Threading

Discussion in 'Android' started by Marcos-Schultz, May 14, 2024.

  1. Marcos-Schultz

    Marcos-Schultz

    Joined:
    Feb 24, 2014
    Posts:
    385
    Hello.
    I'm trying to program an AT-10A tablet using Unity for this. The Unity version is 2022.3.21f1.

    The tablet manufacturer provided me with a file "canbus_api.aar", which contains the CanBus communication functions that I need to access.

    I was able to communicate with their API successfully, using the code below:

    Code (CSharp):
    1. using UnityEngine;
    2. public class JavaClassCAN : MonoBehaviour {
    3.     AndroidJavaObject canBusHelper;
    4.     private void Start() {
    5.         canBusHelper = new AndroidJavaObject("com.android.canbus.CanBusHelper");
    6.         //config serial baudrate
    7.         var retConfigSerial = canBusHelper.Call<int>("setSerialBaudrate", 1, 115200, 8, 0, 1); //OK - it works
    8.         if (retConfigSerial < 0) {
    9.             Debug.Log("ERROR - Config Serial Baudrate");
    10.         }
    11.         Debug.Log("Return 'setSerialBaudrate': " + retConfigSerial.ToString());
    12.         //initialize canbus
    13.         var retInitCAN = canBusHelper.Call<int>("initialize", 1, 115200, 500000, false); //OK - it works
    14.         if (retInitCAN < 0) {
    15.             Debug.Log("ERROR - Config CAN");
    16.         }
    17.         Debug.Log("Return 'initialize': " + retInitCAN.ToString());
    18.         //Start read CanBus
    19.         if (canBusHelper != null) {
    20.             var retReadCan = canBusHelper.Call<int>("readCan", 1, new CanBusCallbackProxy()); //OK - it works
    21.             Debug.Log(retReadCan);
    22.         }
    23.         else {
    24.             Debug.LogError("AndroidJavaObject canBusHelper is null");
    25.         }
    26.     }
    27.     public class CanBusCallbackProxy : AndroidJavaProxy {
    28.         public CanBusCallbackProxy() : base("com.android.canbus.CanBusHelper$CanBusCallback") {
    29.         }
    30.         public void onSetError() {
    31.             Debug.Log("zyz0 --> onSetError");
    32.         }
    33.         public void onSendError() {
    34.             Debug.Log("zyz0 --> onSendError");
    35.         }
    36.         public void onIdError(int count) {
    37.             Debug.Log("onIdError: " + count);
    38.         }
    39.         public void onReceiveCanbusData(int FF, int RTR, int DLC, int ID, int[] DATA) {
    40.             Debug.Log("onReceiveCanbusData: FF=" + FF + ", RTR=" + RTR + ", DLC=" + DLC + ", ID=" + ID.ToString("X"));
    41.             string dataString = "DATA: ";
    42.             for (int i = 0; i < DATA.Length; i++) {
    43.                 dataString += DATA[i].ToString("X") + " ";
    44.             }
    45.             Debug.Log(dataString);
    46.         }
    47.     }
    48. }
    It's quite simple and it works, as shown in logcat:



    However, the "readCan" function is blocking, leaving the program stuck inside a while while waiting to receive data, and this obviously blocks the main unity thread and blocks the execution of the program itself.

    The solution to this is apparently simple, just move the "readCan" function to a separate thread and everything will work fine. So I created this code below, making the function initialize in a new Thread.

    Code (CSharp):
    1. using System.Collections.Generic;
    2. using UnityEngine;
    3. using System.Threading;
    4. public class JavaClassCAN : MonoBehaviour {
    5.     AndroidJavaObject canBusHelper;
    6.     private Thread canReadThread;
    7.     private bool runThread;
    8.     private void Start() {
    9.         canBusHelper = new AndroidJavaObject("com.android.canbus.CanBusHelper");
    10.         //config serial baudrate
    11.         var retConfigSerial = canBusHelper.Call<int>("setSerialBaudrate", 1, 115200, 8, 0, 1);
    12.         if (retConfigSerial < 0) {
    13.             Debug.Log("ERROR - Config Serial Baudrate");
    14.         }
    15.         Debug.Log("Return 'setSerialBaudrate': " + retConfigSerial.ToString());
    16.         //initialize canbus
    17.         var retInitCAN = canBusHelper.Call<int>("initialize", 1, 115200, 500000, false);
    18.         if (retInitCAN < 0) {
    19.             Debug.Log("ERROR - Config CAN");
    20.         }
    21.         Debug.Log("Return 'initialize': " + retInitCAN.ToString());
    22.         //Start thread
    23.         runThread = true;
    24.         canReadThread = new Thread(ReadCanData);
    25.         canReadThread.Start();
    26.     }
    27.     private void OnDisable() {
    28.         runThread = false;
    29.         if (canReadThread != null && canReadThread.IsAlive) {
    30.             canReadThread.Join();
    31.         }
    32.     }
    33.     private void ReadCanData() {
    34.         Thread.Sleep(1000);
    35.         if (runThread) {
    36.             if (canBusHelper != null) {
    37.                 var retReadCan = canBusHelper.Call<int>("readCan", 1, new CanBusCallbackProxy()); //HERE - BUG
    38.                 Debug.Log(retReadCan);
    39.             }
    40.             else {
    41.                 Debug.LogError("AndroidJavaObject canBusHelper is null");
    42.             }
    43.         }
    44.     }
    45.     public class CanBusCallbackProxy : AndroidJavaProxy {
    46.         public CanBusCallbackProxy() : base("com.android.canbus.CanBusHelper$CanBusCallback") {
    47.         }
    48.         public void onSetError() {
    49.             Debug.Log("zyz0 --> onSetError");
    50.         }
    51.         public void onSendError() {
    52.             Debug.Log("zyz0 --> onSendError");
    53.         }
    54.         public void onIdError(int count) {
    55.             Debug.Log("onIdError: " + count);
    56.         }
    57.         public void onReceiveCanbusData(int FF, int RTR, int DLC, int ID, int[] DATA) {
    58.             Debug.Log("onReceiveCanbusData: FF=" + FF + ", RTR=" + RTR + ", DLC=" + DLC + ", ID=" + ID.ToString("X"));
    59.             string dataString = "DATA: ";
    60.             for (int i = 0; i < DATA.Length; i++) {
    61.                 dataString += DATA[i].ToString("X") + " ";
    62.             }
    63.             Debug.Log(dataString);
    64.         }
    65.     }
    66. }
    However, this time, when trying to run the readCan function, I get the error "Error Unity Exception: JNI: Init'd AndroidJavaObject with null ptr!"

     
  2. Tomas1856

    Tomas1856

    Unity Technologies

    Joined:
    Sep 21, 2012
    Posts:
    3,948
  3. Marcos-Schultz

    Marcos-Schultz

    Joined:
    Feb 24, 2014
    Posts:
    385

    Simply perfect!!!

    Just add "AndroidJNI.AttachCurrentThread();" at the beginning of the thread and everything works perfectly.

    Thank you very much for your help and quick response!!!


    My custom thread now:
    Code (CSharp):
    1. private void ReadCanData() {
    2.         Thread.Sleep(100);
    3.         AndroidJNI.AttachCurrentThread();
    4.         //
    5.         CanBusCallbackProxy callBack = new CanBusCallbackProxy();
    6.         if (callBack != null && canBusHelper != null) {
    7.             var retReadCan = canBusHelper.Call<int>("readCan", 1, callBack);
    8.         }
    9.     }
     
    Tomas1856 likes this.