Calling Java method from within Qt yields exception
-
@Bremenpl said in Calling Java method from within Qt yields exception:
but could you elaborate why does the example code work?
Take a look at this (from the example):
A NotificationClient object is exposed to the QML in the main source file, main.cpp: QQuickView view; NotificationClient *notificationClient = new NotificationClient(&view); view.engine()->rootContext()->setContextProperty(QLatin1String("notificationClient"), notificationClient);
As you can see in the example an instance of NotificationClient is created, so m_instance is set. I guess you don't have this part of the code in your project?
-
@Bremenpl said in Calling Java method from within Qt yields exception:
but could you elaborate why does the example code work?
Take a look at this (from the example):
A NotificationClient object is exposed to the QML in the main source file, main.cpp: QQuickView view; NotificationClient *notificationClient = new NotificationClient(&view); view.engine()->rootContext()->setContextProperty(QLatin1String("notificationClient"), notificationClient);
As you can see in the example an instance of NotificationClient is created, so m_instance is set. I guess you don't have this part of the code in your project?
-
@Bremenpl said in Calling Java method from within Qt yields exception:
but could you elaborate why does the example code work?
Take a look at this (from the example):
A NotificationClient object is exposed to the QML in the main source file, main.cpp: QQuickView view; NotificationClient *notificationClient = new NotificationClient(&view); view.engine()->rootContext()->setContextProperty(QLatin1String("notificationClient"), notificationClient);
As you can see in the example an instance of NotificationClient is created, so m_instance is set. I guess you don't have this part of the code in your project?
@jsulm But here is the thing: In some weird way that is not fully understandable to me, this java class is connected with QML, that in my opinion should have nothing to do with anything related to this topic... It seems like somehow the cpp class name
NotificationClient
being the same as the java class name is not an accident? Is there a way to disengage all those hard coded components from this example? -
@jsulm But here is the thing: In some weird way that is not fully understandable to me, this java class is connected with QML, that in my opinion should have nothing to do with anything related to this topic... It seems like somehow the cpp class name
NotificationClient
being the same as the java class name is not an accident? Is there a way to disengage all those hard coded components from this example?@Bremenpl Yes, you're right the code I pasted is C++. I'm not an JNI expert, so I don't know where the instance is created. All I know is what the error message says: there is no instance.
"F m.company.mr: java_vm_ext.cc:542] JNI DETECTED ERROR IN APPLICATION: JNI GetStaticMethodID called with pending exception java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object com.company.mri.NotificationClient.getSystemService(java.lang.String)' on a null object reference"Maybe somebody else has an idea what is missing in your project.
Does the example application work?
-
@Bremenpl Yes, you're right the code I pasted is C++. I'm not an JNI expert, so I don't know where the instance is created. All I know is what the error message says: there is no instance.
"F m.company.mr: java_vm_ext.cc:542] JNI DETECTED ERROR IN APPLICATION: JNI GetStaticMethodID called with pending exception java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object com.company.mri.NotificationClient.getSystemService(java.lang.String)' on a null object reference"Maybe somebody else has an idea what is missing in your project.
Does the example application work?
@jsulm Yes, the example works perfectly well. But my problem is that I am unable to figure out what is necessary in that example and what is not, since it is not described. For instance, I dont need to export any methods to the QML side. I also have no clue either the cpp
NotificationClient
class has the same name as java class for a reason, or it is a coincidence. At last, after you have suggested me what is wrong, I dont know how to create the java class instance via cpp without doing any unnecessarily operations on QML (or furthermore, creating an additional cpp class just for this end). -
@jsulm Yes, the example works perfectly well. But my problem is that I am unable to figure out what is necessary in that example and what is not, since it is not described. For instance, I dont need to export any methods to the QML side. I also have no clue either the cpp
NotificationClient
class has the same name as java class for a reason, or it is a coincidence. At last, after you have suggested me what is wrong, I dont know how to create the java class instance via cpp without doing any unnecessarily operations on QML (or furthermore, creating an additional cpp class just for this end). -
So I am just bumping the topic since it feels like I am close (thanks @jsulm). I would really appreciate anyone explaining me the magic happening in that android notofication example (about the namimg and everything)...
@Bremenpl I was facing the same problem as you. Maybe my solution is still valuable for you or anyone else reading this thread.
First of all, also for me the notification example was working without any problems. The above mentioned code snipped
NotificationClient *notificationClient = new NotificationClient(&view); view.engine()->rootContext()->setContextProperty(QLatin1String("notificationClient"), notificationClient);
relates to the C++ class NotificationClient used in the example. For me it is really a disadvantage of the notification example to name the C++ class the same way as the Java class. This way it is more confusing to distinguish between the Java class and the C++ class.
However, like @Bremenpl I also wanted to apply the same principle of calling Java code to my own project. I also was running into the error:
called with pending exception java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object com.company.mri.NotificationClient.getSystemService(java.lang.String)' on a null object reference
I checked the notification example and added some debug output to the Java class:
public NotificationClient() { Log.i("NotificationClient", "Set instance"); m_instance = this; }
The debug output was shown in the console the QtCreator. I did the same for my own project but there was no debug output. So the static instance of my Java class never has been set. I found a hint to the solution here: http://schorsch.efi.fh-nuernberg.de/roettger/index.php/QtOnAndroid/AppObj
There it is said that you need to provide an AndroidManifest.xml for the Java class. I did so. My file looks something like this:<?xml version="1.0"?> <manifest package="org.qtproject.example.myprojectname" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto"> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/> <application android:icon="@drawable/icon" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="Project_Label"> <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.example.myprojectname.MyJavaClassName" android:label="JNI Caller" android:screenOrientation="unspecified"> </activity> </application> <!-- <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> --> <!-- %%INSERT_PERMISSIONS --> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- %%INSERT_FEATURES --> </manifest>
You can basically copy the AndroidManifest.xml from the Notification example and adapt the package and activity name to fit your project's names.
After adding this file to my own project (OTHER_FILES section in the pro file) and recompiling the project I saw my debug output in the console and was able to run my Java code without running into the NullPointerException! :-)
-
@Bremenpl I was facing the same problem as you. Maybe my solution is still valuable for you or anyone else reading this thread.
First of all, also for me the notification example was working without any problems. The above mentioned code snipped
NotificationClient *notificationClient = new NotificationClient(&view); view.engine()->rootContext()->setContextProperty(QLatin1String("notificationClient"), notificationClient);
relates to the C++ class NotificationClient used in the example. For me it is really a disadvantage of the notification example to name the C++ class the same way as the Java class. This way it is more confusing to distinguish between the Java class and the C++ class.
However, like @Bremenpl I also wanted to apply the same principle of calling Java code to my own project. I also was running into the error:
called with pending exception java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.Object com.company.mri.NotificationClient.getSystemService(java.lang.String)' on a null object reference
I checked the notification example and added some debug output to the Java class:
public NotificationClient() { Log.i("NotificationClient", "Set instance"); m_instance = this; }
The debug output was shown in the console the QtCreator. I did the same for my own project but there was no debug output. So the static instance of my Java class never has been set. I found a hint to the solution here: http://schorsch.efi.fh-nuernberg.de/roettger/index.php/QtOnAndroid/AppObj
There it is said that you need to provide an AndroidManifest.xml for the Java class. I did so. My file looks something like this:<?xml version="1.0"?> <manifest package="org.qtproject.example.myprojectname" xmlns:android="http://schemas.android.com/apk/res/android" android:versionName="1.0" android:versionCode="1" android:installLocation="auto"> <uses-sdk android:minSdkVersion="21" android:targetSdkVersion="28"/> <application android:icon="@drawable/icon" android:name="org.qtproject.qt5.android.bindings.QtApplication" android:label="Project_Label"> <activity android:configChanges="orientation|uiMode|screenLayout|screenSize|smallestScreenSize|locale|fontScale|keyboard|keyboardHidden|navigation" android:name="org.qtproject.example.myprojectname.MyJavaClassName" android:label="JNI Caller" android:screenOrientation="unspecified"> </activity> </application> <!-- <supports-screens android:largeScreens="true" android:normalScreens="true" android:anyDensity="true" android:smallScreens="true"/> --> <!-- %%INSERT_PERMISSIONS --> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <!-- %%INSERT_FEATURES --> </manifest>
You can basically copy the AndroidManifest.xml from the Notification example and adapt the package and activity name to fit your project's names.
After adding this file to my own project (OTHER_FILES section in the pro file) and recompiling the project I saw my debug output in the console and was able to run my Java code without running into the NullPointerException! :-)
@LtKoerschgen You are right about all symptoms. I also agree with this:
For me it is really a disadvantage of the notification example to name the C++ class the same way as the Java class. This way it is more confusing to distinguish between the Java class and the C++ class.
Very poor design, especially misleading for someone who doesn't know this API (like me). I was trying to figure out for a while either the naming has anything to do with this.
I will also leave my extended solution in case anyone needs it:
package com.company.appname; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.os.Build; public class NotificationClient { private static final String m_channelId = "channel"; public static void notify(Context context, String title, String body) { Notification.Builder m_builder; if (Build.VERSION.SDK_INT >= 26) { createChannel(context); m_builder = new Notification.Builder(context, m_channelId); } else { m_builder = new Notification.Builder(context); } NotificationManager m_notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); m_builder.setContentTitle(title); m_builder.setContentText(body); m_builder.setShowWhen(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { m_builder.setSmallIcon(R.drawable.icon_trans); } else { m_builder.setSmallIcon(R.drawable.icon); } m_notificationManager.notify(1, m_builder.build()); } private static void createChannel(Context context) { NotificationChannel channel = new NotificationChannel( m_channelId, "General", NotificationManager.IMPORTANCE_HIGH); channel.setDescription("The general channel"); channel.enableVibration(true); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.createNotificationChannel(channel); } }
-
I was having the same problem, and this thread helped me to solve it.
The only thing you need to do is to change the
android:name
attribute in the<activity>
tag inside the AndroidManifest.xml file. This name needs to be your package name followed by the class name, so for the OP it needs to beandroid:name="com.company.mri.NotificationClient"
-
@LtKoerschgen You are right about all symptoms. I also agree with this:
For me it is really a disadvantage of the notification example to name the C++ class the same way as the Java class. This way it is more confusing to distinguish between the Java class and the C++ class.
Very poor design, especially misleading for someone who doesn't know this API (like me). I was trying to figure out for a while either the naming has anything to do with this.
I will also leave my extended solution in case anyone needs it:
package com.company.appname; import android.app.Notification; import android.app.NotificationChannel; import android.app.NotificationManager; import android.content.Context; import android.os.Build; public class NotificationClient { private static final String m_channelId = "channel"; public static void notify(Context context, String title, String body) { Notification.Builder m_builder; if (Build.VERSION.SDK_INT >= 26) { createChannel(context); m_builder = new Notification.Builder(context, m_channelId); } else { m_builder = new Notification.Builder(context); } NotificationManager m_notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); m_builder.setContentTitle(title); m_builder.setContentText(body); m_builder.setShowWhen(true); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { m_builder.setSmallIcon(R.drawable.icon_trans); } else { m_builder.setSmallIcon(R.drawable.icon); } m_notificationManager.notify(1, m_builder.build()); } private static void createChannel(Context context) { NotificationChannel channel = new NotificationChannel( m_channelId, "General", NotificationManager.IMPORTANCE_HIGH); channel.setDescription("The general channel"); channel.enableVibration(true); NotificationManager notificationManager = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE); notificationManager.createNotificationChannel(channel); } }
@Bremenpl said in Calling Java method from within Qt yields exception:
I will also leave my extended solution in case anyone needs it
Thanks for sharing. In addition, would you mind marking your post as solved?
-
I was having the same problem, and this thread helped me to solve it.
The only thing you need to do is to change the
android:name
attribute in the<activity>
tag inside the AndroidManifest.xml file. This name needs to be your package name followed by the class name, so for the OP it needs to beandroid:name="com.company.mri.NotificationClient"
@Wim-van-der-Meer thanks for info. For what end is this change needed?