赞
踩
内容如其名,纯小白从零开始搞这个。原因是学长说要用as链接linux下ndk编译的so库,而不能直接用as生成,那么好的工具,说不用就不用了。不过既然有要求,那咱也不能含糊,脱下裤子,不不不,撸起袖子就是干。
神奇的又有了更新,下面是调用非JNI标准的函数的步骤,在最后面会加上调用标准JNI函数的步骤
windows10 1803版
VirtualBox 5.2.14版本下的debian-9.5.0-armd64虚拟机
Android studio 3.2
首先配置好NDK环境
1下载ndk https://developer.android.google.cn/ndk/downloads/
2 解压 unzip <filename.zip>
3 配置环境变量
#vim /etc/profile 在文件末尾添加如下
export ANDROID_NDK=“ndk路径”
export PATH="$ANDROID_NDK:$PATH"
4.更新系统变量
#source /etc/profile
5.检查ndk环境配置正确与否
#ndk-build
//出现如下界面即配置成功
AndroidNDK: Could not find application project directory !
Android NDK: Please define the NDK_PROJECT_PATH variable topoint to it.
/android-ndk-r16b/build/core/build-local.mk:151: *** Android NDK: Aborting . Stop.
新建一个文件夹jni,然后在jni文件夹下
首先需要cpp和h文件,我的只是简单做个例子,如下
testC.h
#ifndef TESTC_H
#define TESTC_H
int testC();
#endif
testC.cpp
#include "testC.h"
int testC()
{
return 6;
}
LOCAL_PATH:=$(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE :=testC
LOCAL_SRC_FILES:=testC.cpp
include $(BUILD_SHARED_LIBRARY)
APP_ABI := all # 编译类型为适配所有架构的cpu
APP_PLATFORM := android-28 # 对应版本是28
编译命令
ndk-build APP_ABI=all
会在jni同级目录下生成两个文件夹,libs和obj,libs文件夹下即为刚编译的so库
另: 如果你的cpp或者h文件引入了标准库文件的函数,如
#include <string>
这样执行编译命令的时候会报一个错
ndk-build error string No such file or directory
解决方法是在jni目录下新建一个文件Application.mk
里面添加
APP_STL := gnustl_static
即可找到标准库
新建一个项目,记得勾选include C++ support
然后一路下一步就可以了,然后把linux下编译生成的so文件 放到app下的libs文件夹下
本图已经放进来了
然后编写Cmakelist.txt,里面已经有新建项目是自动生成的东西,注意区分
# 动态方式加载 libtestC.so
add_library(testC SHARED IMPORTED)
# 设置链接so的路径,${ANDROID_ABI}为so文件的cpu架构类型
set_target_properties(testC PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libtestC.so)
# 这个原先中有,在其中添加上testC即可
target_link_libraries(testC)
因为没有主函数,所以直接在系统自动生成的样例函数中测试自己的so库,也就是native-lib.cpp文件,记得把h问价传进as
注意,红框的地方是修改了的
然后编译运行就可以了。
这里有一个问题,如果没报错但是app闪退,那么就在buiild.gradle android里添加
sourceSets{
main{
jniLibs.srcDirs = ['libs']
}
}
下面是完整的我的demo地址
https://github.com/xixihahag/first_ndk
在as编译出现下面的问题
Build command failed.
Error while executing process C:\sdk\cmake\3.6.4111459\bin\cmake.exe with arguments {--build D:\myasworkspace\test2_include_c++_support\app\.externalNativeBuild\cmake\debug\armeabi-v7a --target native-lib}
ninja: error: '../../../../app/libs/armeabi-v7a/libtestC.so', needed by '../../../../build/intermediates/cmake/debug/obj/armeabi-v7a/libnative-lib.so', missing and no known rule to make it
set_target_properties(testC PROPERTIES IMPORTED_LOCATION ${PROJECT_SOURCE_DIR}/app/libs/${ANDROID_ABI}/libtestC.so)
修改后的命令
set_target_properties(testC PROPERTIES IMPORTED_LOCATION ${CMAKE_SOURCE_DIR}/libs/${ANDROID_ABI}/libtestC.so)
错误原因
as中好像找不到
${PROJECT_SOURCE_DIR}
这个地址,改为
${CMAKE_SOURCE_DIR}
后就好了
JNI标准就是cpp文件中的函数名的命名是
Java_包名_类名_函数名
自己参照官网例子写的一个简单的如下
testJni.cpp
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_example_immortals_myapplication_MainActivity_stringFromMyJNI(
JNIEnv *env,
jobject /* this */)
{
std::string hello = "Hello from myJNI";
return env->NewStringUTF(hello.c_str());
}
修改了Android.mk文件,顺便加上了很多注释
LOCAL_PATH:=$(call my-dir) # my-dir就是该Android.mk所在目录,将这个目录复制给CAL_PATH变量
include $(CLEAR_VARS) # 清除此行之前除了LOCAL_PATH外所有的变量,因为定义的多个模块中会有相同名称的变量,目的是避免变量赋值冲突
LOCAL_MODULE :=testJni # 指定一个当前模块名
LOCAL_SRC_FILES:=testJni.cpp # 要编译的源文件
include $(BUILD_SHARED_LIBRARY) # 编译目标 表示编译成动态库 即so库
#include $(PREBUILT_SHARED_LIBRARY) # 编译目标,PREBUILT_表示已经编译好的,在使用NDK编译时不会再次编译,而是直接拷贝到libs目录
# 预编译.a静态库使用 PREBUILT_STATIC_LIBRARY
APP_ABI := all
APP_PLATFORM := android-28
直接编译运行,跑,完事。
No implementation found for java.lang.String
错误原因
大概率是cpp文件中的函数命名和包名不一致,改成一致就可以了
因为JNI是有命名规范的,上面说过是
Java_包名_类名_函数名
所以只要自己写一个java类,然后再类里面声明自己写的函数就可以了
防止有人看不懂,小例子
testJni.cpp
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
// 注意这个函数名的类名改成了JniUtils
Java_com_example_immortals_myapplication_JniUtils_stringFromMyJNI(
JNIEnv *env,
jobject /* this */)
{
std::string hello = "Hello from myJNI";
return env->NewStringUTF(hello.c_str());
}
在和MainActivity.java同级的目录下新建一个class叫JniUtils.java
内容
package com.example.immortals.myapplication;
public class JniUtils {
static {
System.loadLibrary("testJni");
}
// 注意 函数报红是没问题的
public native String stringFromMyJNI();
}
主函数的修改
照例,红框部分是修改了的
然后,编译,跑。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。