博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android Studio编译并集成SO文件
阅读量:5887 次
发布时间:2019-06-19

本文共 10114 字,大约阅读时间需要 33 分钟。

0x00 本文目标

  • 让Java层代码与Native层代码交互
  • 编译Native代码为SO文件
  • 将SO文件集成到最终的APK文件中

为此你需要Android Studio和NDK套装,百度搜索后直接到官网下载。

0x01 Java层

为了让Java层与JNI层交互,来个简单的测试代码。首先用静态代码块加载了test动态链接库。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(123, "abc"));    }    private native String hello(int a, String b);}

接下来实现JNI层代码,在Android Studio中新建一个空白工程,File->New->Folder->JNI Folder创建一个JNI源码文件夹,默认路径在/src/main/jni。

博主另一篇文章演示了纯Java生成SO文件:。

在安卓中同样有这两种方式:Java_固定路径方式,和JNI_OnLoad动态注册方式。

0x02 Java_固定路径写法

本质就是生成一个固定前缀的函数名称和相关参数,最后在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:javahProgram:$JDKPath$\bin\javah.exeArguments:-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上缺啥下啥,自己动手丰衣足食。下载了丢到目录中引用路径,位置没有强硬要求。

添加External Tools完成后首先Build->Make module生成一下classes文件,之后对着类右键->External Tools->javah就可以直接调用javah生成.h文件,相当方便。

.h文件在/src/main/jni目录中,按惯例新建一个cpp文件包含此文件并实现,完整代码如下:

/* DO NOT EDIT THIS FILE - it is machine generated */#include 
/* Header for class com_example_androidtest_MainActivity */#ifndef _Included_com_example_androidtest_MainActivity#define _Included_com_example_androidtest_MainActivity#ifdef __cplusplusextern "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

博主在编写C层的时候Android Studio没有出现代码自动补全,解决办法File > Invalidate Caches / Restart... > Click at Invalidate and Restart

/** * The implemention of com_example_androidtest_MainActivity.h * Created by DXCyber409 on 2019/6/9. */#include 
#include
#include "com_example_androidtest_MainActivity.h"JNIEXPORT jstring JNICALL Java_com_example_androidtest_MainActivity_hello(JNIEnv *env, jobject obj, jint p1, jstring p2){ uint8_t success; char tmp[256] = {
0}; // 将int参数转换为字符串 sprintf(tmp, "%d", p1); // 将jstring转换为const char* const char* str_utf8 = env->GetStringUTFChars(p2, &success); if (success) { strcat(tmp, str_utf8); env->ReleaseStringUTFChars(p2, str_utf8); } // 将char*转换为jstring return env->NewStringUTF(tmp);}

Java_固定前缀写法这部分就全部实现完了,编译等步骤看后续的章节。

0x03 JNI动态注册写法

JNI_OnLoad是安卓SO文件执行时,在较早时机会调用的一个初始化函数。此时通过JNIEnv环境指针调用RegisterNatives函数绑定Java层方法和native层的函数,可以在native函数被执行之前进行提前注册,避免报错的同时也省去了Java_固定路径的长串写法。

那么就不需要javah,也不用配置External Tools命令行了,直接新建一个cpp文件开搞。

/** * Main module of libtest.so * Created by DXCyber409 on 2019/6/9. */#include 
#include
#include
JNIEXPORT jstring JNICALL func_test(JNIEnv *env, jobject obj, jint p1, jstring p2){ uint8_t success; char tmp[256] = {
0}; // 将int参数转换为字符串 sprintf(tmp, "%d", p1); // 将jstring转换为const char* const char* str_utf8 = env->GetStringUTFChars(p2, &success); if (success) { strcat(tmp, str_utf8); env->ReleaseStringUTFChars(p2, str_utf8); } // 将char*转换为jstring return env->NewStringUTF(tmp);}JNINativeMethod gMethods[] = { {
"hello", "(ILjava/lang/String;)Ljava/lang/String;", (void*)func_test},};static const char* const className = "com/example/androidtest/MainActivity";JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reversed){ jclass myClass; JNIEnv* env = NULL; jint result = -1; // 从JavaVM中获取JNIEnv if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) { printf("get env error."); return -1; } // 获取映射的java类 myClass = env->FindClass(className); if (myClass == NULL) { printf("cannot get class:%s\n", className); return -1; } // 通过RegisterNatives方法动态注册 if (env->RegisterNatives(myClass, gMethods, sizeof(gMethods) / sizeof(gMethods[0]))) { printf("cannot get method:%s\n", gMethods[0].name); return -1; } return JNI_VERSION_1_4;}

JNI的函数功能以及写法不是本篇文章的讲解目标,这里不做过多说明。

0x04 SO文件编译

有了源码就需要编译,此时ndk-build指令就派上用场。期间需要一个Makefile文件Android.mk,该文件指示了完成编译所必需的特性。

LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_MODULE    := testLOCAL_SRC_FILES := com_example_androidtest_MainActivity.cppinclude $(BUILD_SHARED_LIBRARY)

在上面的Android.mk中博主指定了编译模块的名称为test,所使用的源码为com_example_androidtest_MainActivity.cpp,编译为共享库(SO)等。这一步Java_固定路径写法和JNI动态注册写法都通用。

具体含义可以参考:。

在命令行切换到jni目录ndk-build,默认会生成所有架构的so文件。在jni/../libs文件有编译好的成品。

0x04 集成SO文件

为了在APK文件中的lib文件夹出现SO文件,还需要最后一步SO文件集成步骤。

博主使用的是Android Studio 3.4.1,这个版本会在执行ndk-build成功后直接将jni/../libs目录下的所有SO集成到APK中,做其他配置反而会报错。

于是博主就没事做了。如果你的版本不是这个,不会自动集成SO文件的话,有两个集成方案,具体参考:。

0x05 编译APK测试结果

Build->Build Bundle/APK->Build APK

生成APK文件后直接安装到手机上测试吧,结果良好。

 

参考源

转载于:https://www.cnblogs.com/DXCyber409/p/10992309.html

你可能感兴趣的文章
6.2实现用户登录逻辑
查看>>
JavaScript 正整数正则表达式
查看>>
单元测试之Stub和Mock
查看>>
solr
查看>>
IOS7 viewDidLoad中调用 pushViewController 的问题
查看>>
oracle merge into 用法详解
查看>>
tf.concat&tf.gather&tf.gather_nd&tf.greater&tf.cast&tf.expand_dims&tf.squeeze
查看>>
VBA基础之Excel 工作表(Sheet)的操作(二)
查看>>
js 日期转换 strToDate
查看>>
空间索引格网大小无效
查看>>
C_数据结构_数组的修改和删除
查看>>
软件测试5gkd
查看>>
伪类与伪元素
查看>>
11.static关键字
查看>>
iOS @try
查看>>
数据结构之栈——二进制转十进制
查看>>
关于Objective-C和C++中的继承及其区别
查看>>
$().bind()的返回值
查看>>
16 个常用的yum 命令
查看>>
JDBC基础语句使用
查看>>