Try to create a java class from c++ side but keep crashing
appBucksAdv.java
package adv;
import org.qtproject.qt5.android.bindings.QtApplication;
import org.qtproject.qt5.android.bindings.QtActivity;
import android.os.Bundle;
public class appBucksAdv extends QtActivity
{
}
simpleJNI.hpp
#include <QObject>
#include <QtAndroidExtras/QAndroidJniObject>
class simpleJNI : public QObject
{
Q_OBJECT
public:
explicit simpleJNI(QObject *parent = nullptr);
private:
QAndroidJniObject advClass_;
};
simpleJNI.cpp
#include "simpleJNI.hpp"
simpleJNI::simpleJNI(QObject *parent) :
QObject(parent),
advClass_("adv/appBucksAdv") //crash when I create the java class
{
}
error messages :
#
W/dalvikvm(21397): dvmFindClassByName rejecting 'org/qtproject/qt5/android/QtMessageDialogHelper'
#
D/dalvikvm(21397): Trying to load lib /data/data/org.qtproject.example.androidTest/lib/libQt5QuickParticles.so 0x4176efd0
#
D/dalvikvm(21397): Shared lib '/data/data/org.qtproject.example.androidTest/lib/libQt5QuickParticles.so' already loaded in same CL 0x4176efd0
#
D/dalvikvm(21397): Trying to load lib /data/data/org.qtproject.example.androidTest/lib/libandroidTest.so 0x4176efd0
#
D/dalvikvm(21397): Added shared lib /data/data/org.qtproject.example.androidTest/lib/libandroidTest.so 0x4176efd0
#
D/dalvikvm(21397): No JNI_OnLoad found in /data/data/org.qtproject.example.androidTest/lib/libandroidTest.so 0x4176efd0, skipping init
#
W/Qt (21397): kernel/qcoreapplication.cpp:416 (QCoreApplicationPrivate::QCoreApplicationPrivate(int&, char**, uint)): WARNING: QApplication was not created in the main() thread.
#
W/dalvikvm(21397): dvmFindClassByName rejecting 'org/qtproject/qt5/android/QtNativeInputConnection'
#
W/dalvikvm(21397): dvmFindClassByName rejecting 'org/qtproject/qt5/android/QtExtractedText'
1.full messages
2.codes
Are you loading the correct Activity? you need to edit the manifest to make it load your custom activity. You should also avoid calling the constructor of the QtActivity class yourself (advClass_("adv/appBucksAdv")), as it will attempt to prepare the application again. What you probably want is a handle to the activity and since you are already extending the QtActivity class, just add a static function that returns a handle to it.
E.g.:
Java:
public class appBucksAdv extends QtActivity
{
...
static appBucksAdv getActivity() { return thisActivity; }
...
}
C++
QAndroidJniObject activity = QAndroidJniObject::callStaticObjectMethod("adv/appBucksAdv",
"getActivity",
"()Ladv/appBucksAdv;");
Related
I have a custom ClassLoader extending GroovyClassLoader which compiles the source code to .class files on disk and then loads the resulting class:
class MyClassLoader extends GroovyClassLoader {
File cache = new File( './cache' )
Compiler compiler
MyClassLoader() {
CompilerConfiguration cc = new CompilerConfiguration( targetDirectory:cache )
compiler = new Compiler( cc )
addClasspath cache.path
}
#Override
Class findClass( name ) {
try{
parent.findClass name
}catch( ClassNotFoundException e ){
compiler.compile name, getBodySomehow()
byte[] blob = loadFromFileSystem name
Class c = defineClass name, blob, 0, blob.length
setClassCacheEntry c
c
}
}
#Override
void removeClassCacheEntry(String name) {
Class c = cache[ name ]
super.removeClassCacheEntry(name)
GroovySystem.metaClassRegistry.removeMetaClass c
deleteFiles name
}
}
Class clazz = myClassLoader.loadClass 'some.pckg.SomeClass'
Now if I change the source code, call myClassLoader.removeClassCacheEntry(name) and try myClassLoader.loadClass() again I'm getting:
java.lang.LinkageError: loader (instance of com/my/MyClassLoader): attempted duplicate class definition for name some/pckg/SomeClass
I read the greater half of the Internet and found a "solution" to initialize a class-loader for each class:
MyClassLoader myClassLoader = new MyClassLoader()
Class clazz = myClassLoader.loadClass 'some.pckg.SomeClass'
This seems to be working but raises performance concerns of mine...
What is the proper way to reload classes? How can I reuse the same class-loader? What am I missing?
Actually there is a trick that could be used
Originally, when you call
classLoader.defineClass(className, classBytes, 0, classBytes.length)
It calls java native method defineClass1 that actually calls loadClass method.
So, possible to intercept this method and process it a bit different then original.
In the folder that contains cached class files I have the following groovy compiled to class: A.class
println "Hello World!"
B.class to check dependent class loading
class B extends A {
def run(){
super.run()
println "Hello from ${this.getClass()}!"
}
}
and C.class to check multi-level class loading
i used this jar to compile following class and run the class re-loading example
class C extends org.apache.commons.lang3.RandomUtils {
def rnd(){ nextInt() }
}
the following class + code loads and reloads the same class:
import java.security.PrivilegedAction;
import java.security.AccessController;
import org.codehaus.groovy.control.CompilationFailedException;
#groovy.transform.CompileStatic
class CacheClassLoader extends GroovyClassLoader{
private File cacheDir = new File('/11/tmp/a__cache')
private CacheClassLoader(){throw new RuntimeException("default constructor not allowed")}
public CacheClassLoader(ClassLoader parent){
super(parent)
}
public CacheClassLoader(Script parent){
this(parent.getClass().getClassLoader())
}
#Override
protected Class getClassCacheEntry(String name) {
Class clazz = super.getClassCacheEntry(name)
if( clazz ){
println "getClassCacheEntry $name -> got from memory cache"
return clazz
}
def cacheFile = new File(cacheDir, name.tr('.','/')+'.class')
if( cacheFile.exists() ){
println "getClassCacheEntry $name -> cache file exists, try to load it"
//clazz = getPrivelegedLoader().defineClass(name, cacheFile.bytes)
clazz = getPrivelegedLoader().defineClass(name, cacheFile.bytes)
super.setClassCacheEntry(clazz)
}
return clazz
}
private PrivelegedLoader getPrivelegedLoader(){
PrivelegedLoader loader = AccessController.doPrivileged(new PrivilegedAction<PrivelegedLoader>() {
public PrivelegedLoader run() {
return new PrivelegedLoader();
}
});
}
public class PrivelegedLoader extends CacheClassLoader {
private final CacheClassLoader delegate
public PrivelegedLoader(){
super(CacheClassLoader.this)
this.delegate = CacheClassLoader.this
}
public Class loadClass(String name, boolean lookupScriptFiles, boolean preferClassOverScript, boolean resolve) throws ClassNotFoundException, CompilationFailedException {
Class c = findLoadedClass(name);
if (c != null) return c;
return delegate.loadClass(name, lookupScriptFiles, preferClassOverScript, resolve);
}
}
}
def c=null
//just to show intermediate class loaders could load some classes that will be used in CacheClassLoader
def cl_0 = new GroovyClassLoader(this.getClass().getClassLoader())
cl_0.addClasspath('/11/tmp/a__cache/commons-lang3-3.5.jar')
//create cache class loader
def cl = new CacheClassLoader(cl_0)
println "---1---"
c = cl.loadClass('A')
c.newInstance().run()
println "---2---"
c = cl.loadClass('A')
c.newInstance().run()
println "---3---"
cl.removeClassCacheEntry('A')
c = cl.loadClass('A')
c.newInstance().run()
println "---4---"
c = cl.loadClass('B')
c.newInstance().run()
println "---5---"
cl.removeClassCacheEntry('A')
cl.removeClassCacheEntry('B')
c = cl.loadClass('B')
c.newInstance().run()
println "---6---"
c = cl.loadClass('C')
println c.newInstance().rnd()
result:
---1---
getClassCacheEntry A -> cache file exists, try to load it
Hello World!
---2---
getClassCacheEntry A -> got from memory cache
Hello World!
---3---
getClassCacheEntry A -> cache file exists, try to load it
Hello World!
---4---
getClassCacheEntry B -> cache file exists, try to load it
getClassCacheEntry A -> got from memory cache
Hello World!
Hello from class B!
---5---
getClassCacheEntry B -> cache file exists, try to load it
getClassCacheEntry A -> cache file exists, try to load it
Hello World!
Hello from class B!
---6---
getClassCacheEntry C -> cache file exists, try to load it
226399895
PS: not sure priviledged access required
JVM does not allow to just unload some class, the only way to unload a class is to GC it. And class can be GC just like every other object -> all reachable references must be removed and GC run.
The tricky part is... class loader hold references to all classes. So the only way to unload a class is to get rid of both class and class loader.
You can find more information in language specification: https://docs.oracle.com/javase/specs/jvms/se13/jvms13.pdf 12.7 Unloading of Classes and Interfaces
An implementation of the Java programming language may unload classes.
A class or interface may be unloaded if and only if its defining class
loader may be reclaimed by the garbage collector as discussed in
§12.6. Classes and interfaces loaded by the bootstrap loader may not
be unloaded.
And class unloading does not need to be implemented at all in some JVM implementations:
Class unloading is an optimization that helps reduce memory use.
[...] system chooses to implement an optimization such as class
unloading. [...] Consequently, whether a class or interface has been unloaded
or not should be transparent to a program.
There is also explanation why class loader can't be reachable to unload class, as class might contain static variables and blocks of code that would be reset and executed again if this same class would be later loaded again. It's quite long and already a bit off topic so I will not paste it here.
So each your script should just use own class loader as that's the only way to actually not waste memory, so class can be GC later. Just make sure that you don't use any libraries that might cache references to your class - like many serialization/ORM libraries might do this for data types, or some other reflection libraries.
Another solution would be to use different scripting language that does not create java classes and just execute some kind of AST structure.
There is also one more solution to this problem, but it is very tricky and it's not something you should use on production, it even requires you to provide special JVM arguments or JVM from JDK that contains all needed modules. As java supports instrumentation API that can allow you to change bytecode of class at runtime, but if class is already loaded you can only change bytecode of methods, you can't add/remove/edit method/field/class signatures. So it could be very bad idea to use it for such scripts, so I will stop here.
I have a .cpp file to use with java on android:
#include<iostream>
#include<jni.h>
jint Java_com_example_gatsj_tutorjatek_MainActivity_Sum(JNIEnv* env, jobject obj)
{
return 5;
}
I use it here:
package com.example.gatsj.tutorjatek;
import android.app.Activity;
import android.os.Bundle;
public class MainActivity extends Activity
{
public native int Sum();
static
{
System.loadLibrary("TestCPP");
}
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
int x = Sum();//IF I REMOVE THIS LINE THE APP DOESN'T CRASH
}
}
I build it in Android Studio using Gradle and this CMakeLists.txt:
cmake_minimum_required(VERSION 3.4.1)
add_library( # Specifies the name of the library.
TestCPP
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/TestCPP.cpp )
When I start the app on my phone it crashes. But if I remove the "int x = Sum();" line, the app can start.
The "loadLibrary" and "native" method part is still in the code but without the "int x = Sum();" line, the app doesn't crash.
How can I use the Sum() method? What causes the problem?
Since C++ is being used instead of C, you should wrap the defination of your native methods inside extern "C" in your cpp file.
extern "C" {
// your native method definations.
}
I am using dll in java using JNA, but i am getting below error
Exception in thread "main" java.lang.UnsatisfiedLinkError: Error looking up function 'GetStatus': The specified procedure could not be found.
Not getting how to resolve this issue?
Please help.
Here is java code
import com.sun.jna.Library;
import com.sun.jna.Native;
/** Simple example of native library declaration and usage. */
public class First {
public interface TEST extends Library {
public String GetStatus();
}
public static void main(String[] args) {
TEST obj = (TEST ) Native.loadLibrary("TEST ", TEST .class);
System.out.println( obj.GetStatus());
}
}
This Nugget is super easy to use and works perfectly. https://www.nuget.org/packages/UnmanagedExports
You need Visual Studio 2012 (express).
Once installed, just add [RGiesecke.DllExport.DllExport] before any static function you want to export. That's it!
Example:
C#
[RGiesecke.DllExport.DllExport]
public static int YourFunction(string data)
{
/*Your code here*/
return 1;
}
Java
Add the import at the top:
import com.sun.jna.Native;
Add the interface in your class. Its your C# function name preceded by the letter "I":
public interface IYourFunction extends com.sun.jna.Library
{
public int YourFunction(String tStr);
};
Call your DLL where you need it in your class:
IYourFunction iYourFunction = (IYourFunction )Native.loadLibrary("full or relative path to DLL withouth the .dll extention", IYourFunction.class);//call JNA
System.out.println("Returned: " + IYourFunction.YourFunction("some parameter"));
EDIT:
If the DLL is 32bit, the JDK/JRE has to be 32bit as well. Add the following check to your code:
if(!System.getProperty("os.arch").equals("x86")) {
throw new Exception(".NET DLL " + "32bits JRE/JDK is required. Currently using " + System.getProperty("os.arch") + ".\r\nTry changing your PATH environement variable.");
}
I'm trying to use a generated interface by JNAerator from a typedef instruction, but I can't find a way to do that:
The function in the .h file is:
MyClass::Initialize(LPCWSTR path);
The header file also includes the original typedef instruction:
typedef __nullterminated CONST WCHAR *LPCWSTR, *PCWSTR;
JNAerator generates:
Method:
public native int Initialize(LPCWSTR path);
The interface:
/// Undefined type
/// Undefined type
public static interface LPCWSTR {
};
And the classes:
/// C type : WCHAR*
public Pointer<Short > LPCWSTR() {
try {
return (Pointer<Short >)BridJ.getNativeLibrary("dlls").getSymbolPointer("LPCWSTR").as(DefaultParameterizedType.paramType(Pointer.class, Short.class)).get();
}catch (Throwable $ex$) {
throw new RuntimeException($ex$);
}
}
/// C type : WCHAR*
public MainLibrary LPCWSTR(Pointer<Short > LPCWSTR) {
try {
{
BridJ.getNativeLibrary("dlls").getSymbolPointer("LPCWSTR").as(DefaultParameterizedType.paramType(Pointer.class, Short.class)).set(LPCWSTR);
return this;
}
}catch (Throwable $ex$) {
throw new RuntimeException($ex$);
}
}
The problem is, I don't know how to instantiate an object LPCWSTR using Pointer and the LPCWSTR interface, using a String, so that I can pass it to the Initialize method.
How can I do this?
UPDATE:
I modified the .h file to use wchar_t*:
MyClass::Initialize(wchar_t* path)
JNAerator generated the method like this:
public native int Initialize(Pointer<Character > path);
So I called it like this:
MyClass factory = new MyClass();
Pointer<Character> path = org.bridj.Pointer.pointerToWideCString("dlls");
factory.Initialize(path);
The problem is that I get the following exception:
java.lang.UnsatisfiedLinkError: main.MyClass.Initialize(Lorg/bridj/Pointer;)I
at this line:
factory.Initialize(path);
What am I doing wrong?
The UnsatisfiedLinkError indicates that your native Initialize method was not bound by BridJ.
This binding is done by CPPObject's constructor (I assume you got a JNAerated MyClass class that inherits from it), and any failure to bind should result in error logs in your console (in any case, you can increase the logs verbosity with BRIDJ_VERBOSE=1 environment variable).
Also, please note that your MyClass should have a #Library("mylibrary") annotation to let it know where to find the appropriate library (.dll, .so or .dylib), or should be an inner class of a class with such an annotation. This is normally the case of JNAerated classes, but if it's not, please report a bug in NativeLibs4Java's tracker.
I was having this problem too. zOlive’s response sent me down the right path. I changed the JNAerator library option to match my DLL name (e.g. -library MyLib MyLib.dll MyLib.h). That fixed it for me. There is more info about the library option here.
For some reason I can only call native functions from my main activity and not any custom views that I've created. Here is an example file (I followed a tutorial, but renamed the classes http://mindtherobot.com/blog/452/android-beginners-ndk-setup-step-by-step/)
See the usage of the native function "getNewString".
package com.example.native;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.Bundle;
import android.view.View;
public class NativeTestActivity extends Activity
{
static
{
System.loadLibrary("nativeTest");
}
private native String getNewString();
#Override public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
this.setContentView(new BitmapView(this));
String hello = getNewString(); // This line works fine
new AlertDialog.Builder(this).setMessage(hello).show();
}
}
class BitmapView extends View
{
static
{
System.loadLibrary("nativeTest");
}
private native String getNewString();
public BitmapView(Context context)
{
super(context);
String hello = getNewString(); // This line throws the UnsatisfiedLinkError
new AlertDialog.Builder(this.getContext()).setMessage(hello).show();
}
}
How can I call native functions in my custom views?
I've built the application as an Android 2.2 app. I'm running the application on my HTC Desire. I have the latest SDK (9) and latest NDK (r5).
Your problem is that you are trying to call the native function from a class where it dont belongs to.
You defined the following JNI function in your c file:
jstring Java_com_example_native_NativeTestActivity_getNewString()
This states that the native function when loaded will bind with the method declared as native in NativeTestActivity class. So when you try to call it from your View class it doesn't find any function to bind to.
In that case it will look for the following function (which of course does not exist in your .so):
jstring Java_com_example_native_BitmapView_getNewString()
If you still want to be able to call the same function from different classes you can declare it in a container class that can be accessed from any class you want.
eg:
java code:
package com.example.native;
public class NativeHelper {
public native String getNewString();
static
{
System.loadLibrary("nativeTest");
}
}
c code:
jstring Java_com_example_native_NativeHelper_getNewString(JNIEnv* env, jobject javaThis)
{
return (*env)->NewStringUTF(env, "Hello from native code!");
}