AndroidStudio下的NDK开发

转载请注明本文出自文韬_武略的博客(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,内容是:

/* 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
生成的头文件有点问题:把#include <jni.h> 修改成#include "jni.h"。否则,你看到的代码将会被标红报错!

然后把生成的头文件拷贝到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


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值