当前位置: 首页 > news >正文

做淘宝还是做网站百度推广电话号码

做淘宝还是做网站,百度推广电话号码,wordpress qq登录评论,郑州网站排名外包JNI基础知识 JNI简介NDK配置开发环境JNI实践配置CMakeJNI编码JNI注册1.静态注册2.动态注册 编译方式CMakeLists编译Makefile编译命令编译 JNI和C/C代码分离Java调用C/C查看so中包含的方法 C/C调用Java打印C/C的log生成多个共享库soJNI调试 本文整理了JNI技术基础知识 JNI简介 …

JNI基础知识

  • JNI简介
  • NDK
  • 配置开发环境
  • JNI实践
    • 配置CMake
    • JNI编码
    • JNI注册
      • 1.静态注册
      • 2.动态注册
    • 编译方式
      • CMakeLists编译
      • Makefile编译
      • 命令编译
  • JNI和C/C++代码分离
  • Java调用C/C++
      • 查看so中包含的方法
  • C/C++调用Java
  • 打印C/C++的log
  • 生成多个共享库so
  • JNI调试

本文整理了JNI技术基础知识

JNI简介

JNI 是java原生接口(Java Native Interface),它定义了 Android 从受管理代码(使用 Java 或 Kotlin 编程语言编写)编译的字节码与原生代码(使用 C/C++ 编写)进行交互的方法,也就是安卓通过JNI技术提供Java调用C/C++或者C/C++调用Java的能力。JNI 不依赖于供应商,支持从动态共享库加载代码,虽然有时较为繁琐,但效率较高。
在这里插入图片描述

NDK

Android NDK(Native Development Kit),原生开发工具包,它是一组能将C或C++(“原生代码”)嵌入到Android 应用中的工具。可以帮助开发者快速开发C/C+的动态库,自动将so和java应用一起打包成apk。
NDK集成了一些交叉编译器,并提供了相应的mk文件,用于隔离CPU、平台、ABI等差异,开发人员通过配置mk文件(指出“哪些文件需要编译”、“编译特性要求”等),就可以生成自己的so库。
原生共享库:NDK 从 C/C++ 源代码构建这些库或 .so 文件。so是shared object的缩写。
原生静态库:NDK 也可构建静态库或 .a 文件,而您可将静态库关联到其他库。

配置开发环境

  1. 下载NDK
    File>Settings>Android SDK>SDK Tools>勾选需要的版本号>apply>OK
  2. 配置项目NDK
    在这里插入图片描述
    如果NDK location无法编辑输入,可以在local.properties中新增ndk.dir进行设置:
    sdk.dir=D\:\\win10_program\\develop\\Android\\AndroidSDK
    ndk.dir=D\:\\win10_program\\develop\\Android\\AndroidSDK\\ndk\\26.1.10909125  // 已经过时的用法,可以直接删除此行配置,具体参考下面的操作。
    
    较新的项目,直接在app\build.gradle中直接配置ndk版本号即可。
    android {namespace 'com.xxx.xxx'compileSdk 33ndkVersion "25.2.9519653"...
    }
    
    命令查看ndk版本号:ndk-build --version

JNI实践

这里使用官方例子介绍。

配置CMake

配置CMake的目的是:告诉CMake改如何从源码编译生成目标库。

# 需要生成的目标库native-lib
# 也可以使用add_executable()生成可执行文件
add_library( # 指定要生成的目标库名称为native-libnative-lib# 将native-lib库设置默认为SHARED(原生共享库.so)STATIC(原生静态库.a)SHARED# 生成native-lib库所需源码的相对路径列表。包含.cpp和.hsrc/main/cpp/native-lib.cppNativeImpl.cpp)   # NativeImpl.cpp在后面代码分离部分实现# 指定源文件关联的头文件(适用于头文件和源文件分离的情况,但是也可以不写,因为.cpp中已经include了)           
include_directories(src/main/cpp/include/)# 在已有库中查找需要的库,并将它的路径存储在变量xxx-lib中。类似用法的函数
# find_file()find_path()find_program()find_package()
find_library( # 自定义变量的名称xxx-libxxx-lib#在ndk开发包中查找需要的libyyy.so,存储到xxx-lib变量中yyy ) # 将依赖的库文件链接到此目标库上
target_link_libraries(# 指定目标库native-lib# 将下面的库列表全部连接到目标库上${xxx-lib}  # 获取find_library找到的yyy库android     # 获取android库log)        # 获取log库

注意:
1、如果对库文件有修改变动,请务必在Gradle之前清理一下项目 Build > Clean Project在这里插入图片描述
2、如果需要生成多个共享库,可以在CMakeLists.txt文件中增加多个成对的add_librarytarget_link_libraries函数。

JNI编码

  1. 在Java侧声明调用方法。如stringFromJNI
    // 应用启动时,调用此函数会加载原生共享文件sodemo.sostatic {System.loadLibrary("sodemo");  //官方推荐使用:ReLinker.loadLibrary}/*** 声明此方法在原生端(共享文件sodemo.so)中实现,它与该应用程序打包在一起。*/public native String stringFromJNI();
  1. 在C侧实现具体方法Java_com_wingtech_sodemo_JNIUtils_stringFromJNI
#include <jni.h>
#include <string>extern "C" JNIEXPORT jstring JNICALL
Java_com_wingtech_sodemo_JNIUtils_stringFromJNI(JNIEnv *env, jobject thiz) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}
方法名称:Java_包名_类名_方法名
方法参数:JNIEnv* 是指向虚拟机环境的指针。jobject 是指向从 Java 端传递的隐式 this 对象的指针。

重要:C/C++和Java通过此方法名称建立了一对一映射关系。

JNI注册

1.静态注册

如果只有一个类具有原生方法,建议使用静态注册。使用标准 System.loadLibrary 从共享库加载原生代码。
从静态类初始化程序中调用 System.loadLibrary(或 ReLinker.loadLibrary)。具体静态注册同前面JNI编码部分所述。

2.动态注册

如果有多个类有原生方法,可以使用RegisterNatives注册,也可以让运行时使用dlsym动态查找它们。可以从 Application进行调用,这样始终加载该库,而且总是会提前加载。当执行到System.loadLibrary()函数时,会回调JNI组件中的JNI_OnLoad()函数;当释放该组件时会回调JNI_OnUnload()函数。

JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void* reserved) {JNIEnv* env;// 通过调用了GetEnv函数获取JNIEnv结构体指针env(JNI环境变量),JNIEnv结构体是指向一个函数表的,// 该函数表又指向了一些列对应的JNI函数。所以可以通过env和java交互,如GetObjectClass,CallVoidMethod等if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {return JNI_ERR;}// Find your class. JNI_OnLoad is called from the correct class loader context for this to work.jclass c = env->FindClass("com/example/app/package/MyClass");if (c == nullptr) return JNI_ERR;// 将所有方法装进数组中。这里数组中每个元素是结构体JNINativeMethod。// typedef struct {// const char* name;//Java层native方法的名字// const char* signature;//Java层native方法的描述符// void*       fnPtr;//对应JNI函数的指针// } JNINativeMethod;static const JNINativeMethod methods[] = {{"nativeFoo", "()V", reinterpret_cast<void*>(nativeFoo)},{"nativeBar", "(Ljava/lang/String;I)Z", reinterpret_cast<void*>(nativeBar)},};// 使用RegisterNatives注册所有原生方法int rc = env->RegisterNatives(c, methods, sizeof(methods)/sizeof(JNINativeMethod));if (rc != JNI_OK) return rc;return JNI_VERSION_1_6;
}

jni函数的指针
void regist(JNIEnv *env, jobject thiz, jobject jCallback) {
LOGD(“–动态注册调用成功–>”);
jstring pJstring = env->NewStringUTF(“动态注册调用成功”);
jclass pJclass = env->GetObjectClass(thiz);
jmethodID id = env->GetMethodID(pJclass, “beInjectedDebug”, “(Ljava/lang/String;)V”);
//执行函数
env->CallVoidMethod(thiz,id,pJstring);
}

编译方式

一般有两种编译方式:

  • 1、CMakeLists编译
  • 2、Makefile编译
  • 3、命令编译

CMakeLists编译

1、CMakeLists配置
具体配置如前面配置CMake的介绍,这里使用cpp目录下的CMakeLists.txt、native-lib.cpp文件生成.so库。
在这里插入图片描述
2、gradle配置
在app\build.gradle中设置库文件适配的CPU架构类型和CMakeLists.txt 文件路径。

android {namespace 'com.xxx.sodemo'compileSdk 33defaultConfig {applicationId "com.xxx.sodemo"minSdk 31targetSdk 33versionCode 1versionName "1.0"testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"ndk {// 生成so库类型abiFilters 'armeabi-v7a', 'arm64-v8a','x86','x86_64'}}externalNativeBuild {cmake {// 设置CMakeLists.txt文件路径path file('src/main/cpp/CMakeLists.txt')version '3.22.1'}}...}

3、编译库文件
点击Make按钮或Build->Make Project,运行结束后,会在 根目录/app/build/intermediates/cmake/debug/obj 路径下生成对应平台的.so库文件。

Makefile编译

使用MK文件编译,不需要编辑CMakeLists.txt,也不需要在build.gradle中配置,只要在Android.mk和Application.mk文件中配置好即可。一般在C/C++同目录下创建mk文件。
1、编写Android.mk文件

#设置当前编译路径为当前文件夹路径
LOCAL_PATH :=$(call my-dir)#清空编译环境的变量(由其他模块设置过的变量)
include $(CLEAR_VARS)
LOCAL_LDLIBS := -lm -llog#指定生成模块的名称(库引用名称),编译时会自动添加lib前缀
LOCAL_MODULE :=JNITest123#需要编译的源文件。如果存在多个.cpp文件时使用"\"隔开
LOCAL_SRC_FILES :=native-lib.cpp \NativeImpl.cpp  # NativeImpl.cpp在后面代码分离部分实现#生成动态库
include $(BUILD_SHARED_LIBRARY)

2、编写Application.mk文件

#模块名字,与Android.mk中保持一致
APP_MODULES := JNITest123#支持平台,这里支持所有平台
APP_ABI := all
APP_ALLOW_MISSING_DEPS=true

3、编译库文件
方式一:
1)检查编译环境
打开cmd窗口,运行ndk-build --version,如下输出,说明ndk配置正确。
在这里插入图片描述
2)在cmd中进入C/C++文件所在目录下,执行ndk-build命令编译。

NDK_PROJECT_PATH=.  # 当前项目
APP_PLATFORM=android-16 # 有默认值,可以不设置
APP_BUILD_SCRIPT=./Android.mk  # 当前目录下的Android.mk文件。注意:这里根据实际情况修改路径
NDK_APPLICATION_MK=./Application.mk  # 当前目录下的Application.mk文件。注意:这里根据实际情况修改路径
NDK_LOG=1   # 打印编译日志

整理成一行命令执行:

ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./Android.mk NDK_APPLICATION_MK=./Application.mk NDK_LOG=1

3)执行结果:
在这里插入图片描述
说明:在哪个目录下执行ndk-build命令编译,就在此目录下生成库文件。

E:\work\Test\Andriod\SoDemo\app\src\main>ndk-build NDK_PROJECT_PATH=. APP_BUILD_SCRIPT=./cpp/Android.mk NDK_APPLICATION_MK=./cpp/Application.mk APP_PLATFORM=android-16
Android NDK: WARNING: APP_PLATFORM android-16 is higher than android:minSdkVersion 1 in ./AndroidManifest.xml. NDK binaries will *not* be compatible with devices older than android-16. See https://android.googlesource.com/platform/ndk/+/master/docs/user/common_problems.md for more information.
[arm64-v8a] Compile++      : JNITest123 <= native-lib.cpp
[arm64-v8a] SharedLibrary  : libJNITest123.so
[arm64-v8a] Install        : libJNITest123.so => libs/arm64-v8a/libJNITest123.so
[x86_64] Compile++      : JNITest123 <= native-lib.cpp
[x86_64] SharedLibrary  : libJNITest123.so
[x86_64] Install        : libJNITest123.so => libs/x86_64/libJNITest123.so
[armeabi-v7a] Compile++ thumb: JNITest123 <= native-lib.cpp
[armeabi-v7a] SharedLibrary  : libJNITest123.so
[armeabi-v7a] Install        : libJNITest123.so => libs/armeabi-v7a/libJNITest123.so
[x86] Compile++      : JNITest123 <= native-lib.cpp
[x86] SharedLibrary  : libJNITest123.so
[x86] Install        : libJNITest123.so => libs/x86/libJNITest123.so

方式二:在Android Studio中,打开终端Terminal,cd进入C/C++文件所在目录的父目录下,执行ndk-build.cmd即可。只要代码没有问题,一般可以在同级目录下生成文件。

PS E:\work\Test\Andriod\SoDemo\app\src\main> D:\win10_program\develop\Android\AndroidSDK\ndk\26.1.10909125\ndk-build.cmd
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-21.
[arm64-v8a] SharedLibrary  : libJNITest123.so
[arm64-v8a] Install        : libJNITest123.so => libs/arm64-v8a/libJNITest123.so
[x86_64] Compile++      : JNITest123 <= native-lib.cpp
[x86_64] SharedLibrary  : libJNITest123.so
[x86_64] Install        : libJNITest123.so => libs/x86_64/libJNITest123.so
[armeabi-v7a] Compile++ thumb: JNITest123 <= native-lib.cpp
[armeabi-v7a] SharedLibrary  : libJNITest123.so
[armeabi-v7a] Install        : libJNITest123.so => libs/armeabi-v7a/libJNITest123.so
[x86] Compile++      : JNITest123 <= native-lib.cpp
[x86] SharedLibrary  : libJNITest123.so
[x86] Install        : libJNITest123.so => libs/x86/libJNITest123.so

常见问题:

E:\work\Test\Andriod\SoDemo\app\src\main> D:\win10_program\develop\Android\AndroidSDK\ndk\21.0.6113669\ndk-build.cmd
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-16.
Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: 
D:\win10_program\develop\Android\AndroidSDK\ndk\21.0.6113669/jni/Android.mk  // 这里是路径问题jni
D:/win10_program/develop/Android/AndroidSDK/ndk/21.0.6113669/build//../build/core/add-application.mk:88: *** Android NDK: Aborting...    .  Stop.

因为这里的mk文件实际在src\main\cpp中,而NDK编译环境默认在jni目录下找mk文件,所以报错无法找到。这里可以将文件名称cpp修改为默认路径jni,也可以在ndk-build命令里指定mk的路径,具体修改如下。

ndk-build.cmd APP_BUILD_SCRIPT=./cpp/Android.mk NDK_APPLICATION_MK=./cpp/Application.mk
PS E:\work\Test\Andriod\SoDemo\app\src\main>  D:\win10_program\develop\Android\AndroidSDK\ndk\26.1.10909125\ndk-build.cmd APP_BUILD_SCRIPT=./cpp/Android.mk NDK_APPLICATION_MK=./cpp/Application.mk
Android NDK: APP_PLATFORM not set. Defaulting to minimum supported version android-21.
[arm64-v8a] Compile++      : JNITest123 <= native-lib.cpp
[arm64-v8a] SharedLibrary  : libJNITest123.so
...

命令编译

JNI和C/C++代码分离

分离设计目的是希望在JNI文件中出现少量的C++代码。
1、编写.cpp文件
这里以NativeImpl.cpp为例

#include <jni.h>
#include "NativeImpl.h"NativeImpl::NativeImpl() {}NativeImpl::~NativeImpl() {}int NativeImpl::Clear_Zero() {LOGD("打印C++ LOGD");LOGE("打印C++ LOGE");LOGI("打印C++ LOGI");LOGW("打印C++ LOGW");return 0;
}

2、编写.h文件
这里以NativeImpl.h为例

#ifndef SODEMO_NATIVEIMPL_H
#define SODEMO_NATIVEIMPL_H
#include "LOG.h"#include <vector>
class NativeImpl {public:NativeImpl();virtual ~NativeImpl();virtual int Clear_Zero();
};#endif //SODEMO_NATIVEIMPL_H

3、在JNI文件中调用C++方法。

#include <jni.h>
#include "NativeImpl.h"NativeImpl nativeImpl;
NativeImpl*  getNativeImpl(){return &nativeImpl;
}extern "C"
JNIEXPORT jint JNICALL
Java_com_mytest_sodemo_JNIUtils_clrNumber(JNIEnv *env, jclass clazz) {// TODO: implement clrNumber()int zero = getNativeImpl()->Clear_Zero();return zero;
}

注意:代码分离后,需要将纯C++的源码添加到编译环境中,也就是在CMakeLists.txt的add_library方法中添加NativeImpl.cpp,或者在Android.mk的LOCAL_SRC_FILES中添加NativeImpl.cpp。具体参考上面的编译方式。

Java调用C/C++

Java调用C或C++程序,前提是给定了C或C++的动态库dll(Windows)或so(Linux)文件和函数头文件说明,这里介绍如何正确调用第三方so。
Java层调用C++函数主要通过建立的映射关系,这里jni函数调用java层的函数就要通过JNIEnv。
1、将第三方提供的so文件全部放进app\libs目录下,然后在app\build.gradle的sourceSets中配置libs,这样就会在打包时,自动把libs下的文件副本迁移到apk的lib目录下。
当然,这里的路径可以自定义,只要Gradle在打包时能通过你配置的路径,找到so的存放位置即可。

android {defaultConfig {...}sourceSets {main {jniLibs.srcDirs = ['libs']          // 打包时会把app\libs下的共享库.so的副本迁移到apk的lib目录下。// jniLibs.srcDirs = ['libs/test']  // 也可以在app\libs下新建各个公司或模块提供的库目录。// jniLibs.srcDirs = ['src/mylibs'] // 也可以自定义路径so的存储路径,只要能找到就行。}}...
}

在这里插入图片描述
2、根据已知的Java_xx包名_yy类名_方法名格式(也可以通过nm命令获取so库的方法),在自己的项目中app\src\main\java目录下新建xx包名,然后再创建一个和so中的yy类名相同的类,这里要确保包名、类名、方法名、库名(不带lib前缀)四者一致,最后在自己的项目中直接调用yy类中的方法即可。

extern "C" JNIEXPORT jstring JNICALL
Java_com_mytest_sodemo_JNIUtils_stringFromJNI(JNIEnv *env, jobject) {std::string hello = "Hello from C++";return env->NewStringUTF(hello.c_str());
}

注意:这一步非常重要,通过如此设计的方法名,建立了java和C之间的映射关系,所以在其他应用中使用时,也需要建立这种映射关系,否则报错UnsatisfiedLinkError。
//todo 缺图
3、清理和检查
清理后重新打包:Build>Clean Project>等一会儿>Make Project或者Build APK(s)>等一会儿。
检查so是否打入包内:Build>Analyze APK>OK>打开目标APK的lib目录。
4、常见异常
如果检查没有发现错误,编译运行后还是出现UnsatisfiedLinkError异常,多半是因为apk中的so没有打入包内,请按照第3步处理.

查看so中包含的方法

需要使用nm工具,一般在sdk\ndk\xx版本\toolchains\x86-4.9\prebuilt\windows-x86_64\i686-linux-android\bin

nm -D "so文件路径" 

C/C++调用Java

打印C/C++的log

在CPP目录下新建head文件LOG.h

#ifndef SODEMO_LOG_H
#define SODEMO_LOG_H#include <android/log.h>#define TAG "haitao"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__);
#define LOGW(...) __android_log_print(ANDROID_LOG_WARN, TAG, __VA_ARGS__);
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__);
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__);#endif //SODEMO_LOG_H

在需要使用的文件中#include "LOG.h"即可。

生成多个共享库so

如果需要生成多个共享库,可以在CMakeLists.txt再增加add_librarytarget_link_libraries
参考配置CMake

# 生成libtest-1.so
add_library(test-1SHAREDnative-lib.cpp)
target_link_libraries(test-2 android)
...# 生成libtest-2.so
add_library(test-2 SHAREDnative-lib.cpp)
target_link_libraries(test-2android)

JNI调试

在这里插入图片描述
在Debug模式下,有时候会出现这个Permission denied的提示。
解决方法:退出App重新debug运行。如果退出无法解决此问题,重新USB连接即可。

与君共勉人生自当扶摇上,揽星衔月逐日光。你只管去劈浪,与众生争锋芒,你举步是八万里宽广,你眼望是千江拍白浪,你平生这一趟,要让旁人想都不敢想!

http://www.khdw.cn/news/12018.html

相关文章:

  • 做网站app公司前景北京网站优化对策
  • 网站架构制作有实力的网站排名优化软件
  • 济南市建设局网站查房产信息一份完整的营销策划书
  • 网站设计是什么专业seo搜索优化排名
  • 网站建设尺寸惠州网络推广平台
  • 网站实施过程网络营销个人总结
  • 网站建设遇到哪些问题今天新疆新闻头条
  • 网站开发技术合同如何做好seo基础优化
  • 郑州做网站建设的公司重庆百度关键词优化软件
  • 印度新冠肺炎疫情最新消息seo百度快速排名
  • 做网站是不是要学编程网站建设与网站设计
  • 网站镜像 cdnqq群排名优化
  • 西宁最好的建站公司权重查询站长工具
  • 如何重新打开wordpress重庆白云seo整站优化
  • 做推广网站的文章术语拼多多搜索关键词排名
  • 做百度网站需不需要备案ds2600ii色带
  • wordpress 企业模板 免费seo百度首页排名业务
  • 视频号分销解决方案的特点武汉seo工厂
  • 福州市连江县建设局网站seo百度关键词优化软件
  • 网络电子商务购物网站百度搜索引擎营销
  • 做网站的软件dw烟台网站建设
  • 杭州 做网站seo舆情优化
  • 济南城市建设集团有限公司网站拉新充场app推广平台
  • 西安市商标局seo排名分析
  • 数字广东网络建设有限公司地址关键词优化包年推广
  • flash网站模板修改北京互联网公司有哪些
  • 模板网站建设哪家好怎么做市场营销和推广
  • 网站开发前后端分离要多少钱中国关键词网站
  • 网站怎么做json数据seo排名怎么优化软件
  • 成都建网站比较好的公司6今天今日头条新闻