In the previous article, I wrote about how to set up ndk R7 In ubuntu. This article implements my first JNI example. The Android SDK API version is 4.0.3 and the ndk version is R7.
1. Create an android project.
Open eclipse, execute file-> New-> Project, select Android-> Android project, and fill in the project name (my project name is myjnitest ), package name (for example, my package name
Com. example. myjnitest. The creation of a project is not carefully described here. If not, please refer to other materials.
2. Compile the Java code.
1) First, write the layout file main. XML, which defines two buttons and one textview for displaying text.
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <Button android:id="@+id/btn1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/Text_from_jni" /> <Button android:id="@+id/btn2" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="@string/Clear_text" /> <TextView android:id="@+id/textdisplay" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Nothing" /></LinearLayout>
2) Compile the strings. xml file. Here I define the Description Language displayed on the button.
<?xml version="1.0" encoding="utf-8"?><resources> <string name="hello">Hello World, MyJniTestActivity!</string> <string name="app_name">MyJniTest</string> <string name="Text_from_jni">Text1 from jni</string> <string name="Clear_text">Text2 from jni</string></resources>
3) Compile the myjnitestactivity. Java file.
Package COM. example. myjnitest; import android. app. activity; import android. OS. bundle; import android. view. view; import android. view. view. onclicklistener; import android. widget. button; import android. widget. textview; public class myjnitestactivity extends activity implements onclicklistener {/** called when the activity is first created. * // *** defines two native methods. * The Declaration of the two methods contains the native keyword, which indicates that the two methods are local. Method, * that is, the two methods are implemented through local code (C/C ++). In Java code, only the */Public native string string1fromjni () is declared (); public native string string2fromjni (); Private button m_btn1; private button m_btn2; private textview m_text; @ override public void oncreate (bundle savedinstancestate) {super. oncreate (savedinstancestate); setcontentview (R. layout. main); m_btn1 = (button) findviewbyid (R. id. btn1); m_btn2 = (button) findviewbyid (R. id. BT N2); m_text = (textview) findviewbyid (R. Id. textdisplay); // register the button listening event. M_btn1.setonclicklistener (this); m_btn2.setonclicklistener (this);} // This function is called when a click event is listened to by a button listener event. Public void onclick (view v) {Switch (v. GETID () {case R. id. btn1: {// libmyjnitest will be called through JNI. the function corresponding to the so library. M_text.settext (string1fromjni ();} break; case R. id. btn2: {m_text.settext (string2fromjni ();} break; default: break;}/*** indicates that myjnitest is loaded when the program starts running, the Code declared in the static area will be executed before the oncreate Method * If your program has multiple classes, and if the myjnitestactivity class is not the entrance of your application, * Then myjnitest (the complete name is libmyjnitest. so) This library will be loaded when myjnitestactivity is used for the first time. */Static {system. loadlibrary ("myjnitest ");}}
Compile the project with eclipse to generate the corresponding. Class file. This step must be completed before the next step, because the corresponding. Class file is used to generate. H files.
3. Write the corresponding C/C ++ code
When I first started learning, there was a problem that would be confusing. How should I write the corresponding C/C ++ code and define the function name? Here is a method to generate the corresponding. h file using the javah tool, and then according to this. h file
Write the corresponding C/C ++ code.
1) generate the corresponding. h file:
First, enter the directory of the myjnitest project just created under the terminal:
Root @ xxx:/home/xxx # cd workspace/myjnitest/
Root @ xxx:/home/XXX/workspace/myjnitest # ls
Androidmanifest. xml assets bin Gen proguard. cfg project. properties res SRC
Create a JNI folder under the project directory:
Root @ xxx:/home/XXX/workspace/myjnitest # mkdir JNI
Root @ xxx:/home/XXX/workspace/myjnitest # ls
Androidmanifest. xml assets bin Gen JNI proguard. cfg project. properties res SRC
The corresponding. h file can be generated as follows:
-Classpath bin: indicates the class path.
-D jni: directory of the generated header file
Com. example. hellojni. hellojni is the complete class name.
The success of this step is based on the myjnitestactivity. Class generated in the bin/COM/example/myjnitest/directory.
Root @ xxx:/home/XXX/workspace/myjnitest # javah-classpath bin-d jni com. example. myjnitest. myjnitestactivity
Error: unable to access COM. example. myjnitest. myjnitestactivity
The class file of COM. example. myjnitest. myjnitestactivity is not found.
Javadoc: Error-the class com. example. myjnitest. myjnitestactivity cannot be found.
Error: no class is specified in the command line. Please try-help.
Later, I checked it on the Internet, which is related to the specified directory. Later, I saw my program generate myjnitestactivity. Class in the path bin/classes/COM/example/myjnitest /,
So run the following command again:
Root @ xxx:/home/XXX/workspace/myjnitest # javah-classpath bin/classes-d jni com. example. myjnitest. myjnitestactivity
Root @ xxx:/home/XXX/workspace/myjnitest # cd JNI/
Root @ xxx:/home/XXX/workspace/myjnitest/JNI # ls
Com_example_myjnitest_myjnitestactivity.h
A. H file is generated successfully.
/* DO NOT EDIT THIS FILE - it is machine generated */#include <jni.h>/* Header for class com_example_myjnitest_MyJniTestActivity */#ifndef _Included_com_example_myjnitest_MyJniTestActivity#define _Included_com_example_myjnitest_MyJniTestActivity#ifdef __cplusplusextern "C" {#endif/* * Class: com_example_myjnitest_MyJniTestActivity * Method: String1FromJni * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_myjnitest_MyJniTestActivity_String1FromJni (JNIEnv *, jobject);/* * Class: com_example_myjnitest_MyJniTestActivity * Method: String2FromJni * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_myjnitest_MyJniTestActivity_String2FromJni (JNIEnv *, jobject);#ifdef __cplusplus}#endif#endif
From the source code above, we can see that this function name is quite long .... However, it is still quite regular. It is named in the format of java_pacakege_class_mathod. If you want to know the specific meanings of macros such as jniexport, you need to learn the JNI guide by yourself.
2) write C/C ++ files
First, in eclipse, choose new> File> myjnitest (select project)> JNI (select the new file storage path), and enter file name: myjnitest. C (My name)
Compile the myjnitest. c file:
#include<string.h>#include<jni.h>/* * Class: com_example_myjnitest_MyJniTestActivity * Method: String1FromJni * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_myjnitest_MyJniTestActivity_String1FromJni (JNIEnv * env, jobject thiz){ return (*env)->NewStringUTF(env,"Welcome to study JNI!");}/* * Class: com_example_myjnitest_MyJniTestActivity * Method: String2FromJni * Signature: ()Ljava/lang/String; */JNIEXPORT jstring JNICALL Java_com_example_myjnitest_MyJniTestActivity_String2FromJni (JNIEnv * env, jobject thiz){return (*env)->NewStringUTF(env,"Hello From JNI!");}
Java_com_example_myjnitest_myjnitestactivity_string1fromjni and java_com_example_myjnitest_myjnitestactivity_string2fromjni functions are simply returned.
The content is "Welcome to study JNI! "And" hello from JNI !" (Corresponding to the string object in Java ).
The myjnitest. c file has been compiled. Now you can delete the com_example_myjnitest_myjnitestactivity.h file. Of course you can keep it. I didn't delete it.
4. Compile the myjnitest. c file to generate the corresponding library.
1) Compile the Android. mk file of the generated library.
First, in eclipse, choose new> File> myjnitest (select project)> JNI (select the new file storage path), and enter file name: Android. mk
Compile the Android. mk file:
# Copyright (C) 2009 The Android Open Source Project## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at## http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.#LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE := MyJniTestLOCAL_SRC_FILES :=MyJniTest.cinclude $(BUILD_SHARED_LIBRARY)
The Analysis of Android. mk is reproduced at http://407827531.iteye.com/blog/1313926:
Local_path: = $ (call my-DIR)
The local_path variable must be defined in an android. mk file. It is used to search for source files in the Development tree. In this example, the macro function 'my-dir' is provided by the compilation system and used to return the current path (that is, the directory containing the Android. mk file ).
Include $ (clear_vars)
Clear_vars is provided by the compilation system, specifying that GNU makefile can clear many local_xxx variables for you (such as local_module, local_src_files, local_static_libraries, etc ...),
Except local_path. This is necessary because all the compilation control files are in the same GNU make execution environment, and all the variables are global.
Local_module: = hello-JNI
The target object to be compiled. The local_module variable must be defined to identify each module you describe in the Android. mk file. The name must be unique and contain no spaces.
Note: The compilation system automatically generates the appropriate prefix and suffix. In other words, a shared library module named 'hello-JNI 'will generate 'libello-JNI. so 'file.
Important Notes:
If you name the library 'libello-JNI ', the compiling system will not add any lib prefix or generate 'libello-JNI. so ', to support Android from the source code of the Android platform. MK file, if you do need to do so.
Local_src_files: = hello-jni.c
The local_src_files variable must contain the C or C ++ source code files to be compiled and packaged into the module. Note that you do not need to list header files and contained files here, because the compilation system will automatically find the dependent files for you; just list the source code files directly transmitted to the compiler.
Note that the default C ++ source code file extension is '. CPP '. it is also possible to specify a different extension. As long as you define the local_default_cpp_extension variable, do not forget the starting dot (that is '. cxx ', not 'cxx ')
Include $ (build_shared_library)
Build_shared_library indicates compiling and generating shared libraries. It is a variable provided by the compilation system and points to a GNU makefile script to collect information from the previous call of 'include $ (clear_vars, define all the information in the local_xxx variable and decide what to compile and how to perform it correctly. The build_static_library variable also indicates generating static libraries: Lib $ (local_module). A, build_executable indicates generating executable files.
2) generate the. So shared library file
The Andro file has been compiled. Now you can use the ndk-build script in the android ndk Development Kit to generate the corresponding. So shared library. The method is as follows:
Root @ xxx:/home/XXX/workspace/myjnitest # ndk-build
Compile thumb: myjnitest <= myjnitest. c
Sharedlibrary: libmyjnitest. So
Install: libmyjnitest. So => libs/armeabi/libmyjnitest. So
We can see that the libmyjnitest. So shared library has been correctly generated. Let's go to the libs/armeabi/directory to see it:
Root @ xxx:/home/XXX/workspace/myjnitest # cd libs/
Root @ xxx:/home/XXX/workspace/myjnitest/libs # ls
Armeabi
Root @ xxx:/home/XXX/workspace/myjnitest/libs # cd armeabi/
Root @ xxx:/home/XXX/workspace/myjnitest/libs/armeabi # ls
Libmyjnitest. So
5. Re-compile the myjnitest project in eclipse to generate a new APK.
In eclipse, refresh the myjnitest project and re-compile and generate the APK. The libmyjnitest. So shared library will be packaged together in the APK file.
Check the running result in the simulator:
After you click text1 from JNI button, the effect is as follows:
After you click text2 from JNI button, the effect is as follows:
Okay, you're done!