jni

来源:互联网 发布:绘画人偶软件 编辑:程序博客网 时间:2024/06/05 22:30

一、配置NDK环境


第一步:在AndroidStudio中配置ndk环境

需要下载ndk包,在AndroidStudio中File-->ProjectStructure-->SDK Location中配置"Android NDK Location",如下:



下载安装完成后build工程;

第二步:配置环境变量

在计算机属性里面配置环境变量,变量地址是Android NDK Location里面的路径:(我这里是:C:\studio\android-sdk-windows\ndk-bundle)




第三步:测试环境变量是否配置成功

在CMD里面输入ndk-build,如果未提示"ndk-build不是系统命令"就表示NDK环境配置完成了!


二、编译JNI生成So包并调用


第一步:生成调用方demo

java代码MainActivity.java:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. public class MainActivity extends Activity {  
  2.   
  3.     static {  
  4.         System.loadLibrary("JniTest");  
  5.     }  
  6.   
  7.     public native String getStringFromNative();  
  8.   
  9.     @Override  
  10.     protected void onCreate(Bundle savedInstanceState) {  
  11.         super.onCreate(savedInstanceState);  
  12.         setContentView(R.layout.activity_main);  
  13.         TextView txt = (TextView) findViewById(R.id.main_txt_msg);  
  14.         txt.setText(getStringFromNative());  
  15.     }  
  16. }  

xml代码"activity_main.xml":
[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout  
  3.     xmlns:android="http://schemas.android.com/apk/res/android"  
  4.     xmlns:tools="http://schemas.android.com/tools"  
  5.     android:layout_width="match_parent"  
  6.     android:layout_height="match_parent"  
  7.     android:paddingBottom="@dimen/activity_vertical_margin"  
  8.     android:paddingLeft="@dimen/activity_horizontal_margin"  
  9.     android:paddingRight="@dimen/activity_horizontal_margin"  
  10.     android:paddingTop="@dimen/activity_vertical_margin"  
  11.     tools:context="com.example.jni.jnitest.MainActivity">  
  12.   
  13.     <TextView  
  14.         android:id="@+id/main_txt_tips"  
  15.         android:layout_width="wrap_content"  
  16.         android:layout_height="wrap_content"  
  17.         android:text="下面是来自JNI里面C代码返回的信息"/>  
  18.   
  19.     <TextView  
  20.         android:id="@+id/main_txt_msg"  
  21.         android:layout_width="wrap_content"  
  22.         android:layout_height="wrap_content"  
  23.         android:layout_below="@id/main_txt_tips"  
  24.         android:textColor="#f00"/>  
  25.   
  26. </RelativeLayout>  


第二步:配置build.gradle

在app文件夹下的build.gradle文件里面配置Ndk相关信息
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. ndk {  
  2.     moduleName "JniTest"  
  3.     ldLibs "log""z""m"  
  4.     abiFilters "armeabi""armeabi-v7a""x86"  
  5. }  
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. sourceSets{  
  2.     main{  
  3.         jniLibs.srcDirs = ['libs']  
  4.     }  
  5. }  



第三步:生成与调用方有关的.h文件(C头文件)

在Android Studio下方的Terminal命令行里面执行生成命令,如果没有Terminal,到"View->Tool Windows->Terminal"里面打开该面板
操作命令:
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. javah -d jni -classpath <SDK_android.jar>;<dir path> 加载So所在的class包名+class名  
后面的"<dir path> 加载So所在的class包名+class名"是相对于你当前所在目录的路径;

参考命令:
[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. C:\git_source\demo\JniTest>javah -d jni -classpath C:\studio\android-sdk-windows\platforms\android-23\android.jar;app\src\main\java com.example.jni.jnitest.MainActivity  
这里的后面一部分是相对于"C:\git_source\demo\JniTest"目录的路径

命令执行成功会自动生成一个jni文件夹和对应的"包名.h"文件,这里的文件名是:com_example_jni_jnitest_MainActivity.h



第四步:编写C代码

C代码"main.c":
[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. /* DO NOT EDIT THIS FILE - it is machine generated */    
  2. #include <jni.h>    
  3. #include <android/log.h>    
  4.     
  5. #ifndef LOG_TAG    
  6. #define LOG_TAG "ANDROID_LAB"    
  7. #define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)    
  8. #endif    
  9.   
  10. /* Header for class com_example_jni_jnitest_MainActivity */  
  11.     
  12. #ifndef _Included_com_example_jni_jnitest_MainActivity  
  13. #define _Included_com_example_jni_jnitest_MainActivity  
  14. #ifdef __cplusplus    
  15. extern "C" {    
  16. #endif    
  17. /*  
  18.  * Class: com_example_jni_jnitest_MainActivity 
  19.  * Method: getStringFromNative  
  20.  * Signature: ()Ljava/lang/String;  
  21.  */    
  22. JNIEXPORT jstring JNICALL Java_com_example_jni_jnitest_MainActivity_getStringFromNative(JNIEnv * env, jobject jObj){  
  23.       return (*env)->NewStringUTF(env,"Hello From JNI!");    
  24.   }  
  25.     
  26. #ifdef __cplusplus    
  27. }    
  28. #endif    
  29. #endif    

注意,你需要将com_example_jni_jnitest_MainActivity_getStringFromNative替换成你工程里面对应的方法路径


第五步:编写Android.mk文件和Application.mk文件


如果jni中没有Android.mk与Application.mk文件,在jni中新建这两个文件
前面第一步的图里面可以看到jni包里面多了一个Android.mk文件和Application.mk文件,还有一个main.c文件是上一步我们生成的
Android.mk文件和Application.mk文件是用来控制编译So文件的, Android.mk文件控制So文件如何编译, Application.mk文件控制支持的架构平台.

Android.mk文件:
[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. LOCAL_PATH := $(call my-dir)  
  2.   
  3. include $(CLEAR_VARS)  
  4. #bzlib模块  
  5. bzlib_files := \  
  6.     main.c  
  7.   
  8. LOCAL_MODULE :libbz  
  9. LOCAL_SRC_FILES := $(bzlib_files)  
  10. include $(BUILD_STATIC_LIBRARY)  
  11.   
  12. #bspath模块  
  13. include $(CLEAR_VARS)  
  14. LOCAL_MODULE    :main  
  15. LOCAL_SRC_FILES :main.c  
  16. LOCAL_STATIC_LIBRARIES :libbz #引入libbz库  
  17.   
  18. include $(BUILD_STATIC_LIBRARY)  
  19.   
  20. include $(CLEAR_VARS)  
  21.   
  22. LOCAL_MODULE    :JniTest  
  23. LOCAL_SRC_FILES :main.c  
  24. LOCAL_STATIC_LIBRARIES :main  
  25. LOCAL_LDLIBS := -llog#加入log  
  26.   
  27. include $(BUILD_SHARED_LIBRARY)  
"bzlib_files :"里面放的是所有的c源文件;"LOCAL_MODULE := JniTest"表示生成的So库名,因为我加载So库的名字是"JniTest"所以这里需要同名;
[java] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. static {  
  2.         System.loadLibrary("JniTest");  
  3.     }  

Application.mk文件:
[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. APP_CFLAGS += -Wno-error=format-security  
  2. APP_ABI :armeabi  
APP_ABI := armeabi 表示只支持armeabi架构的Jni,如需添加其他架构可在后面追加

第六步:编译So文件

在CMD命令行里面cd到项目的目录下, 输入"ndk-build"生成So包


命令执行成功后在工程的libs文件夹下面生成一个So文件,如下:
如果不添加Application.mk文件,会生成所有架构的So文件,如下:


编译出来的So默认是放在工程里面的libs文件夹下的,需要将它移到app文件夹下的libs里面!

第七步:打包运行

运行代码,查看结果:


这里打印出了C代码返回的值,Jni的调用就成功了!


三、遇到的问题与解决

问题一:
Android NDK: Could not find application project directory
如图:  在gradle.properties 文件里面添加 android.useDeprecatedNdk=true 后重新编译即可



问题二:找不到mk文件


原因是JniTest项目里面的jni目录下没有Android.mk文件


问题三:couldn't find "libJniTest.so"说找不到so文件(这里是libJniTest.so)

[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. FATAL EXCEPTION: main  
  2. Process: com.example.jni.jnitest, PID: 30152  
  3. java.lang.UnsatisfiedLinkError: com.android.tools.fd.runtime.IncrementalClassLoader$DelegateClassLoader[DexPathList[[dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-support-annotations-23.4.0_d560f708638dfceb3917510f1cc1dc667f754e94-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-slice_9-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-slice_8-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-slice_7-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-slice_6-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-slice_5-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-slice_4-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-slice_3-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-slice_2-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-slice_1-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-slice_0-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-internal_impl-23.4.0_00c09662b55d223b900e647a021f5f9dd9b4b6e9-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-com.android.support-support-vector-drawable-23.4.0_8b8204428df1954e75ed14d23129e230d51e4401-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-com.android.support-support-v4-23.4.0_ab900e8ce412421b38e4e809122c4e820ea3b15b-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-com.android.support-appcompat-v7-23.4.0_60d063a4fe66aa6d98c8e4fbd0183a230d80c604-classes.dex", dex file "/data/data/com.example.jni.jnitest/files/instant-run/dex/slice-com.android.support-animated-vector-drawable-23.4.0_7565542ff4cab32ab703cc056ccf47fe61af9054-classes.dex"],nativeLibraryDirectories=[/vendor/lib64, /system/lib64, /vendor/lib64, /system/lib64]]] couldn't find "libJniTest.so"  
  4.     at java.lang.Runtime.loadLibrary(Runtime.java:378)  
  5.     at java.lang.System.loadLibrary(System.java:998)  
  6.     at com.example.jni.jnitest.MainActivity.<clinit>(MainActivity.java:11)  
  7.     at java.lang.reflect.Constructor.newInstance(Native Method)  
  8.     at java.lang.Class.newInstance(Class.java:1572)  
  9.     at android.app.Instrumentation.newActivity(Instrumentation.java:1068)  
  10.     at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2303)  
  11.     at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2466)  
  12.     at android.app.ActivityThread.access$1200(ActivityThread.java:152)  
  13.     at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1341)  
  14.     at android.os.Handler.dispatchMessage(Handler.java:102)  
  15.     at android.os.Looper.loop(Looper.java:135)  
  16.     at android.app.ActivityThread.main(ActivityThread.java:5538)  
  17.     at java.lang.reflect.Method.invoke(Native Method)  
  18.     at java.lang.reflect.Method.invoke(Method.java:372)  
  19.     at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:958)  
第一步:需要在app的build.gradle里面的Android{}里面添加如下代码,指定jni放置目录:
[html] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. sourceSets{  
  2.     main{  
  3.         jniLibs.srcDirs = ['libs']  
  4.     }  
  5. }  

第二步:然后将so文件放在app文件夹下的libs里面


第三步:编译运行

1 0