【问题标题】:Android: calling Java class from C++ Native ActivityAndroid:从 C++ Native Activity 调用 Java 类
【发布时间】:2012-04-28 09:31:36
【问题描述】:

Java 代码:

    package local.ttt;

    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Typeface;

    public class Text2Bitmap {
        static Bitmap getBitmap(String text,int fontsize) {
            Paint paint=new Paint();
            paint.setStyle(Paint.Style.FILL);
            paint.setColor(Color.WHITE);
            paint.setTextSize(fontsize);
            paint.setAntiAlias(true);
            paint.setTypeface(Typeface.DEFAULT);

            int outwidth=(int)Math.round(paint.measureText(text));
            Bitmap bitmap=Bitmap.createBitmap(outwidth,fontsize,Bitmap.Config.ALPHA_8);

            Canvas canvas=new Canvas(bitmap);
            canvas.drawText(text,0,fontsize-1,paint);

            return bitmap;
        }
    }

C 源代码:


            ...
            MY_ASSERT(vm_cached!=NULL);
            JNIEnv* env;
            jint res=vm_cached->AttachCurrentThread(&env,NULL);
            MY_ASSERT(res==0);
            jclass activityClass=env->FindClass("android/app/NativeActivity");
            MY_ASSERT(activityClass!=NULL);
            jmethodID getClassLoader=env->GetMethodID(activityClass,"getClassLoader","()Ljava/lang/ClassLoader;");
            MY_ASSERT(getClassLoader!=NULL);
            MY_ASSERT(nativeActivityObjHnd!=NULL);
            jobject cls=env->CallObjectMethod(nativeActivityObjHnd,getClassLoader);
            jclass classLoader=env->FindClass("java/lang/ClassLoader");
            MY_ASSERT(classLoader!=NULL);
            jmethodID findClass=env->GetMethodID(classLoader,"loadClass","(Ljava/lang/String;)Ljava/lang/Class;");
            MY_ASSERT(findClass!=NULL);
            jstring strClassName=env->NewStringUTF("Llocal/ttt/Text2Bitmap;");
            jclass text2bitmapClass=(jclass)env->CallObjectMethod(cls,findClass,strClassName); //fails here
            MY_ASSERT(text2bitmapClass!=NULL);
            res=vm_cached->DetachCurrentThread();
            MY_ASSERT(res==0);
            ...

它失败了:

W/dalvikvm(5614): dvmFindClassByName 拒绝 'Llocal/ttt/Text2Bitmap;' W/dalvikvm(5614): threadid=9: 线程以未捕获的异常退出 (group=0x4002d560) E/AndroidRuntime(5614):致命异常:线程 10 E/AndroidRuntime(5614): java.lang.ClassNotFoundException: Llocal/ttt/Text2Bitmap;在加载器 dalvik.system.PathClassLoader[.] E/AndroidRuntime(5614):在 dalvik.system.PathClassLoader.findClass(PathClassLoader.java:240) E/AndroidRuntime(5614): 在 java.lang.ClassLoader.loadClass(ClassLoader.java:551) E/AndroidRuntime(5614):在 java.lang.ClassLoader.loadClass(ClassLoader.java:511) E/AndroidRuntime(5614): 在 dalvik.system.NativeStart.run(Native Method)

请提示我做错了什么?

【问题讨论】:

  • 你试过调试吗?代码转储在这里并不是真正的洁净
  • 这个问题是一场噩梦,但我还是尝试了一下。您需要确定是哪一行本机代码导致了错误(因为我们没有行号)。
  • 我不能在我的环境中使用调试。我在无法root的设备上进行测试。所以调试在这里不起作用。问题是关于 c 部分,而不是 java。
  • 它在 c 代码中导致“此处失败”错误。
  • 看看我的回答,试试我的一些建议。

标签: java c android-ndk native-activity


【解决方案1】:

不确定是什么导致了您的错误(您没有指出行在哪里,或者您在调试时遇到困难的特定行),但我会继续指出一些我看到的快速错误。

第一个错误是您需要使用CallStaticObjectMethod,因为您调用的是静态方法。你的jmethodid findClass 有一个methodID,而不是一个类,这是误导和错误的。您也有两个参数,但您似乎只传递了一个参数。

基本上,你需要:

(env)->GetStaticMethodID(jclass,"method name", "Parameter list")

--jclass 是类引用 --"method name" 是方法名 IN 引号 --“参数列表”是参数列表,需要查找语法。

跳过一些东西...调用你需要的东西:

(env)->CallStaticObjectMethod(jclass,jmid,parameter1,parameter2)

--jclass 再次是具有静态方法的类。 --jmid 是您从上面的函数中获得的 java 方法 ID。 --parameter1和parameter2是java方法需要的参数。

最后,我认为这可能只是一种偏好,但我不会将线程附加到 JVM,直到您获得所有必要的信息以跳转到 JVM。

【讨论】:

  • 感谢您的回答!尝试使用来自 blog.tewdew.com 的代码“使用本地活动中的 jni”
  • 目标只是将简单的 java 类添加到本机活动 eclipse 项目中,用于从文本字符串制作位图。我想使用 c++ 中的 java 类。
  • 让我减轻你的痛苦,从头开始。阅读 JNI,我花了很长时间才让 JNI 在我的实现中工作,并且对 Java 进行 C 调用并不是最容易理解的部分之一:journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/…
【解决方案2】:

不要乱用 java 类加载器。使用此代码:

MY_ASSERT(vm_cached!=NULL);

JNIEnv* env;
jint res = vm_cached->AttachCurrentThread(&env,NULL);
MY_ASSERT(res==0);

jclass t2bClass = env->FindClass("local/ttt/Text2Bitmap");
MY_ASSERT(t2bClass!=NULL);

jmethodID getBitmap = env->GetStaticMethodID(t2bClass, "getBitmap","(Ljava/lang/String;I)Landroid/graphics/Bitmap;");
MY_ASSERT(getBitmap!=NULL);

jstring text = ...;
int fontsize = ...;
jobject bitmap = env->CallStaticObjectMethod(t2bClass, getBitmap, text, fontsize);
MY_ASSERT(bitmap!=NULL);

// process bitmap here
// ...

res=vm_cached->DetachCurrentThread();
MY_ASSERT(res==0);

【讨论】:

  • 谢谢!我“搞乱类加载器”的原因是,根据我在上面的博客和其他一些来源中读到的,当你使用本机活动时,你必须使用类加载器或者“从 JNIEnv 中查找类不会让人满意。它不会找到你的类。原因是因为 JNIEnv FindClass 的上下文不知道你的类存在。”。她是这么说的:)。我试过你的代码,但它在 MY_ASSERT(t2bClass!=NULL); 上失败了;
  • 也许我应该在 androidmanifest.xml 中添加一些东西?唔。现在它设置为原生活动并具有 android:hasCode="false"。
  • 好吧,如果您没有代码,那么 JNI 肯定不会找到您的类,因为它们不存在 :) 将 hasCode 更改为 true。 JNI 应该可以在 NativeActivity 中正常工作,它可以找到并使用您的类 - 真的没有必要弄乱类加载器。这是另一个如何使用 NativeActivity 中的 Java 代码的示例 - 注释 #1 是我的,它在作者代码中运行良好:github.com/fabiensanglard/Shmup/tree/master/engine/android
  • 只是 findclass 没有类加载器应该可以工作,但即使使用 hascode=true 也对我不起作用。
  • 也根据github.com/fabiensanglard/Shmup/blob/master/engine/android/jni/…:“* JNI ISSUE:* 尝试从我的本机代码调用 Java 方法时出现新错误:NDK JNI 错误:非 VM 线程进行 JNI 调用 * 似乎当我们在游戏线程中收到 JNIEnv 时,它并不是真的有效。我们需要将线程附加到 VM (AttachCurrentThread) 并创建一个新的 JNIEnv * android.wooyd.org/JNIExample/files/JNIExample.pdf * * SNAAAAP !! 即使在附加并获得有效的 JNIEnv 之后,它似乎我们仍然没有正确的类加载器!!!!!!”
猜你喜欢
  • 1970-01-01
  • 2018-11-07
  • 1970-01-01
  • 2018-05-02
  • 1970-01-01
  • 2011-11-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多