先上一张图,同时此文章需要 NDK与JNI基础-AndriodStudio混合编译使用记录 作为铺垫

JNI引用
引用解决什么问题?解决JVM什么时候来回收JNI这个对象,
Activity中添加:
public native void getLocalReference();
CPP中去实现一个模拟:
extern "C"
JNIEXPORT void JNICALL
Java_com_lckiss_ndkgradle_MainActivity_getLocalReference(JNIEnv *env, jobject instance) {
//模拟一个循环 100对象
for (int i = 0; i < 100; ++i) {
jclass _jclass = env->FindClass("java/util/Date");
//env->GetMethodID(_jclass,"<init>","()V") 构造函数的调用方法
jobject _jobject = env->NewObject(_jclass, env->GetMethodID(_jclass, "<init>", "()V"));
//-----对象操作
//释放对象 需要手动释放
env->DeleteLocalRef(_jobject);
//全局变量释放
// env->DeleteGlobalRef(_jobject);
//弱全局引用释放
// env->DeleteWeakGlobalRef(_jobject);
}
}怎么模拟呢?试着不移除,看看内存情况呗。100不够 你就10000个,看看你的内存会不会有问题
JNI异常处理
我们尝试去构造一个异常,并在java中去捕获:
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
//为后面铺垫
public String name="Test ";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//异常处理部分 看能否捕获JNI中的异常
try{
exception();
}
catch (Exception e){
Log.d("Exception", "onCreate: "+e.getMessage());
}
//看出了异常后是否会继续
Log.d("Exception", "onCreate: ------------");
}
public native void exception();
}CPP中对应代码:
extern "C"
JNIEXPORT void JNICALL
Java_com_lckiss_ndkgradle_MainActivity_exception(JNIEnv *env, jobject instance) {
//构造一个异常
int i=0;
int j=100;
int c=j/i;
}运行后你会发现,异常发生了但是并未捕获(打印了 onCreat——),那么应该怎么做呢?
接着改下CPP
extern "C"
JNIEXPORT void JNICALL
Java_com_lckiss_ndkgradle_MainActivity_exception(JNIEnv *env, jobject instance) {
//重新构建一个异常 去获取一个不存在的属性 假定为notCreat,其为String类型
jclass _jclass= env->GetObjectClass(instance);
jfieldID _jfieldID=env->GetFieldID(_jclass,"notCreat","Ljava/lang/String");
//没有这个变量 所以会有异常
}然后你会发现APP直接挂掉,log日志里显示了异常原因,但是JAVA里还是不能捕获,而且后面的代码也不会执行,也就是一个log都不会打印
解决办法:加上头文件以及内容
#include <sstream>
extern "C"
JNIEXPORT void JNICALL
Java_com_lckiss_ndkgradle_MainActivity_exception(JNIEnv *env, jobject instance) {
//重新构建一个异常 去获取一个不存在的属性 假定为notCreat,其为String类型
jclass _jclass= env->GetObjectClass(instance);
jfieldID _jfieldID=env->GetFieldID(_jclass,"notCreat","Ljava/lang/String;");
//--没有这个变量 所以会有异常
//检测异常
jthrowable _jthrowable= env->ExceptionOccurred();
if(_jthrowable!=NULL){
//为了保证Java能继续运行,需要清除异常
env->ExceptionClear();
//补救措施 需要在Act中去创建一个name的String属性 public String name="Test ";
_jfieldID=env->GetFieldID(_jclass,"name","Ljava/lang/String;");
}
jstring _jstring= (jstring) env->GetObjectField(instance, _jfieldID);
char* str= (char *) env->GetStringUTFChars(_jstring, NULL);
//以上 就可以正常运行 如果需要java层面需要捕获怎么做?
if(strcmp(str,"www")!=0){
//抛出异常 一定要是java的异常
jclass newException= env->FindClass("java/lang/IllegalArgumentException");
env->ThrowNew(newException,"非法异常");
}
}然后运行就会发现打印了那句话,

结论:需要抛出异常时,自行定义异常并抛出
JNI缓存策略
实际上是说的 对象的生命周期的问题 上例子
Activity中:
public class MainActivity extends AppCompatActivity {
// Used to load the 'native-lib' library on application startup.
static {
System.loadLibrary("native-lib");
}
public String name="Test ";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//测试缓存机制
for (int i = 0; i <10 ; i++) {
cached();
}
}
public native void cached();
}Cpp中:
#include <android/log.h>
#define TAG "jni" // 这个是自定义的LOG的标识
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,TAG ,__VA_ARGS__) // 定义LOGD类型
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) // 定义LOGI类型
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN,TAG ,__VA_ARGS__) // 定义LOGW类型
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型
#define LOGF(...) __android_log_print(ANDROID_LOG_FATAL,TAG ,__VA_ARGS__) // 定义LOGF类型
extern "C"
JNIEXPORT void JNICALL
Java_com_lckiss_ndkgradle_MainActivity_cached(JNIEnv *env, jobject instance) {
jclass _jclass = env->GetObjectClass(instance);
jfieldID _jfieldID = NULL;
if (_jfieldID == NULL) {
_jfieldID = env->GetFieldID(_jclass, "name", "Ljava/lang/String;");
LOGI("-------------GetFieldID------------");
}
}然后结果出来后,大吃一惊,按Java的写法,应该是只会打印一次,但是打印了2次,说明了啥?
说明这是一个问题,要解决。。。。解决如下
第一种 使用静态局部变量
extern "C"
JNIEXPORT void JNICALL
Java_com_lckiss_ndkgradle_MainActivity_cached(JNIEnv *env, jobject instance) {
jclass _jclass = env->GetObjectClass(instance);
//加上static局部关键字就好了。缓存策略还有一种,就是动态加载的时候初始化全局变量
static jfieldID _jfieldID = NULL;
if (_jfieldID == NULL) {
_jfieldID = env->GetFieldID(_jclass, "name", "Ljava/lang/String;");
LOGI("-------------GetFieldID------------");
}
}LOG打印可以看这篇:http://blog.csdn.net/yf210yf/article/details/9305623
第二种 动态加载的时候做成员变量的初始化
Act中:
public static native void initIds();
static {
System.loadLibrary("native-lib");
initIds();
}Cpp中(加入后,将上面的static关键字和去掉后,继续循环cached,PS:这里有一些修改):
jfieldID _jfieldID = NULL;
extern "C"
JNIEXPORT void JNICALL
Java_com_lckiss_ndkgradle_MainActivity_cached(JNIEnv *env, jobject instance) {
jclass _jclass = env->GetObjectClass(instance);
if (_jfieldID == NULL) {
_jfieldID = env->GetFieldID(_jclass, "name", "Ljava/lang/String;");
LOGI("-------------GetFieldID------------");
}
}
extern "C"
JNIEXPORT void JNICALL
Java_com_lckiss_ndkgradle_MainActivity_initIds(JNIEnv *env, jclass type) {
// static jfieldID _jfieldID = NULL;
if (_jfieldID == NULL) {
_jfieldID = env->GetFieldID(type, "name", "Ljava/lang/String;");
LOGI("-------------GetFieldID------------");
}
}最后也只会打印一次Log,这种方式在真实的企业开发中用于做成员变量的初始化,细心的同学会发现,这里是jclass而不是jobject,原因是这里是static的native方法。
这部分也可以看这篇文章:https://www.jianshu.com/p/a8e68d4da473
关于NDK与JNI,基础部分就这么多了,产生这些东西的最主要原因还是因为Java的执行效率问题,有时间将最复杂的东西放在底层用C/C++去执行会快很多。
关于AndroidStudio的一些工具以及配置文件

以前没有用Gradle的时候,用的是Eclipse,而Eclipse用的是.mk的文件 .mk文件的构建工具在NDK中是有的,就是NDK目录中的那个ndk-build,现在使用AS就不再需要了,因为被externalNativeBuild替代了。


另外就是一个LLDB的工具
作用呢就是用来调试debug之类的,需要在SDK中下载这个工具包
教程和指令在LLDB官网可以查到。在AS中调试时,与普通Java调试无异,但是需要配合命令进行,比如查看数据值的po命令,po +(变量名)、bt命令查看线程详细信息等…
日常发生崩溃时第三方SO库怎么抓日志
因为第三方SO库是没有源码的,所以不能在AS中直接debug,也就没有控制台,怎么做呢?
首先使用adb进行日志抓取:adb logcat >error.log 拿到路径
打开终端cd到SDK的ndk目录下用
ndk-stack -sym 你的SO库路径 -dump 你的Log日志路径
这样就可以拿到很详细的日志信息,就可以定位到崩溃位置
所有的源码:源码下载 (有多的C币的希望贡献两个,谢谢了,没有的上面的代码基本上都贴完了,也无大碍)
本站广告由 Google AdSense 提供
0条评论