I am writing a plugin using android NDK.right now i have done the following:
1- wrote simple functions in C++ and made a .so(shared library) of it. code is as follows:
#include <jni.h>
#include <string.h>
#include <android/log.h>
#define DEBUG_TAG "NDK_AndroidNDK1SampleActivity"
extern "C" {
static JavaVM *gJavaVM;
static JNIEnv *env;
static jobject gInterfaceObject, gDataObject;
JNIEXPORT jstring JNICALL Java_com_gvs_tes_NdkTestActivity_getNumbr(JNIEnv * env,jobject obj);
JNIEXPORT void JNICALL Java_com_gvs_tes_NdkTestActivity_getActivity(JNIEnv * env, jobject obj);};
JNIEXPORT jstring JNICALL Java_com_gvs_tes_NdkTestActivity_getNumbr(JNIEnv * env,jobject obj)
{
jboolean isCopy;
jstring szLogThis =(jstring) "hellog";
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", szLogThis);
return szLogThis;
}
JNIEXPORT void JNICALL Java_com_gvs_tes_NdkTestActivity_getActivity(JNIEnv * env, jobject obj)
{
jstring logs =(jstring) "Function Started";
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", logs);
if(env)
{
jstring logs =(jstring) "env not null";
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", logs);
}
jclass interfaceClass = env->FindClass("com/gvs/tes/testClass");
if(!interfaceClass)
{
jstring logThis =(jstring) "Activity Class Could not be get";
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", logThis);
return;
}
jmethodID altmethod = env->GetMethodID(interfaceClass, "<init>","()V");
jobject objClas = env->NewGlobalRef(env->NewObject(interfaceClass, altmethod));
jmethodID method = env->GetMethodID(interfaceClass, "chekClas","(I)V");
if(!method)
{
jstring logThis =(jstring) "Function Could not be get";
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", logThis);
return;
}
jint val = 3;
env->CallVoidMethod(objClas, method, val);
logs =(jstring) "Function Ended";
__android_log_print(ANDROID_LOG_DEBUG, DEBUG_TAG, "NDK:LC: [%s]", logs);
}
2- I copied .so file to [myUnityProject]/Assets/Plugins/Android folder.
3- then i wrote following Script to call function
using UnityEngine;
using System.Collections;
using System.Runtime.InteropServices;
public class myScript : MonoBehaviour {
//[DllImport ("ndktest")]
//private static extern string Java_com_gvs_tes_NdkTestActivity_getNumbr();
[DllImport ("ndktest")]
private static extern void Java_com_gvs_tes_NdkTestActivity_getActivity();
// Use this for initialization
void Start () {
print("hello start");
//print ("CHECKING return value : "+Java_com_gvs_tes_NdkTestActivity_getNumbr().ToString());
Java_com_gvs_tes_NdkTestActivity_getActivity(); // PROBLEM OCCURS HERE
print("after Function call inside script");
}
// Update is called once per frame
void Update () {
}
}
Now My Main problem is 'How to access Java files present in my Android Project'.Kindly point me into right direction if i am doing it stupidly wrong As my Only purpose is to be able to use an SDK(built for android in java) inside my Unity Application.Any Help is greatly appreciated.
Related
App is crashing in JNI_OnLoad(). I have checked the status it is zero. Still, the app is crashing. I want to create env variable to cache method IDs to use them for callback for java functions. I have tried caching jvm in context struct and doing same in callback() method but App is crashing. I am new bie to this concept. Can anybody explain what I am missing here.
Please find Attached code:
#include <jni.h>
#include <string>
#include "Numbers.h"
#include <android/log.h>
#define LOG_TAG "logs"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
typedef struct number_ctx{
JavaVM *javaVM;
jclass mathsClass;
jobject mathsObj;
jclass mainActivityClass;
jobject mainActivityObj;
pthread_mutex_t lock;
} NumbersCtx;
NumbersCtx g_ctx;
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved) {
JNIEnv* env;
memset(&g_ctx, 0, sizeof(g_ctx));
g_ctx.javaVM = vm;
int status = vm ->GetEnv((void **)env, JNI_VERSION_1_6);
LOGD("Status is %d ", status);
if (vm ->GetEnv((void **)env, JNI_VERSION_1_6) != JNI_OK) {
LOGD("Some error");
return JNI_ERR; // JNI version not supported.
}
if(env->ExceptionCheck()) {
env->ExceptionDescribe();
env->ExceptionClear();
}
/*
jclass clz = env->FindClass(
"com/example/maths/Maths1");
g_ctx.mathsClass = static_cast<jclass>(env->NewGlobalRef(clz));
jmethodID jniHelperCtor = env->GetMethodID(g_ctx.mathsClass,
"printMessage", "()V");
jobject handler = env->NewObject(g_ctx.mathsClass,
jniHelperCtor);
g_ctx.mathsObj = env->NewGlobalRef(handler);
g_ctx.mainActivityObj = NULL;*/
return JNI_VERSION_1_6;
}
void callback() {
}
You forgot to use the Address-Of operator (&) when calling GetEnv. GetEnv expects a pointer-to-a-pointer, but you're just passing it a pointer that you've cast to a pointer-to-a-pointer, which is not the same thing.
So instead of
vm ->GetEnv((void **)env, JNI_VERSION_1_6)
you should be using
vm ->GetEnv((void **)&env, JNI_VERSION_1_6)
The end result of leaving out the Address-Of operator in this case is that GetEnv will store the JNIEnv* who-knows-where, and your env variable most likely won't contain a valid JNIEnv*.
I have compiled ffmpeg code and generated .so files,
Then I put these .so files in jniLibs/armeabi/ folder.
To use it below code :
Controller.java
public class Controller {
static {
System.loadLibrary("avutil");
System.loadLibrary("swresample");
System.loadLibrary("avcodec");
System.loadLibrary("avformat");
System.loadLibrary("swscale");
System.loadLibrary("avfilter");
System.loadLibrary("avdevice");
}
public static native void runffmpegCommand(String[] argv);
public static void testFFMPEG(String[] strings) {
runffmpegCommand(new String[]);
}
}
ffmpeg_controller.c
#include <android/log.h>
#include "ffmpeg_Controller.h"
#include <stdlib.h>
#include <stdbool.h>
int main(int argc, char **argv);
JavaVM *sVm = NULL;
jint JNI_OnLoad( JavaVM* vm, void* reserved )
{
sVm = vm;
return JNI_VERSION_1_6;
}
JNIEXPORT void JNICALL Java_com_test_Controller_runffmpegCommand(JNIEnv *env, jobject obj, jobjectArray args)
{
int i = 0;
int argc = 0;
char **argv = NULL;
jstring *strr = NULL;
if (args != NULL) {
argc = (*env)->GetArrayLength(env, args);
argv = (char **) malloc(sizeof(char *) * argc);
strr = (jstring *) malloc(sizeof(jstring) * argc);
for(i=0;i<argc;i++) { strr[i] = (jstring)(*env)->GetObjectArrayElement(env, args, i);
argv[i] = (char *)(*env)->GetStringUTFChars(env, strr[i], 0);
}
}
main(argc, argv);
for(i=0;i<argc;i++) {
(*env)->ReleaseStringUTFChars(env, strr[i], argv[i]);
}
free(argv);
free(strr);
}
ffmpeg_controller.h
#include <jni.h>
#ifndef _Included_com_test_Controller
#define _Included_com_test_Controller
#ifdef __cplusplus
extern "C" {
#endif
JNIEXPORT void JNICALL Java_com_android_com_test_Controller_runffmpegCommand(JNIEnv *, jobject, jobjectArray);
#ifdef __cplusplus
}
#endif
#endif
When I run this code it through error as below:
Logs:
2018-12-17 14:11:17.850 25598-25598/com.android.test E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.android.test, PID: 25598
java.lang.UnsatisfiedLinkError: dlopen failed: "/data/app/com.android.test-1/lib/arm/libavutil.so" is 64-bit instead of 32-bit
at java.lang.Runtime.loadLibrary0(Runtime.java:989)
at java.lang.System.loadLibrary(System.java:1530)
I think the problem her because you run application with targetSdkVersion 26 or more so the solution i think it's her look:
https://stackoverflow.com/a/52951886/7055487
The Exception is self-explain: "libavutil.so is 64-bit instead of 32-bit": you have copied a 64 bit library in a "jniLibs" subfolder that expects 32 bit libraries.
"jniLibs/armeabi" subfolder is for 32bit ONLY devices
I have a problem for parsing float from char in JNI-Android Application.This is my sample data :
0.00618567 0.00224441 0.002006603 0.001813437 0.003761207 -0.001850192 -0.001011893 -0.00342476 0.003790586 0.002385935 0.002647488 0.004411637 0.005938921 0.00698391 0.004522655 0.001524881 -0.002673242 -0.0002569943 -0.002495839 0.00230171 0.000844161 0.006387557 0.008135659 0.005583601 0.002238941 -0.001932641 -0.003518643 -0.0006784072 0.001636732 0.001213515 0.0021472 0.004911256 0.003613603 0.001362842 -0.0002172031 -0.002115535 -0.0002000824 0.001085831 0.003149634 0.003899722 0.004865647 0.002436467 0.0001896242 -0.001678405 -0.001909177 -0.002954236 0.001802054 0.003751467 0.004150682 0.005844797 0.002612064 0.003680898 -0.0005450704 -0.002621638 -0.002253087 0.0005009398 0.004602027 0.003445318 0.00632045 0.002706638 -0.001308871 -0.002082631 -0.001821213 -0.0005696003 0.002069579 0.006264412 0.004593662 0.005836432 0.0009420562 -0.003753015 -0.004050847 -0.001744672 -0.002664186 0.00101941 0.004568859 0.003175343 0.005315124
And this is a type of data vector<float> for hog.setSVMdetector(const vector<float>& detector):
0.05359386f, -0.14721455f, -0.05532170f, 0.05077307f, 0.11547081f, -0.04268804f, 0.04635834f, -0.05468199f, 0.08232084f, 0.10424068f, -0.02294518f, 0.01108519f, 0.01378693f, 0.11193510f, 0.01268418f, 0.08528346f, -0.06309239f, 0.13054633f, 0.08100729f, -0.05209739f, -0.04315529f, 0.09341384f, 0.11035026f, -0.07596218f
this is my sample code .cpp in JNI :
extern "C"
jboolean
Java_com_example_franksyesipangkar_tourguide_CameraPreview_ImageProcessing
(JNIEnv* env, jobject thiz, jint width, jint height, jbyteArray NV21FrameData, jintArray outPixels, jbyteArray b)
{
LOGD("JNIEnv");
//convert jbyteArray to char
jbyte *cmd = env->GetByteArrayElements(b, 0);
LOGD("JNIEnvFeature");
//char * feature = (char *)cmd;
char feature[90600];
memset(feature,0, sizeof(feature));
memcpy(feature, cmd, strlen((char*)cmd));
In this code, a data in char feature which I want to parsing it to data like a type of data hog.setSVMdetector(const vector<float>& detector).So, do you have an idea ?
Maybe this can help you:
#include <iostream>
#include <vector>
#include <strstream>
#include <string>
std::vector<float> parse_delimeted_list_of_numbers(char* line, char delimeter)
{
std::vector<float> vector_of_numbers;
std::istrstream input_stream(line);
std::string text;
float number;
while (std::getline(input_stream, text, delimeter)) {
sscanf_s(text.c_str(), "%f", &number, text.size());
vector_of_numbers.push_back(number);
}
return vector_of_numbers;
}
// A program to test that the above function works well
int _tmain(int argc, _TCHAR* argv[])
{
// auto vector_of_numbers = parse_delimeted_list_of_numbers("234.0, 345, 465, 46456.0",',');
auto vector_of_numbers = parse_delimeted_list_of_numbers("234.0 34 465 46456.0", ' ');
for (auto number : vector_of_numbers)
std::cout << number << "\n";
return 0;
}
I am calling a native function that do a very simple summation operation, but it returns a wrong result why ?!
Here is my java code:
package com.example.sharedlibexample;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView tv = (TextView) findViewById(R.id.text);
tv.setText("Value = " + String.valueOf(addInJNI(15, 8)));
}
public native int addInJNI(int a, int b);
static {
System.loadLibrary("hello-jni");
}
}
and this is the native code:
int Java_com_example_sharedlibexample_MainActivity_addInJNI(int a, int b) {
return a + b;
}
The result of this sum example is:
-921636135
The two first parameters in the native function should be JNIEnv* env, jobject thiz (or JNIEnv* env, jclass clazz for a static method). What your code current actually does, is that it adds the values of the env and thiz pointers, instead of the real parameters you intended to pass.
You can use javah to generate the method signatures (in the form of a header file), which also includes other attributes which may be necessary in some cases (like JNIEXPORT, JNICALL).
With help from this blog
I could find the solution by using the jint rather than int
The solution is:
Create a header file bativeLib.h
#include <jni.h>
/* Header for class com_marakana_NativeLib */
#ifndef _Included_org_example_ndk_nativeLib
#define _Included_org_example_ndk_nativeLib
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_marakana_NativeLib
* Method: add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_sharedlibexample_MainActivity_addInJNI
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
and the C lib looks like:
#include "nativeLib.h"
JNIEXPORT jint JNICALL Java_com_example_sharedlibexample_MainActivity_addInJNI
(JNIEnv * env, jobject obj, jint value1, jint value2) {
return (value1 + value2);
}
I did an Opencv's application en windows and now I am using JNI to convert this code to Android but I am having some problems.
In concrete my native code not do nothing.
This is my Java class where I define my native methods:
package com.example.telo3;
import org.opencv.core.Mat;
public class Process {
static {
System.loadLibrary("nativo");
}
public Process(){
dir=inicializar_nativo();
}
public void Procesar(Mat framedetect, Mat framedraw){
procesar_nativo(dir,framedetect.getNativeObjAddr(),framedraw.getNativeObjAddr());
}
private long dir;
private static native long inicializar_nativo();
private static native void procesar_nativo(long thiz, long framedetect, long framedraw);
}
This is my JNI code:
#include "nativo.h"
#include <opencv2/objdetect/objdetect.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include "opencv2/video/tracking.hpp"
#include <iostream>
#include <stdio.h>
#include "FaceDetector.h"
#include "Draw.h"
#include "Almacena.h"
#include "Runnable.h"
using namespace std;
using namespace cv;
#include <android/log.h>
#define LOG_TAG "NATIVO"
#define LOGD(...) ((void)__android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__))
struct variables {
Almacena almacena;
Draw draw;
FaceDetector face_detector;
};
JNIEXPORT jlong JNICALL Java_com_example_telo3_Process_inicializar_1nativo(
JNIEnv *, jobject) {
long dir = (long) new variables();
return (dir);
}
JNIEXPORT void JNICALL Java_com_example_telo3_Process_procesar_1nativo(JNIEnv *,
jobject, jlong dir, jlong framedetect, jlong framedraw) {
Mat* telo =(Mat*)framedetect;
Mat* telo2= (Mat*)framedraw;
((variables*)dir)->almacena = ((variables*)dir)->face_detector.Detect(*telo);
//almacena = face_detector.Detect(frame_gray);
((variables*)dir)->draw.Dibujar(*telo2,((variables*)dir)->almacena);
//frame_capturado = draw.Dibujar(frame_capturado, almacena);
if( (((variables*)dir)->almacena.get_faces()).size() ==0){
LOGD("no detecto caras");
}
}
I think that I use the Jni correctly but the function Detect not works correctly because when I uses this if return 0.
Looks like a simple typo for me.
Your package is "com.example.telo3"
Your class is "Process"
Your function is "inicializar_nativo()"
So the c++ function has to be declared as
JNIEXPORT jlong JNICALL Java_com_example_telo3_Process_inicializar_nativo(...)
You have declared it as
JNIEXPORT jlong JNICALL Java_com_example_telo3_Process_inicializar_1nativo(...)
Did you get an unsatisfied link exception or such?