参考了网上各种教程,跌跌撞撞最终才把流程走通,特此记录一下:
有必要先交代下开发环境:
操作系统:Win7
Android Studio 3.0.1
gradle 3.0.1
首先,新建一个Android项目,然后在MainActivity的布局文件activity_main.xml文件中新增一个测试按钮,按钮id是btnTest
,效果如下所示:
然后在新增一个java类,包名是:package com.ctrip.ndktest(具体的名称以你的为主),类名是Java2CJNI,并在类中新增一个实例方法Java2C,代码如下:
package com.ctrip.ndktest;
/**
* Created by yuanlongzhang on 2018/4/17.
*/
public class Java2CJNI {
//这个就是我们将来需要调用的方法
public native String java2C();
}
文件目录图如下:
生成.h文件
然后使用菜单【Build】-【Rebuild Project】将Java2CJNI.java文件变成Java2CJNI.class文件。这些文件一般位于:你的项目目录\app\build\intermediates\classes\debug\下,打开cmd窗口或者在Android Studio中的Terminal窗口(如果该窗口不可见,可以通过菜单【View】-【Tool Windows】-【Terminal】打开),进入该目录使用javah命令为Java2CJNI.class生成对应的头文件,该命令的帮助如下:
用法:
javah [options]
其中, [options] 包括:
-o
-d
-v -verbose 启用详细输出
-h --help -? 输出此消息
-version 输出版本信息
-jni 生成 JNI 样式的标头文件 (默认值)
-force 始终写入输出文件
-classpath
-bootclasspath
(例如, java.lang.Object)。
我们使用如下指令生成对应的.h文件:
javah -classpath D:\androidstudy\app\build\intermediates\classes\debug -jni com.ctrip.ndktest.Java2CJNI
其中D:\androidstudy\app\build\intermediates\classes\debug是我机器android studio生成的java .class文件所在的目录(根据情况改成你的),com.ctrip.ndktest.Java2CJNI 是需要生成对应头文件所在的包名+类名,生成成功后,会在D:\androidstudy\app\build\intermediates\classes\debug位置生成一个com_ctrip_ndktest_Java2CJNI.h文件,我们将这个文件放到 安卓项目路径\app\src\main\jni\文件夹下,如果main目录下没有jni目录,可以新建一个,注意一定是在main目录下的jni,这个时候的com_ctrip_ndktest_Java2CJNI.h的内容如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include
/* Header for class com_ctrip_ndktest_Java2CJNI */
#ifndef _Included_com_ctrip_ndktest_Java2CJNI
#define _Included_com_ctrip_ndktest_Java2CJNI
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_ctrip_ndktest_Java2CJNI
* Method: java2C
* Signature: ()Ljava/lang/String;
*/
JNIEXPORT jstring JNICALL Java_com_ctrip_ndktest_Java2CJNI_java2C
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
然后我们在该jni目录下再新建一个.c文件Java2C.c,并将com_ctrip_ndktest_Java2CJNI.h中的Java_com_ctrip_ndktest_Java2CJNI_java2C函数前面拷贝过去并实现,不知道你有没有注意到,这个函数在com_ctrip_ndktest_Java2CJNI.h中的申明时,函数参数只有类型没有参数名,我们在Java2C.c需要将其补全,并在Java2C.c中包含头文件com_ctrip_ndktest_Java2CJNI.h,Java2C.c内容如下:
//
// Created by yuanlongzhang on 2018/4/17.
//
#include "com_ctrip_ndktest_Java2CJNI.h"
JNIEXPORT jstring JNICALL Java_com_ctrip_ndktest_Java2CJNI_java2C(JNIEnv* env, jobject instance)
{
return (*env)->NewStringUTF(env, "I am From Native C");
}
此时项目目录层次如下:
这些C代码最终会生成*.so文件,为了说明生成的*.so文件的类型和名称,我们需要修改app目录下的build.gradle,注意是app目录下的,不是项目目录下的build.gradle(图中蓝色箭头指向的文件):
这个build.gradle内容如下:
apply plugin: 'com.android.application'
android {
compileSdkVersion 26
defaultConfig {
applicationId "com.ctrip.ndktest"
minSdkVersion 15
targetSdkVersion 26
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
/*
Error:Execution failed for task ':libtermexec:compileReleaseNdk'.
> Error: Your project contains C++ files but it is not using a supported native build system.
Consider using CMake or ndk-build integration with the stable Android Gradle plugin:
https://developer.android.com/studio/projects/add-native-code.html
or use the experimental plugin:
https://developer.android.com/studio/build/experimental-plugin.html.
*/
ndk {
moduleName "Java2C" //生成的库名
abiFilters "armeabi-v7a", "x86" //指定的平台类型,如果不写,则默认会生成全平台的
}
}
/*
sourceSets {
main {
jni.srcDirs = []
}
}
*/
buildTypes { release { minifyEnabled false proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } }}dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) implementation 'com.android.support:appcompat-v7:26.1.0' implementation 'com.android.support.constraint:constraint-layout:1.1.0' testImplementation 'junit:junit:4.12' androidTestImplementation 'com.android.support.test:runner:1.0.1' androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1'}
网上很多文章说要加如下节点:
sourceSets {
main {
jni.srcDirs = []
}
}
在新版的android studio中是不需要添加的,如果添加了可能会导致无法生成*.so文件。另外如果不指定平台编译时会有如下的错误提示:
Information:Gradle tasks [clean, :app:assembleDebug]
Warning:Deprecated NDK integration enabled by android.deprecatedNdkCompileLease flag in gradle.properties will be removed from Android Gradle plugin in the next version.
D:\Users\zxf\AppData\Local\Android\Sdk\ndk-bundle\build\core\setup-app.mk
Error:(81) Android NDK: Application targets deprecated ABI(s): armeabi
Error:(82) Android NDK: Support for these ABIs will be removed in a future NDK release.
Information:BUILD SUCCESSFUL in 8s
Information:2 errors
Information:1 warning
Information:See complete output in console
这是因为armeabi这个平台,已经被废弃了,所以我们需要将它去掉,所以只好显式地指定生成的平台类型,在上文说的build.gradle文件中将armeabi去掉,由
ndk {
moduleName "Java2C" //生成的库名
abiFilters "armeabi", "armeabi-v7a", "x86" //指定的平台类型,如果不写,则默认会生成全平台的
}
变成:
ndk {
moduleName "Java2C" //生成的库名
abiFilters "armeabi-v7a", "x86" //指定的平台类型,如果不写,则默认会生成全平台的
}
另外,为了支持NDK,我们还需要在项目目录的gradle.properties文件中增加如下一行android.useDeprecatedNdk=true,如下图所示:
这在早些版本的android studio中确实可以,但是新版本的android studio也不行了,再次Rebuild Project时会提示如下错误:
Information:Gradle tasks [clean, :app:assembleDebug]
Error:Execution failed for task ':app:compileDebugNdk'.
> Error: Flag android.useDeprecatedNdk is no longer supported and will be removed in the next version of Android Studio. Please switch to a supported build system.
Consider using CMake or ndk-build integration. For more information, go to:
https://d.android.com/r/studio-ui/add-native-code.html#ndkCompile
To get started, you can use the sample ndk-build script the Android
plugin generated for you at:
D:\androidstudy\app\build\intermediates\ndk\debug\Android.mk
Alternatively, you can use the experimental plugin:
https://developer.android.com/r/tools/experimental-plugin.html
To continue using the deprecated NDK compile for another 60 days, set
android.deprecatedNdkCompileLease=1523965196208 in gradle.properties
Information:BUILD FAILED in 5s
Information:1 error
Information:0 warnings
Information:See complete output in console
按照错误提示,我们去掉android.useDeprecatedNdk=true,用另外一行代替android.deprecatedNdkCompileLease=1523965196208,如下图所示:
接下来我们再次Rebuild Project,就可以生成对应的*.so文件了,这些so文件的位置在:项目路径\app\build\intermediates\ndk\debug\目录下,每个平台对应一个文件夹,而这些so的名字就是我们在上面配置的名称Java2C,只不过linux系统(Android是一种linux系统)对这种库文件的格式命名按如下规则来:
lib+库名.so
这样Java2C就变成了libJava2C.so文件。如下图所示:
如何使用这些库呢?我们需要在上文中说的Java2CJNI.java文件中加载这些库,代码如下:
package com.ctrip.ndktest;
/**
* Created by yuanlongzhang on 2018/4/17.
*/
public class Java2CJNI {
static {
//System的S是大写
System.loadLibrary("Java2C");
}
public native String java2C();
}
其中
static {
//System的S是大写,loadLibrary的L也是大写!
System.loadLibrary("Java2C");
}
就是我们新增的代码。
最后我们在MainActivity中使用这个Java2CJNI类和它的Java2C()方法:
package com.ctrip.ndktest;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
((Button)findViewById(R.id.btnTest)).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
String result = new Java2CJNI().java2C();
Toast.makeText(MainActivity.this, "click test button", Toast.LENGTH_SHORT).show();
}
});
}
}
String result = new Java2CJNI().java2C();这一行就是我们添加的,这样一来,我们就可以使用了。程序运行效果如下:
点击TEST按钮就会有提示了。注意第一次调试这个apk可能需要加载很多其他文件,会比较慢,需要耐心等待一下。另外,检测一个NDK程序是否正常,你不仅需要看下对应的so文件是否生成了,你可以将生成的apk文件解压,看下是否在根目录下的lib目录下有对应的so文件,如果没有lib目录或者无对应的so文件,你的生成的apk文件也会有问题。无法运行,运行的时会提示如下错误:
如果正确的话,apk解压后会有个lib目录,目录中有各个平台的so文件:
文章配套源码下载地址:https://download.csdn.net/download/analogous_love/10355768