Java Language | Type | Native Type Description |
---|---|---|
boolean | jboolean | unsigned 8 bits |
byte | jbyte | signed 8 bits |
char | jchar | unsigned 16 bits |
short | jshort | signed 16 bits |
int | jint | signed 32 bits |
long | jlong | signed 64 bits |
float | jfloat | 32 bits |
double | jdouble | 64 bits |
Java生成类方法签名,JNI回调应用层时特别有用
Compiled from "JniParser.java"
public class com.gosuncn.libparser.jni.JniParser {
public static com.gosuncn.libparser.jni.JniParser getInstance();
descriptor: ()Lcom/gosuncn/libparser/jni/JniParser;
public void responseDataCallBack(long, int, java.lang.String);
descriptor: (JILjava/lang/String;)V
public native long createParser();
descriptor: ()J
...
}
jni主要有三种引用类型
上面三种JNI引用的作用不同,作用域也不尽一样,也有着不同的生命周期
对于局部引用,在本地方法被调用时创建,在方法返回时(return),该引用将会自动被释放,因此在方法返回之后再使用该引用是不合法的。并不是任意引用都可以使用在所有上下文环境。但在其有效时,将一直阻止所引用的对象被GC回收。
对于全局引用和弱全局引用,它们可以在多个方法中使用,在手动释放之前一直有效。但是弱全局引用不会阻止GC回收它所引用的对象,因此使用此引用前判空是必要的
A JNIEnv pointer is only valid in the thread associated with it. You must not
pass this pointer from one thread to another, or cache and use it in multiple
threads.
JNIEXPORT void JNICALL
Java_com_junmeng_libnative_JniException_invokeJavaException(
JNIEnv *env,
jclass clazz//注意静态方法与普通方法的区别就是jobject变成了jclass
) {
//jclass jcls = env->FindClass("com/junmeng/libnative/JniException");//由于是静态方法,已经有了jclass,因此就不需要再FindClass了
jmethodID op = env->GetMethodID(clazz, "exception", "()I");//exception就是java层定的回调函数
jmethodID mid2 = env->GetMethodID(clazz, "", "()V");
jobject job = env->NewObject(clazz, mid2);
env->CallIntMethod(job, op);
//一般是用在回调java层时,即使java层产生了异常,也不会直接奔溃掉
jthrowable exc = env->ExceptionOccurred();//检测java层是否有异常产生
if (exc) {
env->ExceptionDescribe();//输出异常信息
env->ExceptionClear();//清除异常
}
}
在jni中经常需要将数据回调给应用层,通常的做法是将要回调给应用层的对象序列化为json字符串(cJSON此库非常好用,下载后也有例子说明),传递到应用层再反序列化为对象使用。
下面举例说明
public class JniInstant {
private static final String TAG = "JniInstant";
static {
System.loadLibrary("instant");
}
private static JniInstant instance;
private JniInstant() {
}
public static JniInstant getInstance() {
if (instance == null) {
instance = new JniInstant();
}
return instance;
}
/**
* 初始化
* 此接口必须首先调用,且成功后才可使用其他接口
*
* @return 0表示成功
*/
public native int init();
//回调///
/**
* GxxApp异常回调
* descriptor: (ILjava/lang/String;)V
* @param status 0--下线 1--上线
*/
public void gxxAppExceptionCallback(int status,String json){
Log.e(TAG, "gxxAppExceptionCallback:status(0--下线 1--上线)= "+status);
}
// c或cpp文件中
#include
JavaVM *jvmInstant= NULL;
jobject objInstant = NULL;
jmethodID gxxAppExceptionCallback=NULL;
extern "C"
JNIEXPORT jint JNICALL
Java_com_gosuncn_instant_jni_JniInstant_init(JNIEnv *env, jobject instance) {
env->GetJavaVM(&jvmInstant);
//函数参数中 jobject 或者它的子类,其参数都是 local reference。Local reference 只在这个 JNI函数中有效,JNI函数返回后,引用的对象就被释放,它的生命周期就结束了。
// 若要留着日后使用,则需根据这个 local reference 创建 global reference。Global reference 不会被系统自动释放,它仅当被程序明确调用 DeleteGlobalReference 时才被回收。(JNI多线程机制)
objInstant = env->NewGlobalRef(instance);
//在子线程中不能这样用
// jclass objclass = env->FindClass( "com/xxx/xxx/JniRGBPlayer");
//这种写法可以用在子线程中,但限制了回调方法的位置必须在当前类中,如本例必须在JniInstant类里
jclass objclass = env->GetObjectClass(instance);
//此处的gxxAppExceptionCallback为应用层的回调方法名,回调方法必须在应用层的java类里,如本例是在JniInstant类里,关于方法对应的方法签名(如"(I)V")可使用javap获得,具体在文章中有说明
gxxAppExceptionCallback = env->GetMethodID(objclass, "gxxAppExceptionCallback", "(ILjava/lang/String;)V");
return 0;
}
...
/**
* 判断是否已经绑定线程
* @param env 注意这里用取地址符
* @return true--已经绑定成功,false--已经绑定,无需再绑定
*/
bool attachThread(JavaVM *mJavaVM,JNIEnv* &env) {
bool attached = false;
if (mJavaVM == NULL) {
LOGE("JavaVM == NULL!!!,please check");
return attached;
}
switch (mJavaVM->GetEnv((void **) &env, JNI_VERSION_1_6)) {
case JNI_OK://已绑定
LOGI("JNI_OK");
break;
case JNI_EDETACHED://已解绑
LOGI("JNI_EDETACHED");
if (mJavaVM->AttachCurrentThread(&env, NULL) != JNI_OK) {
LOGE("Could not attach current thread. ");
} else {
attached = true;
}
break;
case JNI_EVERSION:
LOGE("Invalid java version. ");
}
return attached;
}
/**
* GxxApp异常消息回调
* @param lHandle 登陆句柄
* @param eEvent 异常事件
* @param pUserData 用户数据
*/
void funPtrExceptionCallback(long lHandle,
EnumExceptionEvent eEvent,char *jsonData
void *pUserData) {
JNIEnv *env;
bool attached = attachThread(jvmInstant, env);
//在此处将数据回调到应用层,,如本例中JniInstant类中的gxxAppExceptionCallback即可收到此回调
if (env != NULL && objInstant != NULL && gxxAppExceptionCallback != NULL) {
jstring msg = env->NewStringUTF(jsonData);//返回到java层,由jvm进行释放
//CallVoidMethod后面为方法的参数,方法签名时有多少个,就得写多少个,本例中只有一个整型参数
env->CallVoidMethod(objInstant, gxxAppExceptionCallback, (jint) eEvent,msg );
}
if (attached) {
jvmInstant->DetachCurrentThread();
}
}
有时候经常需要返回多个参数到应用层,那么目前想到用数组作为返回参数,以下示例
public native String[] test();
extern "C"
JNIEXPORT jobjectArray JNICALL
Java_com_gosuncn_vs_JniAlarm_test(JNIEnv *env, jobject instance) {
// 获取类对象
jclass cls = env->FindClass("java/lang/String");
jobjectArray strArray = env->NewObjectArray(2, cls, NULL);
jobject s0 = env->NewStringUTF("1");
jobject s1 = env->NewStringUTF("中文ing");
env->SetObjectArrayElement(strArray, 0, s0);
env->SetObjectArrayElement(strArray, 1, s1);
return strArray;
}
#include
#include
int tid = (int)syscall(SYS_gettid); //获得线程id
int pid = (int)syscall(SYS_getpid);//获得进程id
第一种写法
lock(&mutex);
//一些操作
pthread_cond_signal(&cond);
//一些操作
unlock(&mutex);
缺点:在某些线程的实现中,会造成等待线程从内核中唤醒(由于cond_signal)回到用户空间,然后pthread_cond_wait返回前需要加锁,但是发现锁没有被释放,又回到内核空间所以一来一回会有性能的问题。
但是在LinuxThreads或者NPTL里面,就不会有这个问题,因为在Linux 线程中,有两个队列,分别是cond_wait队列和mutex_lock队列, cond_signal只是让线程从cond_wait队列移到mutex_lock队列,而不用返回到用户空间,不会有性能的损耗。所以Linux中这样用没问题。
第二种写法
lock(&mutex);
//一些操作
unlock(&mutex);
pthread_cond_signal(&cond);
优点:不会出现之前说的那个潜在的性能损耗,因为在signal之前就已经释放锁了
缺点:如果unlock之后signal之前,发生进程交换,另一个进程(不是等待条件的进程)拿到这把梦寐以求的锁后加锁操作,那么等最终切换到等待条件的线程时锁被别人拿去还没归还,只能继续等待
//note:use "/" not "." to splite
char beanPackage[] = "com/gosuncn/libvgis/bean";
/**
* 将java对象转为native层结构体
* @param[in] env
* @param[in] xy
* @param[out] xyValue
* @return 是否成功
*/
bool toXYValue(JNIEnv *env, jobject xy,xy_value * xyValue) {
jclass xy_cls = env->GetObjectClass(xy);
if (xy_cls == NULL) {
LOGE("toXYValue failed.");
return false;
}
jfieldID xFieldID = env->GetFieldID(xy_cls, "x", "I");
jfieldID yFieldID = env->GetFieldID(xy_cls, "y", "I");
jint x = env->GetIntField(xy, xFieldID);
jint y = env->GetIntField(xy, yFieldID);
xyValue->x = x;
xyValue->y = y;
LOGI("toXYValue: x=%d,y=%d", x, y);
return true;
}
jobject xy2jobject(JNIEnv *env, xy_value xyValue) {
char cls_path[64];
strcpy(cls_path, beanPackage);
strcat(cls_path, "/VgisXY");
jclass clas = env->FindClass(cls_path);
if (clas == NULL) {
LOGE("%s/VgisXY failed.", beanPackage);
return NULL;
}
jmethodID construct = env->GetMethodID(clas, "" , "()V");
jobject obj = env->NewObject(clas, construct);
jfieldID xFieldID = env->GetFieldID(clas, "x", "I");
jfieldID yFieldID = env->GetFieldID(clas, "y", "I");
env->SetIntField(obj, xFieldID, xyValue.x);
env->SetIntField(obj, yFieldID, xyValue.y);
return obj;
}
// We use std::nothrow so `new` returns a nullptr if the engine creation fails
HelloOboeEngine *engine = new(std::nothrow) HelloOboeEngine();
#ifndef MEDIAAPP_LOG_H
#define MEDIAAPP_LOG_H
#include
#ifndef DEFAULT_LOG_TAG
#define DEFAULT_LOG_TAG "GS28181AgentSDK"
#endif
#define isDebug true
#define LOGV(...) if(isDebug){__android_log_print(ANDROID_LOG_VERBOSE, DEFAULT_LOG_TAG, __VA_ARGS__);}
#define LOGD(...) if(isDebug){__android_log_print(ANDROID_LOG_DEBUG, DEFAULT_LOG_TAG, __VA_ARGS__);}
#define LOGI(...) if(isDebug){__android_log_print(ANDROID_LOG_INFO, DEFAULT_LOG_TAG, __VA_ARGS__);}
#define LOGITAG(TAG,...) if(isDebug){__android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);}
#define LOGW(...) if(isDebug){__android_log_print(ANDROID_LOG_WARN, DEFAULT_LOG_TAG, __VA_ARGS__);}
#define LOGE(...) if(isDebug){__android_log_print(ANDROID_LOG_ERROR, DEFAULT_LOG_TAG, __VA_ARGS__);}
#define LOGETAG(TAG,...) if(isDebug){__android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);}
#endif //MEDIAAPP_LOG_H
以上__VA_ARGS__
是一个可变参数的宏,上面的文件简单够用
但是你会不会需要动态地改变日志是否输出呢,也即日志是否打印是根据用户自己设置的,而不是硬编码的
那不是很容易吗,封装一下就可以了,但是__android_log_print里有可变参数,这下就不好封装了,不过我们可以借助stdarg.h中的va_list来帮助我们实现,注意使用的是__android_log_vprint而不是__android_log_print了
GSLog.h文件
//
// Created by HuWeiJian on 2019/12/20.
//
#ifndef GSLOG_H
#define GSLOG_H
#include
#define DEFAULT_LOG_TAG "yourTagName"
/**
* 设置是否打印日志
* @param bSwitch
*/
void setLogSwitch(bool bSwitch);
void LOGV(const char *fmt, ...);
void LOGTV(const char* tag,const char *fmt, ...);
void LOGD(const char *fmt, ...) ;
void LOGTD(const char* tag,const char *fmt, ...);
void LOGI(const char *fmt, ...) ;
void LOGTI(const char* tag,const char *fmt, ...);
void LOGW(const char *fmt, ...) ;
void LOGTW(const char* tag,const char *fmt, ...) ;
void LOGE(const char *fmt, ...);
void LOGTE(const char* tag,const char *fmt, ...) ;
void LOGF(const char *fmt, ...);
void LOGTF(const char* tag,const char *fmt, ...) ;
#endif //GSLOG_H
GSLog.cpp文件
//
// Created by HuWeiJian on 2019/12/20.
//
#include "GSLog.h"
bool m_log_switch=true;
void setLogSwitch(bool bSwitch){
m_log_switch=bSwitch;
}
void LOGV(const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_VERBOSE, DEFAULT_LOG_TAG, fmt, arg);
va_end(arg);
}
void LOGTV(const char* tag,const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_VERBOSE, tag, fmt, arg);
va_end(arg);
}
void LOGD(const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_DEBUG, DEFAULT_LOG_TAG, fmt, arg);
va_end(arg);
}
void LOGTD(const char* tag,const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_DEBUG, tag, fmt, arg);
va_end(arg);
}
void LOGI(const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_INFO, DEFAULT_LOG_TAG, fmt, arg);
va_end(arg);
}
void LOGTI(const char* tag,const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_INFO, tag, fmt, arg);
va_end(arg);
}
void LOGW(const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_WARN, DEFAULT_LOG_TAG, fmt, arg);
va_end(arg);
}
void LOGTW(const char* tag,const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_WARN, tag, fmt, arg);
va_end(arg);
}
void LOGE(const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_ERROR, DEFAULT_LOG_TAG, fmt, arg);
va_end(arg);
}
void LOGTE(const char* tag,const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_ERROR, tag, fmt, arg);
va_end(arg);
}
void LOGF(const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_FATAL, DEFAULT_LOG_TAG, fmt, arg);
va_end(arg);
}
void LOGTF(const char* tag,const char *fmt, ...) {
va_list arg;
va_start(arg, fmt);
if (m_log_switch) __android_log_vprint(ANDROID_LOG_FATAL, tag, fmt, arg);
va_end(arg);
}
以上的va_start意思就是获取可变参数的首地址指针,第二个参数填的是可变参数(这里是指…)的前一个参数(这里是指fmt),va_end与va_start成对使用。
/**
* 将jstring转化为GBK编码char数组
* @param [in]env
* @param [in]jstr
* @param [out]returnChar 用户自己管理内存,自己开辟自己销毁
* @param [in]returnCharLen 用户开辟的returnChar的长度,如果长度不够会导致失败
* @return 字符串长度,为-1则表示失败
*/
int jstringToPCharGBK(JNIEnv *env, jstring jstr, char *returnChar, int returnCharLen) {
if (returnChar == NULL) {
return 0;
}
jclass tmpClass = env->FindClass("java/lang/String");
jstring strencode = env->NewStringUTF("gb2312");
jmethodID mid = env->GetMethodID(tmpClass, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray) env->CallObjectMethod(jstr, mid, strencode);
jsize alen = env->GetArrayLength(barr);
if(alen<=0){
return 0;
}
if (returnCharLen <= alen) {
return -1;
}
jbyte *ba = env->GetByteArrayElements(barr, JNI_FALSE);
strcpy(returnChar, reinterpret_cast<const char *>(ba));
returnChar[alen]=0;
env->ReleaseByteArrayElements(barr, ba, 0);
return alen;
}
/**
* 将GBK编码的字符串char*转化为jstring
* @param env
* @param pchar
* @return
*/
jstring charToJstringGBK(JNIEnv *env, const char *pchar ) {
// 定义java String类 strClass
jclass strClass = env->FindClass("java/lang/String");
// 获取java String类方法String(byte[],String)的构造器,用于将本地byte[]数组转换为一个新String
jmethodID ctorID = env->GetMethodID(strClass, "" , "([BLjava/lang/String;)V");
// 建立byte数组
jbyteArray bytes = env->NewByteArray(strlen(pat));
// 将char* 转换为byte数组
env->SetByteArrayRegion(bytes, 0, strlen(pat), (jbyte *) pat);
//设置String, 保存语言类型,用于byte数组转换至String时的参数
jstring encoding = env->NewStringUTF(
"gb-2312");
//将byte数组转换为java String,并输出
jstring result = (jstring) env->NewObject(strClass, ctorID, bytes, encoding);
env->DeleteLocalRef(bytes);
env->DeleteLocalRef(encoding);
return result;
}
//
// Created by HuWeiJian on 2018/6/20.
//
#ifndef INSTANTSDK_COMMONUTIL_H
#define INSTANTSDK_COMMONUTIL_H
#include
#ifdef __cplusplus
extern "C" {
#endif
/**
* 获取当前系统时间戳,精确到毫秒
* @return
*/
int64_t getCurrentTime();
/**
* 获得当前时间描述
* 格式yyyyMMddHHmmss
* @param [out]nowTimeString 输出时间描述
*/
void getNowTimeString(char * nowTimeString);
/**
* 获得当前时间描述
* 格式yyyy-MM-ddTHH:mm:ss
* @param [out]nowTimeString 输出时间描述
*/
void getNowTimeStandString(char *nowTimeString);
#ifdef __cplusplus
}
#endif
#endif //INSTANTSDK_COMMONUTIL_H
//
// Created by HuWeiJian on 2018/6/20.
//
#include "CommonUtil.h"
#include
#include
#include
int64_t getCurrentTime() {
struct timeval tv;
gettimeofday(&tv, NULL);
int64_t ts = (int64_t)tv.tv_sec*1000 + tv.tv_usec/1000;
return ts;
}
/**
* 获得当前时间描述
* 格式yyyyMMddHHmmss
* @param nowTime
*/
void getNowTimeString(char *nowTime)
{
char acYear[5] = {0};
char acMonth[5] = {0};
char acDay[5] = {0};
char acHour[5] = {0};
char acMin[5] = {0};
char acSec[5] = {0};
time_t now;
struct tm* timenow;
time(&now);
timenow = localtime(&now);
strftime(acYear,sizeof(acYear),"%Y",timenow);
strftime(acMonth,sizeof(acMonth),"%m",timenow);
strftime(acDay,sizeof(acDay),"%d",timenow);
strftime(acHour,sizeof(acHour),"%H",timenow);
strftime(acMin,sizeof(acMin),"%M",timenow);
strftime(acSec,sizeof(acSec),"%S",timenow);
strncat(nowTime, acYear, 4);
strncat(nowTime, acMonth, 2);
strncat(nowTime, acDay, 2);
strncat(nowTime, acHour, 2);
strncat(nowTime, acMin, 2);
strncat(nowTime, acSec, 2);
}
/**
* 获得当前时间描述
* 格式yyyy-MM-ddTHH:mm:ss
* @param nowTime
*/
void getNowTimeStandString(char *nowTime)
{
char acYear[5] = {0};
char acMonth[5] = {0};
char acDay[5] = {0};
char acHour[5] = {0};
char acMin[5] = {0};
char acSec[5] = {0};
time_t now;
struct tm* timenow;
time(&now);
timenow = localtime(&now);
strftime(acYear,sizeof(acYear),"%Y",timenow);
strftime(acMonth,sizeof(acMonth),"%m",timenow);
strftime(acDay,sizeof(acDay),"%d",timenow);
strftime(acHour,sizeof(acHour),"%H",timenow);
strftime(acMin,sizeof(acMin),"%M",timenow);
strftime(acSec,sizeof(acSec),"%S",timenow);
strncat(nowTime, acYear, 4);
strncat(nowTime, "-", 1);
strncat(nowTime, acMonth, 2);
strncat(nowTime, "-", 1);
strncat(nowTime, acDay, 2);
strncat(nowTime, "T", 1);
strncat(nowTime, acHour, 2);
strncat(nowTime, ":", 1);
strncat(nowTime, acMin, 2);
strncat(nowTime, ":", 1);
strncat(nowTime, acSec, 2);
}
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
set(JNILIBS_SO_PATH ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs)
# 定义源文件目录
get_filename_component(CPP_SRC_DIR ${CMAKE_SOURCE_DIR}/src/main/cpp ABSOLUTE)
# 定义源文件目录下的源文件,虽然这样可以避免写一大堆文件列表,但由于ide支持不够好,因此有新文件增加时需要手动删除生成的.cxx文件夹后再编译
file(GLOB_RECURSE cpp_sources *.c *.cpp)
if (ANDROID_ABI MATCHES "^armeabi-v7a$")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfloat-abi=softfp -mfpu=neon")
elseif(ANDROID_ABI MATCHES "^arm64-v8a")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -O2 -ftree-vectorize")
endif()
# Specifies a path to native header files.
include_directories(src/main/cpp/encoder/)
include_directories(src/main/cpp/camera/)
include_directories(src/main/cpp/common/)
include_directories(src/main/cpp/audio/)
include_directories(src/main/cpp/media/)
include_directories(src/main/cpp/libyuv/include/libyuv/)
include_directories(src/main/cpp/libyuv/include/)
//添加动态库或静态库,注意动静态库都必须放置在${ANDROID_ABI}文件夹下
add_library(libcamera SHARED IMPORTED )
set_target_properties(libcamera PROPERTIES IMPORTED_LOCATION
${JNILIBS_SO_PATH}/${ANDROID_ABI}/libcamera.so )
add_library(libyuv SHARED IMPORTED )
set_target_properties(libyuv PROPERTIES IMPORTED_LOCATION
${JNILIBS_SO_PATH}/${ANDROID_ABI}/libyuv.so )
add_library(uv STATIC IMPORTED)
set_target_properties(uv
PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/src/main/jniLibs/${ANDROID_ABI}/libuv.a)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
instant
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
${cpp_sources}
)
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
# 如果有依赖关系,被依赖的要放置在后面,假如instant依赖libyuv,则libyuv要放置在后面
instant
libyuv
# Links the target library to the log library
# included in the NDK.
${log-lib} )
#升级到gradle4之后会报错误:More than one file was found with OS independent path 'lib/armeabi/libstreamhandler.so'
#解决办法有两个:一是删除jniLibs/armeabi/libstreamhandler.so,同时注释掉下面生成so输出路径的语句即可
#二是在当前build.gradle中添加 android{ packagingOptions { pickFirst 'lib/armeabi/libstreamhandler.so' }}
#在指定目录生成so文件,注意目录区分大小写,如jniLibs_DIR的“jniLibs”必须和后面build.gradle指定的sourceSet目录中指定的“jniLibs”完全一致
set(jniLibs_DIR ${CMAKE_CURRENT_SOURCE_DIR}/src/main/jniLibs)
set_target_properties( instant
PROPERTIES
LIBRARY_OUTPUT_DIRECTORY
"${jniLibs_DIR}/${ANDROID_ABI}")
apply plugin: 'com.android.library'
android {
compileSdkVersion 27
defaultConfig {
...
externalNativeBuild {
cmake {
cppFlags "-frtti -fexceptions -D_LINUX -Wno-error=format-security"
}
}
ndk {
// Specifies the ABI configurations of your native
// libraries Gradle should build and package with your APK.
abiFilters 'armeabi-v7a'// ,'arm64-v8a','x86', 'x86_64', 'armeabi'
}
}
externalNativeBuild {
cmake {
path "CMakeLists.txt"
}
}
packagingOptions {
pickFirst 'lib/armeabi-v7a/libinstant.so'
//假如so冲突,可在此进行排除
exclude 'lib/armeabi-v7a/libavcodec-57.so'
exclude 'lib/armeabi-v7a/libAvDecodePlugin.so'
exclude 'lib/armeabi-v7a/libavdevice-57.so'
exclude 'lib/armeabi-v7a/libavfilter-6.so'
exclude 'lib/armeabi-v7a/libavformat-57.so'
exclude 'lib/armeabi-v7a/libavutil-55.so'
exclude 'lib/armeabi-v7a/libGMFLib.so'
exclude 'lib/armeabi-v7a/libGSFoundation.so'
exclude 'lib/armeabi-v7a/libGSLog.so'
exclude 'lib/armeabi-v7a/libGSUtil.so'
exclude 'lib/armeabi-v7a/libpicture.so'
exclude 'lib/armeabi-v7a/libpostproc-54.so'
exclude 'lib/armeabi-v7a/libswresample-2.so'
exclude 'lib/armeabi-v7a/libswscale-4.so'
}
}
...
POSIX线程详解 - 我的梦
https://blog.csdn.net/u013457167/article/details/89290250
Android NDK——必知必会之Native线程操作及线程同步全面详解(六)最美不过,心中有梦,身旁有你!-CSDN博客
https://blog.csdn.net/CrazyMo/article/details/82498581
Unicode(UTF-8, UTF-16)令人混淆的概念 - Boblim - 博客园
https://www.cnblogs.com/fnlingnzb-learner/p/6163205.html
JNI十大缺陷,拉低性能 - 简书
https://www.jianshu.com/p/c2354a75f50c
IntelliJ IDEA平台下JNI编程(二)—类型映射 - 简书
https://www.jianshu.com/p/3d483597d641
Android深入理解JNI(一)JNI原理与静态、动态注册 - 简书
https://www.jianshu.com/p/b9a595022462
[原创]Android逆向新手答疑解惑篇——JNI与动态注册-『Android安全』-看雪安全论坛
https://bbs.pediy.com/thread-224672.htm
JNI笔记 : 在JNI中使用引用
https://blog.csdn.net/qq_28261343/article/details/80946828
Linux条件变量pthread_condition细节(为何先加锁,pthread_cond_wait为何先解锁,返回时又加锁)_Lupin-CSDN博客
https://blog.csdn.net/shichao1470/article/details/89856443