Android调用不符合JNI命名规范的so库

现在有一个纯c++风格的arm平台的动态链接库libbytebuffer.so和头文件ByteBuffer.h,接下来我将先将其封装成符合JNI命名规范的so库,这样JNI能直接调用,然后将JNI层的代码制作成jar包,然后再给Android App调用。

这过程中有灰常多的bug,比如我电脑(Win10)装不了高版本的AS,安装程序总是装到一半就停止工作,但是AS2.1.2安装没问题,于是只能用这个版本。但是这个版本用的Java版本是1.7,然我本机装的是Java1.8。没事,后面会提到,配置下也能解决。

1. 将原生so库封装成JNI so库

a. 新建一个AS工程,命名为JNIDemo,然后会自动生成一个app,然后再在该工程下新建Java Library Moudle,命名为jlib。这样JNIDemo有一个app和一个jlib。

b. 在jlib下新建Java接口JNIInterface.java用来调用so库。

package com.lp.jlib;

public class JNIInterface {
    public native String name();
    public native boolean task();
}

then Build -> Make Moudle jlib 生成class文件, then use javah to generate cpp header file and put it in jlib/src/main/jni.

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

#ifndef _Included_com_lp_jlib_JNIInterface
#define _Included_com_lp_jlib_JNIInterface
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_lp_jlib_JNIInterface
 * Method:    name
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_lp_jlib_JNIInterface_name
  (JNIEnv *, jobject);

/*
 * Class:     com_lp_jlib_JNIInterface
 * Method:    task
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_com_lp_jlib_JNIInterface_task
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

c. then new a cpp source file named com_lp_jlib_JNIInterface.cpp in jlib/src/main/jni, and call functions in libbytebuffer.so in source file.

#include "com_lp_jlib_JNIInterface.h"
#include <Android/log.h>
#include <thread>
#include <chrono>
#include <string>
#include <functional>
#include <memory>
#include "ByteBuffer.h"

#define TAG "MainActivity"
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__)

/*
 * Class:     com_lp_jlib_JNIInterface
 * Method:    name
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_lp_jlib_JNIInterface_name
  (JNIEnv *env, jobject jobj) {
    LOGI("Java_com_lp_jlib_JNIInterface_name");
    std::this_thread::sleep_for(std::chrono::microseconds(10));
    std::string str("123");

    std::unique_ptr<int> m_thread;
    if (!m_thread)
        m_thread = std::make_unique<int>(11);

    bytebuf::ByteBuffer buf(100);
    buf.put("13:54 from bytebuffer");
    buf.flip();

    return env->NewStringUTF(buf.to_str().c_str());
}

/*
 * Class:     com_lp_jlib_JNIInterface
 * Method:    task
 * Signature: ()V
 */
JNIEXPORT jboolean JNICALL Java_com_lp_jlib_JNIInterface_task
  (JNIEnv *, jobject) {
    return true;
}
可以看到我们在Java_com_lp_jlib_JNIInterface_name函数中使用libbytebuffer的相关函数。

d. then new Android.mk & Application.mk in jlib/src/main/jni.

Application.mk:

APP_ABI := armeabi-v7a
APP_STL:=gnustl_static
APP_OPTIM := release
APP_PLATFORM := android-14
Android.mk

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE := clib
LOCAL_CFLAGS := -std=c++14
LOCAL_LDFLAGS := -Wl,--build-id
LOCAL_LDLIBS := \
	-llog \
	-lz \
	-lm \

LOCAL_SRC_FILES := com_lp_jlib_JNIInterface.cpp \

LOCAL_C_INCLUDES +=  \
src \
src/Include \
E:\ShareFolder\project\GavinLib\ByteBuffer\src

LOCAL_SHARED_LIBRARIES += bytebuffer

include $(BUILD_SHARED_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := bytebuffer
LOCAL_SRC_FILES := E:\ShareFolder\project\GavinLib\lib\armeabi-v7a\libbytebuffer.so
include $(PREBUILT_SHARED_LIBRARY)
待会在jlib/src/main下运行ndk-build命令,ndk会自动去找当前目录下的jni目录,然后编译,所以Android.mk中的当前目录为jlib/src/main/jni,源文件和头文件只要写相对路径就行了。

可以看到这里面有两个编译模块,include $(CLEAR_VARS) 开始,include $(BUILD_SHARED_LIBRARY)结束。先看第二个,LOCAL_MODULE填依赖的库名,LOCAL_SRC_FILES填库路径。再看第一个,LOCAL_MODULE写生成的so库名字,LOCAL_SRC_FILES写源文件列表,LOCAL_C_INCLUDES写头文件目录列表,LOCAL_SHARED_LIBRARIES写依赖的so库名。then run ndk-build to generate libclib.so & libbytebuffer.so in jlib/src/main/libs, libclib.so is generated by compile, and libbytebuffer.so is copied from E:\ShareFolder\project\GavinLib\lib\armeabi-v7a\libbytebuffer.so.

2. 将so库封装成jar包

上一步中我们把原生so库封装成能在jni中调用的so库,然后我们在jlib的java代码中调用jni接口,修改JNIInterface.java

package com.lp.jlib;

public class JNIInterface {
    static {
        System.loadLibrary("clib");
        System.loadLibrary("bytebuffer");
    }
    public native String name();
    public native boolean task();
}
两个so库都要System.loadLibrary一下。这时候还没生成jar包,在app模块中添加依赖jlib后就会在jlib/build/libs下生成jlib.jar了。

Project Structure -> 选择app -> Dependencies -> 点‘+’ -> 选择jlib,然后再Build -> Make Moudle jlib就会在jlib/build/libs下生成jlib.jar了。


3. 在Android app中测试验证

Copy jlib/src/main/libs/*.so to app/src/main/jniLibs. Then call JNIInterface in Android app, 如在MainActivity.java onCreate中

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        JNIInterface jniInterface = new JNIInterface();
        Log.i(TAG, jniInterface.name());
    }
then make project & run. 然后可能会报上面提到过jdk版本的问题,修改jlib.build.gradle

apply plugin: 'java'

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    sourceCompatibility = '1.7'
    targetCompatibility = '1.7'
}
then make project & run, output "13:54 from bytebuffer" in logcat, 大功告成。


  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值