摘要
基本原理:为什么要用jni,此处就不讲了,网上已经很详细了。写本文的原因是在网上没有发现用最新版AS2.3进行ndk开发的,所以抛砖引玉一下。在java代码中调用c代码,大致步骤如下:
1.在需要调用c代码的地方声明native方法。
2.在c代码中对该native方法进行实现。
3.通过ndk开发工具包和gradle进行编译。
4.在调用的地方,加载生成的so库文件,调用方法。
5.下面详细讲解,写的不对的地方欢迎大家提出来。
第一步:修改Project的build.gradle文件
首先修改project工程文件的build.gradle文件,把gradle换成实验gradle-exprimental。具体请看详情图。
接下来需要修改这个文件,见图:
第二步:修改app的build.gradle文件
此处修改较大,先上一个效果图,然后直接上代码:
// 注意:此处已修改,原来为apply plugin: 'com.android.application'
apply plugin: 'com.android.model.application'
// 用model包裹android的内容
model{
android {
compileSdkVersion 25
buildToolsVersion "25.0.0"
defaultConfig {
applicationId "com.wangjin.hellondkdemo"
minSdkVersion.apiLevel 19 // 此处需要修改,添加 .apiLevel 原来为minSdkVersion 14
targetSdkVersion.apiLevel 25 // 此处需要修改,添加 .apiLevel
versionCode 1
versionName "1.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
// 此处需要修改,原来为 proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
// 修改成下面这句话
proguardFiles.add(file('proguard-android.txt'))
}
}
ndk {
moduleName "hello"
}
}
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
compile 'com.android.support:appcompat-v7:25.2.0'
compile 'com.android.support.constraint:constraint-layout:1.0.0-beta2'
testCompile 'junit:junit:4.12'
}
第三步:配置项目的NDK开发工具包
话说,还是图片深得我心,直接上图最可靠。。。
关键代码来了:
编译完成后,打开local.propertices文件,显示如下图 则成功
此时,说明项目的ndk配置基本完成。
接下来,需要在app的build.gradle文件中添加要生成的so库文件的文件名,代替Android.mk文件,也就是说,以后直接在gradle中配置一句话,直接省略了mk文件的编写,是不是很爽。
至此,配置的终于完成了 ,哇咔咔,不过话说回来,也不是特别难吗,相信第二次只要3分钟绝对能配置完成。
第四步:关键点来了,新建jni文件夹,编写c文件
还是贴图把吧,最后会把代码demo地址发出来
1.首先在app/src/main文件夹下新建一个和java同级的jni文件夹
2.在jni文件夹下新建c文件
3.各种扯皮结束了,是时候开始撸代码
大家都知道,第一步肯定是手撕native方法。我们在MainActivity中新建了两个方法,第一个helloFromJni,调用c代码返回一个字符串;第二个callBackJavafromC,是调用c代码,然后在c代码中调用我们定义好的logout方法在控制台打印一句话。
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 调用native方法
}
static {
// 加载so库文件,这个是固定写法,需要调用so库文件的方法,首先需要加载so库
System.loadLibrary("hello"); // hello是我们在build.gradle中定义的,要一致
}
public native String helloFromJni();
public native void callBackJavafromC();
public void logout() {
System.out.println("hahahahahahahahahah===");
}
}
4.定义了native方法后,方法名会报红,是因为没有在c代码中实现,所以接下来是实现c代码,并处理相应的逻辑,此处需要一定的c语言基础,
只要看懂就行,基本的操作也不难,大家找个速成的c语言视频look一下。先上两张图,解释一下怎样快速生成c代码。
5.此时,在我们新建的hello.c中就实现了我们的方法,只需要处理相应的逻辑就行了。
6.上面4和5两步基本上讲清楚了我们native方法的声明和c代码的实现,下面贴一下 我们自己定义的两个方法的实现,一个是返回一个字符串,另一个是回调java的方法,打印一句话。下面看c代码的实现,在我们新建的hello.c文件中:
#include <jni.h>
/**
* 这个是返回一个字符串的c代码
*/
JNIEXPORT jstring JNICALL
Java_com_wangjin_hellondkdemo_MainActivity_helloFromJni(JNIEnv *env, jobject instance) {
char* c ="hello from c";
return (*env)->NewStringUTF(env, c);
}
/**
* 这个是回调java方法打印一句话的代码
*/
JNIEXPORT void JNICALL
Java_com_wangjin_hellondkdemo_MainActivity_callBackJavafromC(JNIEnv *env, jobject obj) {
// 1.通过反射找到类
jclass clazz = (*env) -> FindClass(env, "com/wangjin/hellondkdemo/MainActivity");
// 2. 找到方法ID
jmethodID methodId = (*env) -> GetMethodID(env, clazz, "logout", "()V");
// 3.调用方法,obj就是调用的类实例,所以不用再次创建了
(*env)->CallVoidMethod(env, obj, methodId);
}
7.此时,native方法和c的实现都写好了,只需要在oncreat()方法diao用就行了。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 调用c中的方法弹出Toast
Toast.makeText(this, helloFromJni(), Toast.LENGTH_LONG).show();
// java调用c然后回调java方法,在logcat中打印 hahahahaahah====
callBackJavafromC();
}
大功告成
看到此处是不是觉得jni的逻辑也是很简单的,有什么不明白的随时留言联系,我会很快处理的。最后让我打一波广告,我的淘宝店顺利开业,地址程序员潮流店铺地址
,大家点击进去瞅一瞅,多多支持,谢谢各位猿猿们。
最后github地址:
GitHub地址,点我就行