本文以一个简单的Android App来说明如何实现在Java中调用C函数,以及如何在Android Studio中使用NDK进行开发
APP功能:在Java代码中调用C语言编写的两数之和函数计算出结果,然后将结果显示出来。
一、安装和配置 NDK 开发环境
请参考下面文章
https://blog.csdn.net/u012851408/article/details/103474412
二、创建Android项目
JniTest.java的内容如下:
package com.example.jnitest;
public class JniTest {
private static final String TAG = "JniTest";
static {
System.loadLibrary("SumLib");
}
public native int sum(int x, int y);
}
注意:在Android Studio Java代码中写native本地方法的时候会提示红色异常,解决方法是在File->Settings->Editor->Code Style->Inspections->Android中找到Missing JNI funciotn ,把对钩去掉然后点击Apply即可
MainActivity代码如下:
package com.example.jnitest;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
public class MainActivity extends Activity {
private EditText value1,value2;
private TextView result;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
value1 = findViewById(R.id.value1);
value2 = findViewById(R.id.value2);
result = findViewById(R.id.result);
Button equal = findViewById(R.id.equal);
equal.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String v1 = value1.getText().toString();
String v2 = value2.getText().toString();
if(v1.isEmpty() || v2.isEmpty()){
Toast.makeText(MainActivity.this,"Please input number1 and number2 first!",Toast.LENGTH_LONG).show();
}else{
int data1 = Integer.parseInt(v1);
int data2 = Integer.parseInt(v2);
JniTest jniTest = new JniTest();
result.setText(""+jniTest.sum(data1,data2));
}
}
});
}
}
三、生成本地方法原型
1、创建jni目录
在项目右键,选择New->Folder->JNI Folder,在弹出的对话框中选择Finish就会在项目的app/src/main/目录下创建jni目录。
2、生成本地方法原型.h文件
可以通过命令行生成.h文件也可以通过配置Android Studio通过编译生成。
命令行方法:
先进入的项目的app/src/main/java目录然后执行下面命令,就会在当前目录下生成com_example_jnitest_JniTest.h文件,最后把com_example_jnitest_JniTest.h文件移动到app/src/main/jni目录即可
javah -jni com.example.jnitest.JniTest
通过Android Studio的方法:
点击File->Settings->Tools->External Tools 打开外部工具配置页面,点击"+"选项新建一个工具。
Name和Description:自己命名,如javah。
Program:系统中javah程序的路径
Arguments:-classpath . -jni -d $ModuleFileDir$/src/main/jni $FileClass$
Working directory:$ModuleFileDir$/src/main/java
填写完成后点击OK就会保存并创建工具。然后右击JniTest.java,在菜单中选择External Tools->javah就可以快速的生成com_example_jnitest_JniTest.h文件并放到jni目录
com_example_jnitest_JniTest.h文件内容如下
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_jnitest_JniTest */
#ifndef _Included_com_example_jnitest_JniTest
#define _Included_com_example_jnitest_JniTest
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_jnitest_JniTest
* Method: sum
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_jnitest_JniTest_sum
(JNIEnv *, jobject, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
四、编写C代码
在jni目录下新建一个c语言源码,这里叫做sumlib.c。然后实现com_example_jnitest_JniTest.h文件中所定义的函数
在函数体中实现传入参数两数之和的运算,然后回调Java代码中的CalculationResult(String result)方法将结果反馈给java程序
代码如下:
#include <com_example_jnitest_JniTest.h>
#include <android/log.h>
#define LOG_TAG "Syste.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG , LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO , LOG_TAG, __VA_ARGS__)
/*
* Class: com_example_jnitest_JniTest
* Method: sum
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_jnitest_JniTest_sum
(JNIEnv * env, jobject object, jint x, jint y) {
LOGI("Native method is called!");
return x + y;
}
五、创建Android.mk和Application.mk文件
mk文件用于告诉ndk-build该如何编译C源码,在jni目录下依次创建Android.mk和Application.mk文件
Android.mk文件内容如下:
LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := SumLib
LOCAL_SRC_FILES := \
sum.c
#Add Log
LOCAL_LDLIBS := -lm -llog
include $(BUILD_SHARED_LIBRARY)
Application.mk文件内容如下:
APP_MODULES := SumLib
APP_ABI := all
六、编译C程序生成
我们需要通过Android Studio调用ndb-build来编译,首先需要在项目module的build.gradle里,android.defaulConfig中加入ndk和sourceSet.main内容配置,如下:
ndk{
moduleName "SumLib"
ldLibs "log"
}
sourceSets.main{
jni.srcDirs = []
jniLibs.srcDirs "src/main/libs"
}
C程序的编译有两种方法
第一种方法:可以使用ndk-build命令编译或外部工具方便编译
外部工具添加方法:点击File->Setting->Tools->External Tools 打开外部工具配置页面,点击"+"选项新建一个工具。
Name和Description自己命名,我填的ndk-build。
Program是你系统中ndk-build程序的路径,Arguments填:不填
Working directory填:$ProjectFileDir$/app/src/main
填写完成后点击OK就会保存创建工具。然后在任意地方右击在菜单中选择External Tools->ndk-build即可编译C源码,成功后就可看见创建了libs目录,里面包含了不同平台下的动态库文件
用同样的方法可以添加一个清除C库的选项如下
Name和Description自己命名,我填的ndk-build clean。
Program是你系统中ndk-build程序的路径,Arguments填:clean
Working directory填:$ProjectFileDir$/app/src/main
第二种方法:可以将NDK Build添加到build.gradle中这样每次编译app的时候就会默认编译C库,而且这样的话在写C代码的时候Android Studio会和Java代码一样有提示补全代码功能。添加方法如下:
1.在File->Project Structrue->SDK Location->Android NDK location中设置NDK的路径
2.在Android视图下,项目名右键->Link C++ Project with Gradle
Build System 选ndk-build
Project Path 填:项目中Android.mk的绝对路径
点击OK 后你就会在module build.gradle的 android选项中看到
externalNativeBuild {
ndkBuild {
path 'src/main/jni/Android.mk'
}
}
七、编译项目生成apk,安装到设备,效果如下
八、程序源码