x

Search in
Sort by:

Question Status:

Search help

  • Simple searches use one or more words. Separate the words with spaces (cat dog) to search cat,dog or both. Separate the words with plus signs (cat +dog) to search for items that may contain cat but must contain dog.
  • You can further refine your search on the search results page, where you can search by keywords, author, topic. These can be combined with each other. Examples
    • cat dog --matches anything with cat,dog or both
    • cat +dog --searches for cat +dog where dog is a mandatory term
    • cat -dog -- searches for cat excluding any result containing dog
    • [cats] —will restrict your search to results with topic named "cats"
    • [cats] [dogs] —will restrict your search to results with both topics, "cats", and "dogs"

Custom java called from c++ causes ClassNotFoundException

I'm currently trying to extend the java part of a game i'm developing for Android. We need to access photo gallery and camera from the device. So we have added a few java classes to Build/Android/src/.../ folder to call from c++.

However, when we tried to access the classes, we are receiving a not found error on jni. We created a simple java class, and tried to call it from an emptry project with also no luck.

We need help for solving this problem... :)

I added a few lines to proguard-project.txt to keep the class if it was being removed, yet it was not. I can see that the class exists in dex file.

There is also another problem. We cannot add any files under Build/Android folder (custom proguard file, build file, extended manifest, etc), as it crashes the UnrealBuildTool with a "pak file not found error", or sometimes, "file already exists"... but that is a different problem

So... here are the codes that you can use to re-create the circumstances: proguard-project.txt (C:\Program Files\Epic Games\4.13\Engine\Build\Android\Java)

 -keep class com.markakod.hitme.Tools {
   public *;
 }

This is our simple test class:

 package com.markakod.hitme;
 
 import android.content.Context;
 import android.widget.Toast;
 
 public class Tools {
     
     public static void openPhotoGallery(Context context, String fileLocation){
         Toast.makeText(context, fileLocation, Toast.LENGTH_LONG).show();
     }
 
     public static void openCamera(Context context, String fileLocation){
         Toast.makeText(context, fileLocation, Toast.LENGTH_LONG).show();
     }
     
 }

And this is the code we are using to call the class (exception occurs in FindClass line):

 #if PLATFORM_ANDROID
 #include "Android/AndroidApplication.h"
 #endif

 void UCaller::CallPhotoGallery(FString Location)
 {
     UE_LOG(HITMELOG, Error, TEXT("[UCaller::CallPhotoGallery] For %s"), *Location);
 
     CreateFolder();
 
     FString CurrentLocation = LocalImageFolder() + "/" + Location;
     FString ClassName = "[Lcom/markakod/hitme/Tools;";
     FString MethodName = "openPhotoGallery";
     FString MethodSignature = "(Landroid/content/Context;Ljava/lang/String;)V";
 
 #if PLATFORM_ANDROID
 
     UE_LOG(HITMELOG, Error, TEXT("[UCaller::CallPhotoGallery] Get java environment..."));
     JNIEnv* Env = FAndroidApplication::GetJavaEnv(false);
     FAndroidApplication::CheckJavaException();
 
     UE_LOG(HITMELOG, Error, TEXT("[UCaller::CallPhotoGallery] Get main activity"));
     jobject Activity = FAndroidApplication::GetGameActivityThis();
     FAndroidApplication::CheckJavaException();
 
     UE_LOG(HITMELOG, Error, TEXT("[UCaller::CallPhotoGallery] Get class %s"), *ClassName);
     jclass Class = Env->FindClass(TCHAR_TO_ANSI(*ClassName));
     FAndroidApplication::CheckJavaException();
 
     if (Class != NULL) {
         UE_LOG(HITMELOG, Error, TEXT("[UCaller::CallPhotoGallery] Get static method %s"), *MethodName);
         jmethodID Method = Env->GetStaticMethodID(Class, TCHAR_TO_ANSI(*MethodName), TCHAR_TO_ANSI(*MethodSignature));
         FAndroidApplication::CheckJavaException();
 
         if (Method != NULL) {
             UE_LOG(HITMELOG, Error, TEXT("[UCaller::CallPhotoGallery] Create string from %s"), *CurrentLocation);
             jstring jLocation = Env->NewStringUTF(TCHAR_TO_ANSI(*CurrentLocation));
 
             UE_LOG(HITMELOG, Error, TEXT("[UCaller::CallPhotoGallery] Call static method %s"), *MethodName);
             Env->CallStaticVoidMethod(Class, Method, Activity, jLocation);
             FAndroidApplication::CheckJavaException();
 
             UE_LOG(HITMELOG, Error, TEXT("[UCaller::CallPhotoGallery] Done..."));
             Env->DeleteLocalRef(jLocation);
         }
     }
 #endif
 }

From the crash log...

 12-28 10:53:08.001 13360 13380 W System.err: java.lang.ClassNotFoundException: Didn't find class "com.markakod.hitme.Tools" on path: DexPathList[[directory "."],nativeLibraryDirectories=[/vendor/lib, /system/lib]]
 12-28 10:53:08.001 13360 13380 W System.err:    at dalvik.system.BaseDexClassLoader.findClass(BaseDexClassLoader.java:56)
 12-28 10:53:08.001 13360 13380 W System.err:    at java.lang.ClassLoader.loadClass(ClassLoader.java:511)
 12-28 10:53:08.001 13360 13380 W System.err:    at java.lang.ClassLoader.loadClass(ClassLoader.java:469)
 12-28 10:53:08.001 13360 13380 W System.err:    Suppressed: java.lang.ClassNotFoundException: com.markakod.hitme.Tools
 12-28 10:53:08.001 13360 13380 W System.err:            at java.lang.Class.classForName(Native Method)
 12-28 10:53:08.001 13360 13380 W System.err:            at java.lang.BootClassLoader.findClass(ClassLoader.java:781)
 12-28 10:53:08.001 13360 13380 W System.err:            at java.lang.BootClassLoader.loadClass(ClassLoader.java:841)
 12-28 10:53:08.001 13360 13380 W System.err:            at java.lang.ClassLoader.loadClass(ClassLoader.java:504)
 12-28 10:53:08.001 13360 13380 W System.err:            ... 1 more
 12-28 10:53:08.001 13360 13380 W System.err:    Caused by: java.lang.NoClassDefFoundError: Class not found using the boot class loader; no stack trace available

And at last, this is from the dexdump of classes.dex:

 dexdump.exe classes.dex | findstr Tools
   Class descriptor  : 'Lcom/markakod/hitme/Tools;'
     #0              : (in Lcom/markakod/hitme/Tools;)
         0x0000 - 0x0004 reg=0 this Lcom/markakod/hitme/Tools;
     #1              : (in Lcom/markakod/hitme/Tools;)
     #2              : (in Lcom/markakod/hitme/Tools;)
   source_file_idx   : 14003 (Tools.java)


Decompiling the apk proves again that the Tools class exists.

 .class public Lcom/markakod/hitme/Tools;
 .super Ljava/lang/Object;
 .source "Tools.java"

and the static method signature we are using is true

 .method public static openPhotoGallery(Landroid/content/Context;Ljava/lang/String;)V


Product Version: UE 4.13
Tags:
more ▼

asked Dec 28 '16 at 08:22 AM in Packaging & Deployment

avatar image

emrahgunduz
168 13 15 21

avatar image emrahgunduz Jan 04 '17 at 11:48 AM

as christmas is over, may someone from staff check this one out? :)

avatar image emrahgunduz Jan 05 '17 at 12:19 PM

i've upgraded to 4.14, yet the same error occurs.

(comments are locked)
10|2000 characters needed characters left

1 answer: sort voted first

While looking what was going on, I found out that the environment reference that was returning from vm was not the main activity's environment. It was somewhat a bogus system enviroment that can never be able to access the dex classes. I must get the environment from the active context, which it is not possible outside of JNI_Onload...

While checking the source for Android on UE4 I found out that Epic choose a way to get class loader class from the main activity while JNI_OnLoad is first executed and they keep a reference to it plus to the methodid for FindClass.

I am so blind... Epic already thought what I was trying to do and implemented a static method. This is what my code is now:

 JNIEnv* Env = FAndroidApplication::GetJavaEnv();
 jclass FoundClass = FAndroidApplication::FindJavaClass("com/markakod/hitme/Tools");
 FAndroidApplication::CheckJavaException();

Android/AndroidApplication.h has already a method for calling classes and works as expected. So simple

Yet now I have another problem (which can be solved on java class)... I always thought that blueprint was working on GUI thread. Yet calling a blueprintable method shows me that JNI is working on another thread. So I need to run my java code in an activity's runOnUiThread runnable class...

more ▼

answered Jan 10 '17 at 02:33 PM

avatar image

emrahgunduz
168 13 15 21

avatar image panha Feb 05 '17 at 01:26 AM

Hi, you seems to know a lot about android side of unreal!

Could you please help me with this? Get the Android intent extra data on Unreal app launch

https://answers.unrealengine.com/questions/558437/read-android-intent-extra-data-on-unreal-app-launc.html

(comments are locked)
10|2000 characters needed characters left
Your answer
toggle preview:

Up to 5 attachments (including images) can be used with a maximum of 5.2 MB each and 5.2 MB total.

Follow this question

Once you sign in you will be able to subscribe for any updates here

Answers to this question