转载请注明本文出自文韬_武略的博客(http://blog.csdn.net/fwt336/article/details/52296927),请尊重他人的辛勤劳动成果,谢谢!
1.开发环境
AndroidStudio 2.2.3
SDK 24.0.0
NDK 12.1.2977051
NDK安装与配置
首先,去官网下载Android NDK到本地;
其次,创建一个Android项目工程;
最后,打开Project Structure,选择Android NDK的路径,然后确认。
此时,在local.properties文件中已经生成了这行代码:sdk.dir=D\:\\android_studio\\sdk
另外,由于ndk版本和IDE的版本可能存在不一致性,所以编译时会检查ndk的过时性,这个时候如果报警了,可以在gradle.properties文件下添加一行:android.useDeprecatedNdk=true.到此,NDK的安装和配置就完成.
2.HelloWord
1. 在main下创建jni目录,用于存放jni文件。
2. 在创建HelloWorld类:
package com.victor.hello; public class HelloWorld { static { System.loadLibrary("helloworld"); } public native People getPeopleInstance(People people); public native int add(int a, int b); public native String showResult(String value); public native int getPeopleToStr(People people); public native void setPeopleParams(People people, String name, int age); }
3.生成头文件
把项目想make一下,会在项目中生成HelloWorld的class文件:
进入到~/debug目录,cmd进入到该级目录,使用命令:
javah -classpath . -jni com.victor.jnihello.MainActivity
然后,在debug目录下会生成头文件:
com_victor_hello_HelloWorld.h,内容是:
生成的头文件有点问题:把#include <jni.h> 修改成#include "jni.h"。否则,你看到的代码将会被标红报错!/* DO NOT EDIT THIS FILE - it is machine generated */ #include "jni.h" /* Header for class com_victor_hello_HelloWorld */ #ifndef _Included_com_victor_hello_HelloWorld #define _Included_com_victor_hello_HelloWorld #ifdef __cplusplus extern "C" { #endif /* * Class: com_victor_hello_HelloWorld * Method: getPeopleInstance * Signature: ()Lcom/victor/hello/People; */ JNIEXPORT jobject JNICALL Java_com_victor_hello_HelloWorld_getPeopleInstance (JNIEnv *, jobject, jobject); /* * Class: com_victor_hello_HelloWorld * Method: add * Signature: (II)I */ JNIEXPORT jint JNICALL Java_com_victor_hello_HelloWorld_add (JNIEnv *, jobject, jint, jint); /* * Class: com_victor_hello_HelloWorld * Method: showResult * Signature: (Ljava/lang/String;)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_victor_hello_HelloWorld_showResult (JNIEnv *, jobject, jstring); /* * Class: com_victor_hello_HelloWorld * Method: getPeopleToStr * Signature: (Lcom/victor/hello/People;)I */ JNIEXPORT jint JNICALL Java_com_victor_hello_HelloWorld_getPeopleToStr (JNIEnv *, jobject, jobject); /* * Class: com_victor_hello_HelloWorld * Method: setPeopleParams * Signature: (Lcom/victor/hello/People;Ljava/lang/String;I)V */ JNIEXPORT void JNICALL Java_com_victor_hello_HelloWorld_setPeopleParams (JNIEnv *, jobject, jobject, jstring, jint); #ifdef __cplusplus } #endif #endif
然后把生成的头文件拷贝到cpp文件夹下。当然你可以在生成的时候直接就指定到该文件夹下。
4. 创建cpp文件夹和cpp源文件
在main文件夹下简历jni文件夹,将com_victor_hello_HelloWorld.h头文件的名字复制下,作为cpp文件的名字,保持跟头文件一致:com_victor_hello_HelloWorld.cpp。
最后,导入头文件:
#include "com_victor_hello_HelloWorld.h"把头文件内的方法拷贝一份到cpp里面进行实现即可。
/* * Class: com_victor_hello_HelloWorld * Method: add * Signature: (II)I */ #include "com_victor_hello_HelloWorld.h" JNIEXPORT jint JNICALL Java_com_victor_hello_HelloWorld_add (JNIEnv *env, jobject thiz, jint a, jint b) { jint result = a + b; return result; } /* * * Class: com_victor_hello_HelloWorld * Method: showResult * Signature: (I)Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_victor_hello_HelloWorld_showResult (JNIEnv *env, jobject clazz, jstring a) { // String result = "test"; return env->NewStringUTF("hello jni"); } /* * Class: Java_com_victor_hello_HelloWorld_getPeopleToStr * Method: peopleToStr * Signature: (I)Ljava/lang/String; */ JNIEXPORT jint JNICALL Java_com_victor_hello_HelloWorld_getPeopleToStr (JNIEnv *env, jobject clazz, jobject peopleObj) { //先找到com/victor/hello/People类在JNI层中对应的jclass实例。 jclass peopleInterface = env->FindClass("com/victor/hello/People"); jmethodID setAgeMethodID = env->GetMethodID(peopleInterface, "setAge", "(I)V"); env->CallIntMethod(peopleObj, setAgeMethodID, 110); jmethodID getAgeMethodID = env->GetMethodID(peopleInterface, "getAge", "()I"); jint age = env->CallIntMethod(peopleObj, getAgeMethodID); return age; } /** * set多参数 */ JNIEXPORT void JNICALL Java_com_victor_hello_HelloWorld_setPeopleParams (JNIEnv *env, jobject clazz, jobject clientObj, jstring name, jint age) { jclass peopleInterface = env->FindClass("com/victor/hello/People"); jmethodID setAgeMethodID = env->GetMethodID(peopleInterface, "setNameAndAge", "(Ljava/lang/String;I)V"); env->CallIntMethod(clientObj, setAgeMethodID, name, age); } /** * 获取Object参数 */ JNIEXPORT jobject JNICALL Java_com_victor_hello_HelloWorld_getPeopleInstance (JNIEnv *env, jobject clazz, jobject client) { Java_com_victor_hello_HelloWorld_setPeopleParams(env, clazz, client, env->NewStringUTF("JNI"), 2017); return client; }
5. Gradle配置,在build.gradle中添加so库的名称:
defaultConfig { applicationId "com.victor.hello" minSdkVersion 14 targetSdkVersion 24 versionCode 1 versionName "1.0" ndk { moduleName "helloworld" } }
这里的moduleName是在代码中需要加载库时System.loadLibrary(xxx)所;填写的名称,也是生成so包的lib后面的名称,与jni目录下的c文件名称没有关系
6. 调用本地方法
HelloWorld helloWorld = new HelloWorld(); TextView tvContent = (TextView) findViewById(R.id.tv_content); TextView tvResult = (TextView) findViewById(R.id.tv_content2); TextView tvObject = (TextView) findViewById(R.id.tv_content3); TextView tvSetNameAndAge = (TextView) findViewById(R.id.tv_content4); TextView tvGetObject = (TextView) findViewById(R.id.tv_content5); try { int a = helloWorld.add(10,5); tvContent.setText("" + a); Log.e("hello-jni", helloWorld.showResult("hehe")); } catch (Exception ex) { tvContent.setText(ex.getMessage()); } try { String helle = helloWorld.showResult("hehe"); tvResult.setText("" + helle); } catch (Exception ex) { tvResult.setText(ex.getMessage()); } People people = null; try { people = new People(); people.setAge(18); people.setName("victor"); int peopleAge = helloWorld.getPeopleToStr(people); tvObject.setText("" + peopleAge); } catch (Exception ex) { tvObject.setText(ex.getMessage()); } try { helloWorld.setPeopleParams(people, "Jack", 27); tvSetNameAndAge.setText(people.getName() + " : "+ people.getAge()); } catch (Exception ex) { tvSetNameAndAge.setText(ex.getMessage()); } try { People people1 = helloWorld.getPeopleInstance(people); tvGetObject.setText(people1.getName() + " : "+ people1.getAge()); } catch (Exception ex) { tvGetObject.setText(ex.getMessage()); }
现在,你就可以make下,然后run吧!
7.So包的产生和使用
so库包其实在你make项目之后,就已经产生了。如下图,在build中已经生成了各个版本需要的so库文件。这些so包都是以libhelloworld为名称的,由lib+moduleName组成。
当我们不想将.c和.h源码暴露在外面的时候,我们就需要使用.so库了。
AndroidStudio中so包存放的目录默认是在main/jniLibs下面,然后把所有需要的so包拷贝进入就可以了。
最后,把jni中对应的.c和.h文件删除就可以了。就像平常使用第三方库一样。
8.定制so库
到上面那一步其实就已经完成so包的创建和使用了,在使用过程中我们基本都是使用的默认配置,当然我们可以进行在gradle中配置以致自己项目所需。在项目创建的基础上:
添加需要支持的lib包: ldLibs("log")
指定编译出所需的平台:abiFilters("armeabi", "armeabi-v7a", "x86") ,这里指定这三个,当然也可以不指定,将会生成所有安卓支持的so库。
STL支持:stl = "system", STL目前可支持 system(系统默认的最小支持的C++运行时库);stlport_static(以静态链接的方式使用stlport版本的STL);stlport_shared(以动态链接的方式使用stlport版本的STL);gnustl_static(以静态链接的方式使用gnu版本的STL)
cFlags标记: cFlags("-std=c++11")
ndk { moduleName "helloworld" ldLibs("log") abiFilters("armeabi", "armeabi-v7a", "x86") stl="system" cFlags("-std=c++11") }
Rebuild Project之后,对应会生成我们知道的so包。
欢迎大家指点和回复交流。
源码:http://download.csdn.net/detail/fwt336/9769334