0x00 本文目标
让 Java 层代码与 Native 层代码交互
编译 Native 代码为 SO 文件
将 SO 文件集成到最终的 APK 文件中
为此你需要 Android Studio 和 NDK 套装, 百度搜索后直接到官网下载.
0x01 Java 层
为了让 Java 层与 JNI 层交互, 来个简单的测试代码. hello 方法用于获取从 native 返回的字符串, 并显示到 TextView 中, 没有 TextView 的同学自己在布局文件中新建一个就行了.
- package com.example.androidtest;
- import Android.support.v7.App.AppCompatActivity;
- import Android.os.Bundle;
- import Android.widget.TextView;
- public class MainActivity extends AppCompatActivity {
- private TextView txt_jni;
- static {
- System.loadLibrary("test");
- }
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- setContentView(R.layout.activity_main);
- txt_jni = findViewById(R.id.txt_jni);
- txt_jni.setText(hello(1, "2"));
- }
- private native String hello(int a, String b);
- }
接下来实现 JNI 层代码, 在 Android Studio 中新建一个空白工程, File->New->Folder->JNI Folder 创建一个 JNI 源码文件夹, 默认路径在 / src/main/jni.
博主另一篇文章演示了纯 Java 生成 SO 文件: https://www.cnblogs.com/DXCyber409/p/10854415.html .
在安卓中同样有这两种方式: Java_固定路径方式, 和 JNI_OnLoad 动态注册方式.
0x02 JNI 固定前缀写法
本质就是生成一个固定前缀的函数名称和相关参数, 最后在 c/cpp 文件中实现它.
可以手动生成. h 文件, 不再演示. 本文使用 Android Studio 提供的 External Tools 来实现一键生成. h 头文件, 这将大大提高开发效率.
File->Settings->Tools->Extenal Tools, 点个加号创建一个新的项. 该命令实际上会执行命令行指令传递相关参数, 结合手写的写法:
D:\RTEws\Java\jdk1.8.0_121\bin>javah -d "E:\Workspace\NetBeans\DXCyber409\src\main\java\dxcyber409\jni" -classpath "E:\Workspace\NetBeans\DXCyber409\target\classes" -jni dxcyber409.Test$Cls
Insert Marcros 是内置环境变量参考窗口, 不要错过:
最终路径参数方案调整如下:
- Name:javah
- Program:$JDKPath$\bin\javah.exe
- Arguments:-d "$ModuleFileDir$\src\main\jni" -classpath "$OutputPath$;$ModuleSdkPath$\platforms\android-28\android.jar;$ModuleSdkPath$\extras\android\android-support-v4.jar;$ModuleSdkPath$\extras\android\android-support-v7.jar" -jni $FileClass$
- Working directory:
在 Arguments 中 - classpath 选项除了自己代码中已编译的 classes 目录外, 还加入 Android.jar,Android-support-v4.jar,Android-support-v7.jar 等依赖包, 不加会缺少实现类而报错.
写法不止一种, 博主在 Arguments 中已经把所有相关路径都拼接完成了, 因此 Working directory 没啥用留空就行.
Ps. 博主的 Android SDK 貌似不完整的样子, 在 Android SDK 中找不到 appcompat 相关依赖包. 如果你也不想重新安装 Android SDK 的话这里有个小技巧, 到 Maven Repository 上缺啥下啥, 自己动手丰衣足食. 下载了丢到目录中引用路径就行, 位置没有强硬要求.
随后在 / src/main/jni 目录中就得到了. h 头文件, 按惯例新建一个 cpp 文件包含此文件照着写就行, 完整代码如下:
- /* DO NOT EDIT THIS FILE - it is machine generated */
- #include <jni.h>
- /* Header for class com_example_androidtest_MainActivity */
- #ifndef _Included_com_example_androidtest_MainActivity
- #define _Included_com_example_androidtest_MainActivity
- #ifdef __cplusplus
- extern "C" {
- #endif
- #undef com_example_androidtest_MainActivity_BIND_ABOVE_CLIENT
- #define com_example_androidtest_MainActivity_BIND_ABOVE_CLIENT 8L
- #undef com_example_androidtest_MainActivity_BIND_ADJUST_WITH_ACTIVITY
- #define com_example_androidtest_MainActivity_BIND_ADJUST_WITH_ACTIVITY 128L
- #undef com_example_androidtest_MainActivity_BIND_ALLOW_OOM_MANAGEMENT
- #define com_example_androidtest_MainActivity_BIND_ALLOW_OOM_MANAGEMENT 16L
- #undef com_example_androidtest_MainActivity_BIND_AUTO_CREATE
- #define com_example_androidtest_MainActivity_BIND_AUTO_CREATE 1L
- #undef com_example_androidtest_MainActivity_BIND_DEBUG_UNBIND
- #define com_example_androidtest_MainActivity_BIND_DEBUG_UNBIND 2L
- #undef com_example_androidtest_MainActivity_BIND_EXTERNAL_SERVICE
- #define com_example_androidtest_MainActivity_BIND_EXTERNAL_SERVICE -2147483648L
- #undef com_example_androidtest_MainActivity_BIND_IMPORTANT
- #define com_example_androidtest_MainActivity_BIND_IMPORTANT 64L
- #undef com_example_androidtest_MainActivity_BIND_NOT_FOREGROUND
- #define com_example_androidtest_MainActivity_BIND_NOT_FOREGROUND 4L
- #undef com_example_androidtest_MainActivity_BIND_WAIVE_PRIORITY
- #define com_example_androidtest_MainActivity_BIND_WAIVE_PRIORITY 32L
- #undef com_example_androidtest_MainActivity_CONTEXT_IGNORE_SECURITY
- #define com_example_androidtest_MainActivity_CONTEXT_IGNORE_SECURITY 2L
- #undef com_example_androidtest_MainActivity_CONTEXT_INCLUDE_CODE
- #define com_example_androidtest_MainActivity_CONTEXT_INCLUDE_CODE 1L
- #undef com_example_androidtest_MainActivity_CONTEXT_RESTRICTED
- #define com_example_androidtest_MainActivity_CONTEXT_RESTRICTED 4L
- #undef com_example_androidtest_MainActivity_MODE_APPEND
- #define com_example_androidtest_MainActivity_MODE_APPEND 32768L
- #undef com_example_androidtest_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING
- #define com_example_androidtest_MainActivity_MODE_ENABLE_WRITE_AHEAD_LOGGING 8L
- #undef com_example_androidtest_MainActivity_MODE_MULTI_PROCESS
- #define com_example_androidtest_MainActivity_MODE_MULTI_PROCESS 4L
- #undef com_example_androidtest_MainActivity_MODE_NO_LOCALIZED_COLLATORS
- #define com_example_androidtest_MainActivity_MODE_NO_LOCALIZED_COLLATORS 16L
- #undef com_example_androidtest_MainActivity_MODE_PRIVATE
- #define com_example_androidtest_MainActivity_MODE_PRIVATE 0L
- #undef com_example_androidtest_MainActivity_MODE_WORLD_READABLE
- #define com_example_androidtest_MainActivity_MODE_WORLD_READABLE 1L
- #undef com_example_androidtest_MainActivity_MODE_WORLD_WRITEABLE
- #define com_example_androidtest_MainActivity_MODE_WORLD_WRITEABLE 2L
- #undef com_example_androidtest_MainActivity_RECEIVER_VISIBLE_TO_INSTANT_APPS
- #define com_example_androidtest_MainActivity_RECEIVER_VISIBLE_TO_INSTANT_APPS 1L
- #undef com_example_androidtest_MainActivity_DEFAULT_KEYS_DIALER
- #define com_example_androidtest_MainActivity_DEFAULT_KEYS_DIALER 1L
- #undef com_example_androidtest_MainActivity_DEFAULT_KEYS_DISABLE
- #define com_example_androidtest_MainActivity_DEFAULT_KEYS_DISABLE 0L
- #undef com_example_androidtest_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL
- #define com_example_androidtest_MainActivity_DEFAULT_KEYS_SEARCH_GLOBAL 4L
- #undef com_example_androidtest_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL
- #define com_example_androidtest_MainActivity_DEFAULT_KEYS_SEARCH_LOCAL 3L
- #undef com_example_androidtest_MainActivity_DEFAULT_KEYS_SHORTCUT
- #define com_example_androidtest_MainActivity_DEFAULT_KEYS_SHORTCUT 2L
- #undef com_example_androidtest_MainActivity_RESULT_CANCELED
- #define com_example_androidtest_MainActivity_RESULT_CANCELED 0L
- #undef com_example_androidtest_MainActivity_RESULT_FIRST_USER
- #define com_example_androidtest_MainActivity_RESULT_FIRST_USER 1L
- #undef com_example_androidtest_MainActivity_RESULT_OK
- #define com_example_androidtest_MainActivity_RESULT_OK -1L
- #undef com_example_androidtest_MainActivity_HONEYCOMB
- #define com_example_androidtest_MainActivity_HONEYCOMB 11L
- #undef com_example_androidtest_MainActivity_MSG_REALLY_STOPPED
- #define com_example_androidtest_MainActivity_MSG_REALLY_STOPPED 1L
- #undef com_example_androidtest_MainActivity_MSG_RESUME_PENDING
- #define com_example_androidtest_MainActivity_MSG_RESUME_PENDING 2L
- /*
- * Class: com_example_androidtest_MainActivity
- * Method: hello
- * Signature: (ILjava/lang/String;)Ljava/lang/String;
- */
- JNIEXPORT jstring JNICALL Java_com_example_androidtest_MainActivity_hello
- (JNIEnv *, jobject, jint, jstring);
- #ifdef __cplusplus
- }
- #endif
- #endif
0x03 JNI 动态注册写法
本质是向 RegisterNatives
0x04 编译 SO 文件
0x05 集成 SO 文件
0x06 测试结果
NDK 官方指定使用 Makefile 来进行 SO 文件编译, 那么新建一个文件重命名 Android.mk.
目前最重要的是编译出一个 SO 成品来, 一切配置最简单化. 后续有详细需要各个编译选项的可以参考官方文档:.
参考源
https://www.jianshu.com/p/faa3eebbd401
来源: http://www.bubuko.com/infodetail-3086928.html