Writing Native Code for Android Systems

Download Report

Transcript Writing Native Code for Android Systems

Writing Native Code for Android
Systems
Why ndk
• There exist large c++ code libraries
– E.g., Audio and video compression, e.g., Ogg
Vorbis, The LAME Project (mp3), ..
• OpenGL
• OpenSL ES
– Low level audio
• Advanced CPU features
– E.g., some ARM cpu support the NEON instruction
set for signal and video processing
• App are mixed java and c/c++
• The java app is like a regular app. The java app is
started by os.
– It is not possible to have a stand alone c++ program
(not sure, it might be)
• The c++ program is placed in a shared library
– Shared libraries have names like libMyProgram.so
• The application package (.apk) will include the
java app and the shared library
• jni (Java Native Interface) must be used to move
data between java and c++
Outline of Steps
• Write c++ code in MyProject/jni
• Describe project sources in MyProject/jni/Android.mk
– Like a make file, but much easier
• Build project by running the command ../android-ndk-r5b/ndk-build from
your MyProject directory
– ndk-build is like make
• ndk-build
– Builds
• Ndk-build clean
– Cleans everything
– Generates shared lib (libXX.so file) and places it in correct directory so the java
program can get it
• Make .apk file by building app in eclipse
– Important: whenever you make a change in the c++ program, of coruse, you
need to run ndk-build. But, you also must rerun the java compile. To do this,
make a trivial change in your java code and resave.
HelloJni
•
Make new app called
–
–
–
•
Make new subdirectory in project call jni
–
•
•
•
i.e., HelloJni/jni
In jni directory make new file called
–
•
Package: edu.udel.eleg454.HelloJni
Activity Name: HelloJni
Project: HelloJni
MyHelloJni.cpp
In this file, put
–
–
–
–
–
–
–
–
–
#include <string.h>
#include <jni.h>
extern "C" {
JNIEXPORT jstring JNICALL
Java_edu_udel_eleg454_helloJni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz )
{
return env->NewStringUTF("Hello from JNI!");
}
–
}
Save file
Important: function names must be exactly correct
–
Java_packageNameWithDotReplacedByUnderScore_JavaClassNameThatWillCallThisFunction_functionName
Android.mk
• In HelloJni/jni make new file called Android.mk
• Put the following in Android.mk
– LOCAL_PATH := $(call my-dir)
– include $(CLEAR_VARS)
– LOCAL_MODULE := HelloJni
– LOCAL_SRC_FILES := HelloJni.cpp
– include $(BUILD_SHARED_LIBRARY)
• Note that LOCAL_MODULE is the module name
• Build library
– Open terminal.
– Cd dir to <workspace>/HelloJni/jni
– Run build
• <android-ndk-r5b>/ndk-build
– Check that libHelloJni.so is created
•
In java HelloJni
After public class HelloJni extends Activity {
– public native String stringFromJNI(); // the c++ function name
– static {
–
System.loadLibrary("HelloJni"); // shared lib is called libHelloJni.so.
• // this name is from the LOCAL_MODULE part of the Android.mk file
•
– }
– Note: HelloJni is our
In onCreate, after setContentView(R.layout.main); put
– Log.e("debug","calling jni");
– Log.e("debug",stringFromJNI()); // last part of name of c++ function
– Log.e("Debug","done");
• Run and check log
• Note: public native … allows any function to be defined. But when
this function is called, the shared library must have already been
loaded (via System.loadLibrary)
play
• Change c++ function to be make string
– Hello from JNI 2
• Instead of
– Hello from JNI!
• Rebuild and run from eclipse
– Log does not show anything. Not even an error
• In eclipse make trivial change (delete and add
;)
• Run, and everything is ok
C++ Function name
• Change c++ function name. recompile and see
error in LogCat
– “no implementation found for native …”
•
•
•
•
Make a new class called TestJni
Move jni stuff into TestJni
Run and see error
Change function name from
– Java_edu_udel_eleg454_helloJni_HelloJni_stringFrom
JNI
• To
– Java_edu_udel_eleg454_helloJni_TestJni_stringFromJ
NI
• And runs ok
Logging from c++
• In cpp file, add
– #include <android/log.h>
• In Android.mk add
– LOCAL_LDLIBS := -llog
• In function add
– __android_log_print(ANDROID_LOG_INFO,
“DEBUG", “Here we are");
Passing strings from java to c++ with JNI
•
In java code, make function arg include a string
– Change
•
public native String stringFromJNI();
– To
•
public native String stringFromJNI(String name);
– And change
•
Log.e("debug",stringFromJNI());
– To
•
•
Log.e("debug",stringFromJNI("string para"));
In c++ code
– Change
•
•
•
JNIEXPORT jstring JNICALL
Java_edu_udel_eleg454_helloJni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz)
– To
•
•
•
JNIEXPORT jstring JNICALL
Java_edu_udel_eleg454_helloJni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz, jstring javaString )
– And add
•
•
•
•
•
const char *str = env->GetStringUTFChars(javaString, 0); // convert java string to c++ str
__android_log_print(ANDROID_LOG_INFO, "DEBUG", str); // do something
env->ReleaseStringUTFChars(javaString, str); // release str
Build, compile, run
Note: after release, str is no longer valid
Passing int, floats, etc to c++
•
In java
– Change
•
public native String stringFromJNI();
– To
•
public native String stringFromJNI(int val);
– And change
•
Log.e("debug",stringFromJNI());
– To
•
•
•
int i = 100;
Log.e("debug",stringFromJNI(i));
In c++
– Change
•
•
•
JNIEXPORT jstring JNICALL
Java_edu_udel_eleg454_helloJni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz)
– To
•
•
•
JNIEXPORT jstring JNICALL
Java_edu_udel_eleg454_helloJni_HelloJni_stringFromJNI( JNIEnv* env,
jobject thiz, jint ji )
– And comment out
•
•
const char *str = env->GetStringUTFChars(javaString, 0);
env->ReleaseStringUTFChars(javaString, str);
– Add
•
•
•
•
char str[80];
sprintf(str,"data is %d",ji); // be sure to add #include <stdio.h>
__android_log_print(ANDROID_LOG_INFO, "DEBUG", str);
Build, compile, run
Jni Data types
•
•
•
•
•
•
•
•
•
•
C++ type = jave type
unsigned char = jboolean
signed char = jbyte
unsigned short = jchar
Short = jshort
Long = jlong
Long long = jlong
__int64 = jlong
float = jfloat
double = jdouble
Passing arrays of ints to c++
• In java
– Define function to take int array as argument
• Replace
– public native String stringFromJNI();
• With
– public native String stringFromJNI(int[] val);
– In onCreate
• Make array
– int[] ints = new int[]{1,1,2,3,5,8,13};
• Call function with ints as augment
– Log.e("debug",stringFromJNI(ints));
Passing arrays of ints to c++
• In c++
– Define function to take array as argument
• JNIEXPORT jstring JNICALL
•
Java_edu_udel_eleg454_helloJni_HelloJni_stringFromJNI( JNIEnv* env,
•
jobject thiz, jintArray jiArray )
– Get size of array
• jsize arrayLength = env->GetArrayLength(jiArray);
• char str[80];
• __android_log_print(ANDROID_LOG_INFO, "DEBUG", str);
– Get pointer to array
• jint *data = env->GetIntArrayElements(jiArray, 0);
– Do something with data
• for (int i=0; i<arrayLength; i++) {
– sprintf(str,"val %d is %d",i,data[i]);
– __android_log_print(ANDROID_LOG_INFO, "DEBUG", str);
– data[i] = i;
• }
– Release pointer
• env->ReleaseIntArrayElements(jiArray, data, 0);
• Build, compile, run
More passing arrays to c++
• env->ReleaseIntArrayElements(jiArray, data, 0);
– Last argument is 0 => data is copied back to java and java
can delete data array
– Last argument is JNI_COMMIT => data is copied back, but
java should not delete the array
– Last argument is JNI_ABORT => data is not copied back and
java can delete
• Check if the data was changed in c++
– In java, after Log.e("debug",stringFromJNI(ints)); add
• for (int i=0; i<ints.length; i++) {
•
Log.e("DEBUG","ret val["+i+"] = "+ints[i]);
• }
– run, and see that ints is updated
Returning data
• Java
– define function to return int
• public native int stringFromJNI(int[] val);
– Call function and print return value
• Log.e("debug","results = "+stringFromJNI(ints));
• C++
– Change function prototype to return jint
• JNIEXPORT jint JNICALL
•
Java_edu_udel_eleg454_helloJni_HelloJni_stringFromJNI(
JNIEnv* env,
•
jobject thiz, jintArray jiArray )
– return int
• return 12;
• Build, compile, run
Return arrays
• Same as returning a string but use
NewIntArray