android NDK JNI so文件的製作和使用

參考:

java jni 入門1 - 一個簡單的從Java程序中調用C函數 : http://blog.csdn.net/u012005313/article/details/49644283



#########################################################


之前也接觸過NDK和JNI,但是並沒有很好的結合NDK和JNI來總結關於so文件的製作和使用。現在把最近一段時間的接觸的關於so的製作和使用總結一下。


還是從最簡單的例子開始:android apk調用一個C函數,輸出一個log記錄,並返回一個字符串“Hello JNI”


##############################################################3


新建一個工程SoDemo



後面一直選擇Next/Finish即可完成新建工程



修改activity_main.xml代碼如下:

[html] view plain copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  3.     xmlns:tools="http://schemas.android.com/tools"  
  4.     android:layout_width="match_parent"  
  5.     android:layout_height="match_parent"  
  6.     android:paddingBottom="@dimen/activity_vertical_margin"  
  7.     android:paddingLeft="@dimen/activity_horizontal_margin"  
  8.     android:paddingRight="@dimen/activity_horizontal_margin"  
  9.     android:paddingTop="@dimen/activity_vertical_margin"  
  10.     tools:context="com.zj.sodemo.MainActivity">  
  11.   
  12.     <TextView  
  13.         android:id="@+id/text"  
  14.         android:layout_width="match_parent"  
  15.         android:layout_height="wrap_content"  
  16.         android:textAlignment="center"  
  17.         android:text="Hello World!"  
  18.         android:layout_alignParentTop="true"  
  19.         />  
  20.   
  21.     <Button  
  22.         android:id="@+id/button"  
  23.         android:layout_width="match_parent"  
  24.         android:layout_height="wrap_content"  
  25.         android:text="click me"  
  26.         android:layout_alignParentBottom="true"  
  27.         />  
  28. </RelativeLayout>  

功能介紹:加入一個文本框和按鈕。apk運行後,點擊按鈕調用C程序,文本框顯示返回的字符串


修改MainActivity.java代碼如下:

[java] view plain copy
  1. package com.zj.sodemo;  
  2.   
  3. import android.app.Activity;  
  4. import android.support.v7.app.AppCompatActivity;  
  5. import android.os.Bundle;  
  6. import android.view.View;  
  7. import android.widget.Button;  
  8. import android.widget.TextView;  
  9.   
  10. public class MainActivity extends Activity {  
  11.   
  12.     private Button mButton;  
  13.     private TextView mTextView;  
  14.       
  15.     private String text = null;  
  16.   
  17.     static {  
  18.         System.loadLibrary("JNITest");  
  19.     }  
  20.       
  21.     public native String helloJNI();  
  22.   
  23.     @Override  
  24.     protected void onCreate(Bundle savedInstanceState) {  
  25.         super.onCreate(savedInstanceState);  
  26.         setContentView(R.layout.activity_main);  
  27.   
  28.         mButton = (Button)findViewById(R.id.button);  
  29.         mTextView = (TextView)findViewById(R.id.text);  
  30.   
  31.         mButton.setOnClickListener(new View.OnClickListener() {  
  32.             @Override  
  33.             public void onClick(View view) {  
  34.                 text = helloJNI();  
  35.                 mTextView.setText(text);  
  36.             }  
  37.         });  
  38.     }  
  39. }  
功能介紹:新建文本框和按鈕,同時調用so庫JNITest,調用so庫裏面的函數是helloJNI,同時爲按鈕建立一個點擊監聽器,點擊按鈕,則調用helloJNI()函數,並顯示返回的字符串。


至此,前期準備工作都已完成

##########################################################3


接下來需要使用JDK工具javah來建立java和C/C++之間的聯繫

參考文檔: Android Studio & NDK: I got an error when “javah -d jni -classpath…” - http://stackoverflow.com/questions/27252712/android-studio-ndk-i-got-an-error-when-javah-d-jni-classpath


在  android ndk 入門 - 一個簡單的ndk工程 : http://blog.csdn.net/u012005313/article/details/49911693 中,已經介紹瞭如何在android studio中配置javah

下面,我介紹一種使用命令行的方式

首先你需要先生成MainActivity.java的class文件,點擊菜單欄build->Make Project(快捷鍵Ctrl + F9)即可編譯整個工程。編譯完成後,在路徑app/build/intermediates/classes/debug/com/zj/sodemo下應該會出現MainActivity.class文件


android studio配置了控制檯窗口,在窗口底部工具欄中應該有Terminal選項


如果不存在,則點擊頂部菜單欄View->Tool Windows->Terminal(快捷鍵Ctrl + F12)就會彈出控制檯窗口


進入main目錄並新建文件夾jni

[plain] view plain copy
  1. cd app/src/main/  
  2. mkdir jni  


運行以下命令:

[plain] view plain copy
  1. javah -d jni -classpath /opt/android-sdk-linux/platforms/android-24/android.jar:../../build/intermediates/classes/debug/ com.zj.sodemo.MainActivity  
命令介紹:-d參數-輸出目錄

-classpath-從中加載類的路徑。沒有配置android.jar環境變量的話就需要在這裏加入(注意,android.jar後面跟的是一個冒號:,而不是分號;

com.zj.sodemo.MainActivity是生成JNI樣式的表頭文件

在這裏我們將生成的.h文件保存在jni文件夾內:


打開文件com_zj_sodemo_MainActivity.h:

  1. /* DO NOT EDIT THIS FILE - it is machine generated */  
  2. #include <jni.h>  
  3. /* Header for class com_zj_sodemo_MainActivity */  
  4.   
  5. #ifndef _Included_com_zj_sodemo_MainActivity  
  6. #define _Included_com_zj_sodemo_MainActivity  
  7. #ifdef __cplusplus  
  8. extern "C" {  
  9. #endif  
  10. #undef com_zj_sodemo_MainActivity_BIND_ABOVE_CLIENT  
  11. #define com_zj_sodemo_MainActivity_BIND_ABOVE_CLIENT 8L  
  12. #undef com_zj_sodemo_MainActivity_BIND_ADJUST_WITH_ACTIVITY  
  13. #define com_zj_sodemo_MainActivity_BIND_ADJUST_WITH_ACTIVITY 128L  
  14. #undef com_zj_sodemo_MainActivity_BIND_ALLOW_OOM_MANAGEMENT  
  15. #define com_zj_sodemo_MainActivity_BIND_ALLOW_OOM_MANAGEMENT 16L  
  16. #undef com_zj_sodemo_MainActivity_BIND_AUTO_CREATE  
  17. #define com_zj_sodemo_MainActivity_BIND_AUTO_CREATE 1L  
  18. #undef com_zj_sodemo_MainActivity_BIND_DEBUG_UNBIND  
  19. #define com_zj_sodemo_MainActivity_BIND_DEBUG_UNBIND 2L  
  20. #undef com_zj_sodemo_MainActivity_BIND_EXTERNAL_SERVICE  
  21. #define com_zj_sodemo_MainActivity_BIND_EXTERNAL_SERVICE -2147483648L  
  22. #undef com_zj_sodemo_MainActivity_BIND_IMPORTANT  
  23. #define com_zj_sodemo_MainActivity_BIND_IMPORTANT 64L  
  24. #undef com_zj_sodemo_MainActivity_BIND_NOT_FOREGROUND  
  25. #define com_zj_sodemo_MainActivity_BIND_NOT_FOREGROUND 4L  
  26. #undef com_zj_sodemo_MainActivity_BIND_WAIVE_PRIORITY  
  27. #define com_zj_sodemo_MainActivity_BIND_WAIVE_PRIORITY 32L  
  28. #undef com_zj_sodemo_MainActivity_CONTEXT_IGNORE_SECURITY  
  29. #define com_zj_sodemo_MainActivity_CONTEXT_IGNORE_SECURITY 2L  
  30. #undef com_zj_sodemo_MainActivity_CONTEXT_INCLUDE_CODE  
  31. #define com_zj_sodemo_MainActivity_CONTEXT_INCLUDE_CODE 1L  
  32. #undef com_zj_sodemo_MainActivity_CONTEXT_RESTRICTED  
  33. #define com_zj_sodemo_MainActivity_CONTEXT_RESTRICTED 4L  
  34. #undef com_zj_sodemo_MainActivity_MODE_APPEND  
  35. #define com_zj_sodemo_MainActivity_MODE_APPEND 32768L  
  36. #undef com_zj_sodemo_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING  
  37. #define com_zj_sodemo_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING 8L  
  38. #undef com_zj_sodemo_MainActivity_MODE_MULTI_PROCESS  
  39. #define com_zj_sodemo_MainActivity_MODE_MULTI_PROCESS 4L  
  40. #undef com_zj_sodemo_MainActivity_MODE_NO_LOCALIZED_COLLATORS  
  41. #define com_zj_sodemo_MainActivity_MODE_NO_LOCALIZED_COLLATORS 16L  
  42. #undef com_zj_sodemo_MainActivity_MODE_PRIVATE  
  43. #define com_zj_sodemo_MainActivity_MODE_PRIVATE 0L  
  44. #undef com_zj_sodemo_MainActivity_MODE_WORLD_READABLE  
  45. #define com_zj_sodemo_MainActivity_MODE_WORLD_READABLE 1L  
  46. #undef com_zj_sodemo_MainActivity_MODE_WORLD_WRITEABLE  
  47. #define com_zj_sodemo_MainActivity_MODE_WORLD_WRITEABLE 2L  
  48. #undef com_zj_sodemo_MainActivity_DEFAULT_KEYS_DIALER  
  49. #define com_zj_sodemo_MainActivity_DEFAULT_KEYS_DIALER 1L  
  50. #undef com_zj_sodemo_MainActivity_DEFAULT_KEYS_DISABLE  
  51. #define com_zj_sodemo_MainActivity_DEFAULT_KEYS_DISABLE 0L  
  52. #undef com_zj_sodemo_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL  
  53. #define com_zj_sodemo_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L  
  54. #undef com_zj_sodemo_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL  
  55. #define com_zj_sodemo_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L  
  56. #undef com_zj_sodemo_MainActivity_DEFAULT_KEYS_SHORTCUT  
  57. #define com_zj_sodemo_MainActivity_DEFAULT_KEYS_SHORTCUT 2L  
  58. #undef com_zj_sodemo_MainActivity_RESULT_CANCELED  
  59. #define com_zj_sodemo_MainActivity_RESULT_CANCELED 0L  
  60. #undef com_zj_sodemo_MainActivity_RESULT_FIRST_USER  
  61. #define com_zj_sodemo_MainActivity_RESULT_FIRST_USER 1L  
  62. #undef com_zj_sodemo_MainActivity_RESULT_OK  
  63. #define com_zj_sodemo_MainActivity_RESULT_OK -1L  
  64. /* 
  65.  * Class:     com_zj_sodemo_MainActivity 
  66.  * Method:    helloJNI 
  67.  * Signature: ()Ljava/lang/String; 
  68.  */  
  69. JNIEXPORT jstring JNICALL Java_com_zj_sodemo_MainActivity_helloJNI  
  70.   (JNIEnv *, jobject);  
  71.   
  72. #ifdef __cplusplus  
  73. }  
  74. #endif  
  75. #endif  


裏面有函數聲明JNIEXPORT jstring JNICALL Java_com_zj_sodemo_MainActivity_helloJNI

接下來就是實現該函數聲明


#############################################3

生成.h文件後我們就可以編寫相應的C/C++實現代碼


在jni目錄下新建main.cpp,代碼如下:

  1. #include <iostream>  
  2. #include <jni.h>  
  3. #include <android/log.h>  
  4. #include "com_zj_sodemo_MainActivity.h"  
  5. using namespace std;  
  6.   
  7. #define LOG_TAG "zj"  
  8. #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)  
  9.   
  10. extern "C"  
  11. {  
  12.   
  13. /* 
  14.  * Class:     com_zj_sodemo_MainActivity 
  15.  * Method:    helloJNI 
  16.  * Signature: ()Ljava/lang/String; 
  17.  */  
  18. JNIEXPORT jstring JNICALL Java_com_zj_sodemo_MainActivity_helloJNI  
  19.   (JNIEnv *env, jobject obj)  
  20.   {  
  21.     LOGI("now in jni world");  
  22.   
  23.     return env->NewStringUTF("Hello JNI");  
  24.   }  
  25.   
  26. }  

程序介紹:

1.生成so文件需要頭文件jni.h;

2.輸出log需要頭文件android/log.h;

3.需要包含頭文件com_zj_sodemo_MainActivity.h並實現其中聲明的函數

4.如果使用C++代碼,則必須實現本地方法的聲明extern "C"(阻止C++編譯器生產C++特有的代碼)

5.JNICALL Java_com_zj_sodemo_MainActivity_helloJNI定義中實現了一條記錄的輸出,並返回一個字符串


#################################################


參考:

android studio 使用 jni 編譯 opencv 完整實例 之 圖像邊緣檢測!從此在andrid中自由使用 圖像匹配、識別、檢測 - http://www.cnblogs.com/linguanh/p/4624768.html?utm_source=tuicool&utm_medium=referral

android ndk 入門之打印log信息 - http://blog.csdn.net/qiuxiaolong007/article/details/7548580


接下來就是生成so庫


在目錄jni中新建Android.mk:

  1. LOCAL_PATH := $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3.   
  4. LOCAL_MODULE := JNITest  
  5. LOCAL_SRC_FILES := main.cpp  
  6. LOCAL_LDLIBS +=-L$(SYSROOT)/usr/lib -lm -llog  
  7. include $(BUILD_SHARED_LIBRARY)  

新建Application.mk:

  1. APP_STL := gnustl_static  
  2. APP_CPPFLAGS := -frtti -fexceptions  
  3. APP_ABI := armeabi-v7a       #這句是設置生成的cpu指令類型,提示,目前絕大部分安卓手機支持armeabi,libs下太多類型,編譯進去 apk 包會過大  
  4. APP_PLATFORM := android-8    #這句是設置最低安卓平臺,可以不弄  

打開android stdio的控制檯窗口,進入jni路徑,運行命令ndk-build:



運行成功,同時生成so庫libJNITest.so,位於目錄libs/armeabi-v7a中:



###############################


有兩種方式使用so庫。

1.android studio默認調用src/main/jniLibs下的so文件;

2.通過配置,可以使用src/main/libs下的so文件


我們現在實現第2種。打開app/build.gradle文件,在android{}內加入:

[plain] view plain copy
  1. sourceSets {  
  2.     main() {  
  3.         jniLibs.srcDirs = ['src/main/libs']  
  4.         jni.srcDirs = []  
  5.     }  
  6. }  

同時,在gradle.properties文件中加入語句:

[plain] view plain copy
  1. android.useDeprecatedNdk=true  

編譯整個工程:點擊頂部菜單欄Build->Make Project

編譯完成後點擊運行按鈕(Run 'app',快捷鍵Shift + F10)




點擊按鈕




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章