參考:
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代碼如下:
- <?xml version="1.0" encoding="utf-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:paddingBottom="@dimen/activity_vertical_margin"
- android:paddingLeft="@dimen/activity_horizontal_margin"
- android:paddingRight="@dimen/activity_horizontal_margin"
- android:paddingTop="@dimen/activity_vertical_margin"
- tools:context="com.zj.sodemo.MainActivity">
- <TextView
- android:id="@+id/text"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:textAlignment="center"
- android:text="Hello World!"
- android:layout_alignParentTop="true"
- />
- <Button
- android:id="@+id/button"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="click me"
- android:layout_alignParentBottom="true"
- />
- </RelativeLayout>
功能介紹:加入一個文本框和按鈕。apk運行後,點擊按鈕調用C程序,文本框顯示返回的字符串
修改MainActivity.java代碼如下:
- package com.zj.sodemo;
- import android.app.Activity;
- import android.support.v7.app.AppCompatActivity;
- import android.os.Bundle;
- import android.view.View;
- import android.widget.Button;
- import android.widget.TextView;
- public class MainActivity extends Activity {
- private Button mButton;
- private TextView mTextView;
- private String text = null;
- static {
- System.loadLibrary("JNITest");
- }
- public native String helloJNI();
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- mButton = (Button)findViewById(R.id.button);
- mTextView = (TextView)findViewById(R.id.text);
- mButton.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View view) {
- text = helloJNI();
- mTextView.setText(text);
- }
- });
- }
- }
至此,前期準備工作都已完成
##########################################################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
- cd app/src/main/
- mkdir jni
運行以下命令:
- javah -d jni -classpath /opt/android-sdk-linux/platforms/android-24/android.jar:../../build/intermediates/classes/debug/ com.zj.sodemo.MainActivity
-classpath-從中加載類的路徑。沒有配置android.jar環境變量的話就需要在這裏加入(注意,android.jar後面跟的是一個冒號:,而不是分號;)
com.zj.sodemo.MainActivity是生成JNI樣式的表頭文件
在這裏我們將生成的.h文件保存在jni文件夾內:
打開文件com_zj_sodemo_MainActivity.h:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_zj_sodemo_MainActivity */
- #ifndef _Included_com_zj_sodemo_MainActivity
- #define _Included_com_zj_sodemo_MainActivity
- #ifdef __cplusplus
- extern "C" {
- #endif
- #undef com_zj_sodemo_MainActivity_BIND_ABOVE_CLIENT
- #define com_zj_sodemo_MainActivity_BIND_ABOVE_CLIENT 8L
- #undef com_zj_sodemo_MainActivity_BIND_ADJUST_WITH_ACTIVITY
- #define com_zj_sodemo_MainActivity_BIND_ADJUST_WITH_ACTIVITY 128L
- #undef com_zj_sodemo_MainActivity_BIND_ALLOW_OOM_MANAGEMENT
- #define com_zj_sodemo_MainActivity_BIND_ALLOW_OOM_MANAGEMENT 16L
- #undef com_zj_sodemo_MainActivity_BIND_AUTO_CREATE
- #define com_zj_sodemo_MainActivity_BIND_AUTO_CREATE 1L
- #undef com_zj_sodemo_MainActivity_BIND_DEBUG_UNBIND
- #define com_zj_sodemo_MainActivity_BIND_DEBUG_UNBIND 2L
- #undef com_zj_sodemo_MainActivity_BIND_EXTERNAL_SERVICE
- #define com_zj_sodemo_MainActivity_BIND_EXTERNAL_SERVICE -2147483648L
- #undef com_zj_sodemo_MainActivity_BIND_IMPORTANT
- #define com_zj_sodemo_MainActivity_BIND_IMPORTANT 64L
- #undef com_zj_sodemo_MainActivity_BIND_NOT_FOREGROUND
- #define com_zj_sodemo_MainActivity_BIND_NOT_FOREGROUND 4L
- #undef com_zj_sodemo_MainActivity_BIND_WAIVE_PRIORITY
- #define com_zj_sodemo_MainActivity_BIND_WAIVE_PRIORITY 32L
- #undef com_zj_sodemo_MainActivity_CONTEXT_IGNORE_SECURITY
- #define com_zj_sodemo_MainActivity_CONTEXT_IGNORE_SECURITY 2L
- #undef com_zj_sodemo_MainActivity_CONTEXT_INCLUDE_CODE
- #define com_zj_sodemo_MainActivity_CONTEXT_INCLUDE_CODE 1L
- #undef com_zj_sodemo_MainActivity_CONTEXT_RESTRICTED
- #define com_zj_sodemo_MainActivity_CONTEXT_RESTRICTED 4L
- #undef com_zj_sodemo_MainActivity_MODE_APPEND
- #define com_zj_sodemo_MainActivity_MODE_APPEND 32768L
- #undef com_zj_sodemo_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING
- #define com_zj_sodemo_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING 8L
- #undef com_zj_sodemo_MainActivity_MODE_MULTI_PROCESS
- #define com_zj_sodemo_MainActivity_MODE_MULTI_PROCESS 4L
- #undef com_zj_sodemo_MainActivity_MODE_NO_LOCALIZED_COLLATORS
- #define com_zj_sodemo_MainActivity_MODE_NO_LOCALIZED_COLLATORS 16L
- #undef com_zj_sodemo_MainActivity_MODE_PRIVATE
- #define com_zj_sodemo_MainActivity_MODE_PRIVATE 0L
- #undef com_zj_sodemo_MainActivity_MODE_WORLD_READABLE
- #define com_zj_sodemo_MainActivity_MODE_WORLD_READABLE 1L
- #undef com_zj_sodemo_MainActivity_MODE_WORLD_WRITEABLE
- #define com_zj_sodemo_MainActivity_MODE_WORLD_WRITEABLE 2L
- #undef com_zj_sodemo_MainActivity_DEFAULT_KEYS_DIALER
- #define com_zj_sodemo_MainActivity_DEFAULT_KEYS_DIALER 1L
- #undef com_zj_sodemo_MainActivity_DEFAULT_KEYS_DISABLE
- #define com_zj_sodemo_MainActivity_DEFAULT_KEYS_DISABLE 0L
- #undef com_zj_sodemo_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL
- #define com_zj_sodemo_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L
- #undef com_zj_sodemo_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL
- #define com_zj_sodemo_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L
- #undef com_zj_sodemo_MainActivity_DEFAULT_KEYS_SHORTCUT
- #define com_zj_sodemo_MainActivity_DEFAULT_KEYS_SHORTCUT 2L
- #undef com_zj_sodemo_MainActivity_RESULT_CANCELED
- #define com_zj_sodemo_MainActivity_RESULT_CANCELED 0L
- #undef com_zj_sodemo_MainActivity_RESULT_FIRST_USER
- #define com_zj_sodemo_MainActivity_RESULT_FIRST_USER 1L
- #undef com_zj_sodemo_MainActivity_RESULT_OK
- #define com_zj_sodemo_MainActivity_RESULT_OK -1L
- /*
- * Class: com_zj_sodemo_MainActivity
- * Method: helloJNI
- * Signature: ()Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_com_zj_sodemo_MainActivity_helloJNI
- (JNIEnv *, jobject);
- #ifdef __cplusplus
- }
- #endif
- #endif
裏面有函數聲明JNIEXPORT jstring JNICALL Java_com_zj_sodemo_MainActivity_helloJNI
接下來就是實現該函數聲明
#############################################3
生成.h文件後我們就可以編寫相應的C/C++實現代碼
在jni目錄下新建main.cpp,代碼如下:
- #include <iostream>
- #include <jni.h>
- #include <android/log.h>
- #include "com_zj_sodemo_MainActivity.h"
- using namespace std;
- #define LOG_TAG "zj"
- #define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
- extern "C"
- {
- /*
- * Class: com_zj_sodemo_MainActivity
- * Method: helloJNI
- * Signature: ()Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_com_zj_sodemo_MainActivity_helloJNI
- (JNIEnv *env, jobject obj)
- {
- LOGI("now in jni world");
- return env->NewStringUTF("Hello JNI");
- }
- }
程序介紹:
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:
- LOCAL_PATH := $(call my-dir)
- include $(CLEAR_VARS)
- LOCAL_MODULE := JNITest
- LOCAL_SRC_FILES := main.cpp
- LOCAL_LDLIBS +=-L$(SYSROOT)/usr/lib -lm -llog
- include $(BUILD_SHARED_LIBRARY)
新建Application.mk:
- APP_STL := gnustl_static
- APP_CPPFLAGS := -frtti -fexceptions
- APP_ABI := armeabi-v7a #這句是設置生成的cpu指令類型,提示,目前絕大部分安卓手機支持armeabi,libs下太多類型,編譯進去 apk 包會過大
- 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{}內加入:
- sourceSets {
- main() {
- jniLibs.srcDirs = ['src/main/libs']
- jni.srcDirs = []
- }
- }
同時,在gradle.properties文件中加入語句:
- android.useDeprecatedNdk=true
編譯整個工程:點擊頂部菜單欄Build->Make Project
編譯完成後點擊運行按鈕(Run 'app',快捷鍵Shift + F10)
點擊按鈕