【Android性能优化】使用NDK进行Java和C++混编
16lz
2021-01-24
转载请注明原文地址
笔者把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 /* Header for class com_scu_miomin_learnndk_JniUtils */#ifndef _Included_com_scu_miomin_learnndk_JniUtils#define _Included_com_scu_miomin_learnndk_JniUtils#ifdef __cplusplusextern "C" {#endifJNIEXPORT 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 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");}
更多相关文章
- Android实现类似excel表格的方法整理
- 一文彻底搞懂Android(安卓)View的绘制流程
- 剖析 Android(安卓)架构组件之 ViewModel
- AIDL跨进程通信和Service调用
- Android(安卓)ContentProvider 使用
- Android(安卓)TV框架TIF
- android中setNegativeButton和setNeutralButton的区别是什么?
- Android(安卓)Material Design之Snackbar
- Android(安卓)ANR介绍与避免