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

【Android性能优化】使用NDK进行Java和C++混编

2016-05-15 09:53 681 查看
转载请注明原文地址

笔者把Android重难点和读书笔记都整理在github上:https://github.com/miomin/AndroidDifficulty

如果你觉得对你有帮助的话,希望可以star/follow一下哟,我会持续保持更新。

一、Java和C/C++混编的步骤

(1)在Java代码中声明本地方法

(2)实现Java本地接口(JNI)粘合层

(3)创建Android makefile文件(Android Studio不需要,Gradle代替)

(4)使用C/C++实现native方法

(5)编译native库

(6)加载native库

1、声明本地方法

JniUtils.java

private static native long fibonacciNative(int n);


  写好之后clean然后rebuild,可以看到生成了classes文件夹。

2、实现JNI粘合层

打开Terminal,输入命令,进入到debug文件夹。

cd app/build/intermediates/classes/debug


继续输入命令,生成头文件。

javah -jni com.scu.miomin.learnndk.JniUtils


这个时候查看debug文件夹,可以看到多了一个.h文件,剪切一下,在scr/main下创建jni文件夹,把这个.h文件粘贴进去。

下面上代码

com_scu_miomin_learnndk_JniUtils.h

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

#ifndef _Included_com_scu_miomin_learnndk_JniUtils
#define _Included_com_scu_miomin_learnndk_JniUtils
#ifdef __cplusplus
extern "C" {
#endif

JNIEXPORT jlong JNICALL Java_com_scu_miomin_learnndk_JniUtils_fibonacciNative
(JNIEnv *, jclass, jint);

#ifdef __cplusplus
}
#endif
#endif


jni.c

//
// Created by miomin on 2015/8/29.
//

#include "com_scu_miomin_learnndk_JniUtils.h"
#include "fibonacci.h"

JNIEXPORT jlong JNICALL Java_com_scu_miomin_learnndk_JniUtils_fibonacciNative
(JNIEnv *env, jobject obj, jint n) {
return fibonacci(n);
}


fibonacci.h

//
// Created by 莫绪旻 on 16/5/14.
//

#ifndef LEARNNDK_FIBONACCI_H
#define LEARNNDK_FIBONACCI_H

#include <stdio.h>

extern long fibonacci(unsigned int n);

#endif //LEARNNDK_FIBONACCI_H


fibonacci.c

//
// Created by miomin on 2015/8/29.
//
#include "fibonacci.h"

long fibonacci(unsigned int n) {
if (n > 1) return fibonacci(n - 2) + fibonacci(n - 1);
return n;
}


3、配置gradle

在gradle.properties文件末尾添加android.useDeprecatedNdk=true

在app下的build.gradle中defaultConfig括号内添加如下代码

ndk {
moduleName "JniUtils"  //生成的so名字
abiFilters "armeabi", "armeabi-v7a", "x86" //输出指定三种abi体系结构下的so库,目前可有可无。
}


4、加载native库

在JniUtils.java中添加如下static代码块

static {
try {
System.loadLibrary("JniUtils");//之前在build.gradle里面设置的so名字,必须一致}
success = true;
} catch (Throwable e) {

}
}


5、为Native提供备用的Java方案

有些设备不能支持NDK所支持的所有ABI的任意一种,这时候就调用不了native方法,为了方式应用奔溃,我们提供如下解决方案。

/**
* Created by miomin on 16/5/13.
*/
public class JniUtils {

private static final boolean useNative;

static {
boolean success;
try {
System.loadLibrary("JniUtils");//之前在build.gradle里面设置的so名字,必须一致}
success = true;
} catch (Throwable e) {
success = false;
}
useNative = success;
}

public static long fibonacci(int n) {

if (useNative)
return fibonacciNative(n);

return fibonacciJava(n);
}

private static long fibonacciJava(int n) {
if (n > 1)
return fibonacciJava(n - 2) + fibonacciJava(n - 1);
return n;
}

private static native long fibonacciNative(int n);
}


二、更多关于JNI的信息

1、JNI字符串

在JNI中使用字符串参数传递,经常会导致性能问题,Java使用Unicode编码字符串,而C/C++使用char*(ASC或UTF-8),Java的字符串必须转换成C/C++字符串才能使用。看下面的例子:

JniUtils.java

public static void withString(String s) {

if (useNative)
withStringNative(s);
else
withStringNative(s);
}

private static void withStringJava(String s) {

}

private static native void withStringNative(String s);


jni.c

JNIEXPORT void Java_com_scu_miomin_learnndk_JniUtils_withStringNative
(JNIEnv *env, jobject obj, jstring s) {
const char *str = (*env)->GetStringUTFChars(env, s, NULL);
if (str != NULL) {
// 使用str

// 释放字符串,否则会内存泄漏
(*env)->ReleaseStringChars(env, s, str);
}
}


2、在JNI中访问Java对象或方法

JniUtils.java

static {
boolean success;
try {
System.loadLibrary("JniUtils");//之前在build.gradle里面设置的so名字,必须一致}
success = true;
} catch (Throwable e) {
success = false;
}
useNative = success;

if (success)
getIds();
}

// 避免每次访问域时都去获取一次id,应该在类加载时获取,只执行一次
private static native void getIds();

public static int i = 0;

public static void sayHello() {

if (useNative)
sayHelloNative();
else
sayHelloJava();
}

private static native void sayHelloNative();

private static void sayHelloJava() {

}

public static void callFromJNI() {
Log.i("miomin", "callFromJni");
}


JniUtils.c

static jfieldID iId;
static jfieldID callFromJNIId;

JNIEXPORT void JNICALL Java_com_scu_miomin_learnndk_JniUtils_sayHelloNative
(JNIEnv *env, jclass clazz) {
// 增加i
jint i = (*env)->GetStaticIntField(env, clazz, iId);
(*env)->SetStaticIntField(env, clazz, callFromJNIId, i + 1);

// 调用callFromJNI
(*env)->CallStaticVoidMethod(env, clazz, callFromJNIId);
}

JNIEXPORT void JNICALL Java_com_scu_miomin_learnndk_JniUtils_getIds
(JNIEnv *env, jclass clazz) {
// 获取i的域和callFromJNI方法的id
iId = (*env)->GetStaticFieldID(env, clazz, "i", "I");
callFromJNIId = (*env)->GetStaticMethodID(env, clazz, "callFromJNI", " ()V");
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: