您的位置:首页 > 移动开发 > Android开发

Android jni/NDK 开发流程(注意事项)

2017-03-07 00:00 441 查看
1:配置NDK环境变量,将ndk根目录配置到path当中。

2:新建Android工程,在local.properties中配置ndk路径,比如:

ndk.dir=H\:\\opt\\android-sdk-windows19-24\\ndk-bundle

在gradle.properties文件中加上:

android.useDeprecatedNdk=true(为了适配低版本的ndk,不加编译可能会不过),Android Studio3.0之后改成

android.useDeprecatedNdk=true


3:新建Jni类,并写好本地方法,比如:

package com.zhy.youku;

/**
*   生成头文件之前要运行  set classpath=E:\Android\Studio_App\Jni_Demo\app\src\main\java
* Created by kzp on 2017/3/7.
*/
public class Jni {
public native int add(int x, int y);
}

4:在cmd中,到工程的java目录(src\main\java)执行 javah com.zhy.youku.Jni,则会生成一个头文件,如:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_zhy_youku_Jni */

#ifndef _Included_com_zhy_youku_Jni
#define _Included_com_zhy_youku_Jni
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class:     com_zhy_youku_Jni
* Method:    add
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_zhy_youku_Jni_add
(JNIEnv *, jobject, jint, jint);

#ifdef __cplusplus
}
#endif
#endif

新建一个文件夹,取名jni,并将编译好的头文件剪切进去。(生成头文件的另一个方法:make后在build\intermediates\classes\debug目录下执行javah -classpath . -jni 包名.类名)即可生成。

5:在jni文件夹中,新建一个C的资源文件,文件名字任意取,并include头文件, 完成C方法,如图:

#include "com_zhy_youku_Jni.h"

JNIEXPORT jint JNICALL Java_com_zhy_youku_Jni_add
(JNIEnv *env, jobject obj, jint jint1, jint jint2){

return jint1 + jint2;
}

6:在项目的build.gradle文件中添加ndk配置:

apply plugin: 'com.android.application'

android {
compileSdkVersion 25
buildToolsVersion "25.0.2"

defaultConfig {
applicationId "com.zhy.youku"
minSdkVersion 15
targetSdkVersion 25
versionCode 1
versionName "1.0"

ndk{
moduleName "jni_test"  //动态库的名称
abiFilters "armeabi-v7a", "x86", "armeabi" //支持的CPU版本
ldLibs "log"  //增加打印信息
}
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
testCompile 'junit:junit:4.12'
compile 'com.android.support:appcompat-v7:25.2.0'
}

7:最后new Jni(),就可以调用C方法:(不要忘了在调用之前要动态加载本地库,库名要和build.gradle中配置的名字相同);

static {
System.loadLibrary("jni_test");
}

调用:

Log.e("m_tag", "result=" + new Jni().add(1,1));

正确打印结果:03-07 14:35:50.186 18069-18069/com.zhy.youku E/m_tag: result=2。

二:上面讲的是java调用C的过程,下面将的是C通过java的反射机制调用java类当中的方法的过程,生成头文件的方法和上面是一样的,不同的是C方法中实现步骤的不同。

1:Jni文件夹为:

package zhy.ccalljava;

import android.util.Log;

/**
* Created by kzp on 2017/3/1.
*/

public class Jni {

static {
System.loadLibrary("ccalljava");
}

/**
* 当执行这个代码的时候,让C代码调用 add(int x, int y)
*/
public native void callbackAdd();

/**
* 当执行这个代码的时候,让C代码调用 helloFromJava()
*/
public native void callbackHelloFromJava();

/**
* 当执行这个代码的时候,让C代码调用 printString()
*/
public native void callbackPrintString();

/**
* 当执行这个代码的时候,让C代码调用静态方法 sayHello(String s)()
*/
public native void callbackSayHello();

public int add(int x, int y) {
Log.e("m_tag", "add() x=" + x + " y=" + y);
return x + y;
}

public void helloFromJava() {
Log.e("m_tag", "helloFromJava()");
}

public void printString(String s) {
Log.e("m_tag","C中输入的:" + s);
}

public static void sayHello(String s){
Log.e("m_tag",  "我是java代码中的JNI." + "java中的sayHello(String s)静态方法,我被C调用了:"+ s);
}

}

2:C的实现步骤为:

//
// Created by kzp on 2017/3/1.
//

//获取java方法签名:在build->intermediates->classes->debug 目录下  javah -s 报名.类名

#include "zhy_ccalljava_Jni.h"
#include <stdlib.h>
#include <stdio.h>

#include <android/log.h>
#define LOG_TAG "m_tag" //增加打印 app.gradld中也要配置
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

/**
* add(int x, int y)
*/
JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackAdd(JNIEnv *env, jobject obj){

//得到字节码
jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni");

//得到方法  (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);  //参数:env, 类, 方法名, 方法签名
jmethodID methodId = (*env)->GetMethodID(env, jclazz, "add", "(II)I");
//实例化该类
jobject jobject1 = (*env)->AllocObject(env, jclazz);
//调用方法 (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
jint value = (*env)->CallIntMethod(env, jobject1, methodId, 1, 1); //成功调用

LOGE("value=%d\n", value);

}

/**
* helloFromJava()
*/
JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackHelloFromJava
(JNIEnv *env, jobject obj){
jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni");
jmethodID methodId = (*env)->GetMethodID(env, jclazz, "helloFromJava", "()V");
jobject jobject1 = (*env)->AllocObject(env, jclazz);
(*env)->CallVoidMethod(env, jobject1, methodId);

}

/**
* 让C代码调用printString(String s)
*/
JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackPrintString
(JNIEnv *env, jobject obj){

jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni");
jmethodID methodId = (*env)->GetMethodID(env, jclazz, "printString", "(Ljava/lang/String;)V");
jobject jobject1 = (*env)->AllocObject(env, jclazz);

jstring jstring1 = (*env)->NewStringUTF(env, "i am ard");

(*env)->CallVoidMethod(env, jobject1, methodId, jstring1);

}

/**
* 让C代码调用sayHello(String s)
* 该方法是静态的
*/
JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackSayHello
(JNIEnv *env, jobject obj){

jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni");
jmethodID methodId = (*env)->GetStaticMethodID(env, jclazz, "sayHello", "(Ljava/lang/String;)V");

//jobject jobject1 = (*env)->AllocObject(env, jclazz); sayHello方法是静态的 此步骤省掉

jstring jstring1 = (*env)->NewStringUTF(env, "i am static mothod");

(*env)->CallStaticVoidMethod(env, jclazz, methodId, jstring1);
}

3:在MainActivity中调用的步骤为:

package zhy.ccalljava;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

Jni jni;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

jni = new Jni();

findViewById(R.id.add).setOnClickListener(this);
findViewById(R.id.string_void).setOnClickListener(this);
findViewById(R.id.print).setOnClickListener(this);
findViewById(R.id.static_hello).setOnClickListener(this);
}

@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.add:
jni.callbackAdd();
break;

case R.id.string_void:
jni.callbackHelloFromJava();
break;

case R.id.print:
jni.callbackPrintString();
break;

case R.id.static_hello:
jni.callbackSayHello();
break;
}

}
}


补充:获取java方法签名:在build->intermediates->classes->debug 目录下 javah -s 报名.类名

在C方法中打印log信息的方法:1;导库文件

#include <android/log.h>
#define LOG_TAG "m_tag"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)

2;在build.gradle的ndk配置中加log,如:

apply plugin: 'com.android.application'

android {
compileSdkVersion 25
buildToolsVersion "25.0.2"

defaultConfig {
applicationId "zhy.ccalljava"
minSdkVersion 17
targetSdkVersion 25
versionCode 1
versionName "1.0"

testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

ndk{
moduleName "ccalljava"
ldLibs "log"

}

}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

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.1.0'
testCompile 'junit:junit:4.12'
}

3;打印信息

LOGE("value=%d\n", value);
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: