ndk-stack从报错的堆栈信息中可以得到错误对应的源文件和行号。该工具在ndk安装目录下prebuil/windows-x86_64/bin下。首先将报错堆栈信息保存为error.log文件。然后执行
.\ndk-stack.exe -sym 工程所在目录\build\intermediates\cmake\debug\obj\armeabi-v7a\ -dump error.log
即可得到错误对应的源文件和行号
ndk-stack从报错的堆栈信息中可以得到错误对应的源文件和行号。该工具在ndk安装目录下prebuil/windows-x86_64/bin下。首先将报错堆栈信息保存为error.log文件。然后执行
.\ndk-stack.exe -sym 工程所在目录\build\intermediates\cmake\debug\obj\armeabi-v7a\ -dump error.log
即可得到错误对应的源文件和行号
首先新建一个模块,比如Android Library。在该模块的java类中,首先载入c++的共享库,语句如下:
static{
System.loadLibrary("libname");
}
//libname就是下面要建立的cpp文件的名称
后面声明使用JNI的java方法,比如
public native String stringFromJNI();
其中的native不可省略
完成后在该模块下src/main目录下新建cpp文件夹, 然后在cpp文件夹下新建cpp文件(文件名要与java类中载入共享库的名称相同),实现该JNI方法。所对应的c++函数名形式为:
#include <jni.h> //必要语句 extern "C" JNIEXPORT jstring JNICALL Java_包名_类名_方法名(JNIEnv *env) 注:一般在写的时候AS会给出提示,建议直接使用提示生成。
cpp文件写完后, 再在该文件夹下新建CMakelists.txt文件。内容如下:
# 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)
# 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.
libname
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
src/main/cpp/libname.cpp)
# 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.
libname
# Links the target library to the log library
# included in the NDK.
${log-lib} )
然后再在该模块下的build.gradle中添加对c++的编译配置信息。在defaultConfig节点下,添加
externalNativeBuild {
cmake {
cppFlags "-fexceptions"
arguments "-DANDROID_STL=c++_shared"
}
ndk{
abiFilters 'armeabi-v7a', 'arm64-v8a' ,'x86'
}
}
作用是配置为共享库,与设置目标平台。接着再将CMakeLists.txt关联到模块,在android节点下添加
externalNativeBuild {
cmake {
path "src/main/cpp/CMakeLists.txt"
}
}
至此模块的任务算是完成了。可以在其它模块中引入该模块使用。引入方式为:
import 该模块包名.模块类名;
另外要在其模块的build.gradle中,dependencies节点中,添加
implementation project(‘:模块名’)
在AS中,有时会要传递bitmap给c++函数,使用opencv等工具进行处理。这时会将bitmap传递给jni函数。bitmap在jni的函数参数中,是jobject类型。下面介绍如何将传入的bitmap转化成opencv常用的mat类型,从而进行处理
首先要包含bitmap的头文件<android/bitmap.h>。这个头文件中定义了一些重要信息。了解这些信息非常必要
AndroidBitmapInfo 一个包含bitmap常用信息的结构体
/** Bitmap info, see AndroidBitmap_getInfo(). */
typedef struct {
/** The bitmap width in pixels. */
uint32_t width;
/** The bitmap height in pixels. */
uint32_t height;
/** The number of byte per row. */
uint32_t stride;
/** The bitmap pixel format. See {@link AndroidBitmapFormat} */
int32_t format;
/** Unused. */
uint32_t flags; // 0 for now
} AndroidBitmapInfo;
AndroidBitmapFormat 图片格式的枚举类型。格式为图片的像素位数。最常见的是ANDROID_BITMAP_FORMAT_RGBA_8888
/** Bitmap pixel format. */
enum AndroidBitmapFormat {
/** No format. */
ANDROID_BITMAP_FORMAT_NONE = 0,
/** Red: 8 bits, Green: 8 bits, Blue: 8 bits, Alpha: 8 bits. **/
ANDROID_BITMAP_FORMAT_RGBA_8888 = 1,
/** Red: 5 bits, Green: 6 bits, Blue: 5 bits. **/
ANDROID_BITMAP_FORMAT_RGB_565 = 4,
/** Deprecated in API level 13. Because of the poor quality of this configuration, it is advised to use ARGB_8888 instead. **/
ANDROID_BITMAP_FORMAT_RGBA_4444 = 7,
/** Alpha: 8 bits. */
ANDROID_BITMAP_FORMAT_A_8 = 8,
};
AndroidBitmap_getInfo 获取bitmap信息的函数。返回值为后面的枚举类型。成功读取的返回值为0
/**
* Given a java bitmap object, fill out the AndroidBitmapInfo struct for it.
* If the call fails, the info parameter will be ignored.
*/
int AndroidBitmap_getInfo(JNIEnv* env, jobject jbitmap,
AndroidBitmapInfo* info);
/** AndroidBitmap functions result code. */
enum {
/** Operation was successful. */
ANDROID_BITMAP_RESULT_SUCCESS = 0,
/** Bad parameter. */
ANDROID_BITMAP_RESULT_BAD_PARAMETER = -1,
/** JNI exception occured. */
ANDROID_BITMAP_RESULT_JNI_EXCEPTION = -2,
/** Allocation failed. */
ANDROID_BITMAP_RESULT_ALLOCATION_FAILED = -3,
};
AndroidBitmap_lockPixels 非常关键的一个函数。将bitmap的像素在内存中的地址进行锁定,确保这一块内存不会被移动。传入的addrPtr是一个二级指针,*addPtr是指向bitmap像素地址的指针。该函数正确调用后,一定要在该像素地址指针不再使用后,使用AndroidBitmap_unlockPixels进行释放。
/**
* Given a java bitmap object, attempt to lock the pixel address.
* Locking will ensure that the memory for the pixels will not move
* until the unlockPixels call, and ensure that, if the pixels had been
* previously purged, they will have been restored.
*
* If this call succeeds, it must be balanced by a call to
* AndroidBitmap_unlockPixels, after which time the address of the pixels should
* no longer be used.
*
* If this succeeds, *addrPtr will be set to the pixel address. If the call
* fails, addrPtr will be ignored.
*/
int AndroidBitmap_lockPixels(JNIEnv* env, jobject jbitmap, void** addrPtr);
AndroidBitmap_unlockPixels
/**
* Call this to balance a successful call to AndroidBitmap_lockPixels.
*/
int AndroidBitmap_unlockPixels(JNIEnv* env, jobject jbitmap);
了解以上信息之后,就可以探索如何将传入的bitmap转为需要的mat了。通用的做法是先获取bitmap的信息
AndroidBitmapInfo img_info; CV_Assert(AndroidBitmap_getInfo(env, bitmap, &img_info) >= 0); CV_Assert(img_info.format == ANDROID_BITMAP_FORMAT_RGBA_8888);
使用CV_Assert确保获取信息成功
然后初始化一个指针,并将该指针的地址传递给AndroidBitmap_lockPixels,若成功则该指针即指向bitmap的像素在内存中的地址
void *ori_img_pxl = NULL; CV_Assert(AndroidBitmap_lockPixels(env, bitmap, &ori_img_pxl) >= 0); CV_Assert(ori_img_pxl);
然后就可以将该指针传递给mat的构造函数,创建出我们需要的mat了。
Mat ori_img_mat(img_info.height, img_info.width, CV_8UC4, ori_img_pxl);
需要注意得到的mat的数据指针是指向bitmap的像素内存的,因此mat中对像素的操作会直接反映在bitmap中
AndroidBitmap_unlockPixels(env, bitmap);
最后要记得释放锁定!