这过程中有灰常多的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, 大功告成。