西班牙三棵树 发表于 2021-5-27 09:58

Android Studio 3.6 SO文件生成(CMake)

希望记录一下在学习过程中遇到的最新的问题,过于浅薄,希望各位大佬尽量多的提出问题熬。

- 安装NDK:

现有的NDK最新的版本是NDK22.1,但是NDK22.1并不能成功地兼容。(具体原因我忘了,大概一个月前的时候,有大佬知道可以提点我一下)于是安装的是NDK21,但是下面创建项目时,会提示最优的版本是NDK20,但是好在NDK21没有出现大的兼容问题,没有安装的伙伴可以在创建项目后首次进行Sync(小象图标)根据那个NDK缺失情况进行安装。



在Android Studio 2.2 之后,工具中增加了 CMake 的支持,所以在 Android Studio 2.2 之后有2种选择来编译 c/c++ 代码。

- 一种是 ndk-build + Android.mk + Application.mk 的方式

- 另一种是 CMake + CMakeLists.txt 的方式

实现NDK的完整步骤如下:

1. 创建Android ndk project,使用`Native`关键字定义`Java`方法(即需要调用的native方法)
2. 使用`javac`编译上述 `Java`源文件 (即 `.java文件`)最终得到 `.class`文件
3. 通过 `javah` 命令编译`.class`文件,最终导出`JNI`的头文件(`.h`文件)
4. 用`C++`实现在 `Java`中声明的`Native`方法
5. `Activity`加载`so库`,使用`native`方法
6. 生成`.so`库文件
7. 执行代码

我初步是跟随大佬帖子里,使用ndk-build进行so库生成,主要有几个问题:

- javah的指令已经过期,已经嵌入到了javac -h里面
- 在全部配置完后,rebuild Project并不能生成对应so库
- 在搜寻过一堆ndk-build的so库解决方案并且对应改完之后,我生成了so库。但是对应的apk会闪退。。。

接下来我会继续研究关于ndk-build生成so文件的方案,以及问题的解决方案

今天要介绍的是通过Cmake方式进行So文件生成。

如果是新的工程,可以在新建工程时选择,进去直接就能找到对应的jni库和c++写好的helloworld函数,进行修改,或者再加入新的代码就可以。



如果是在原有工程下进行so库的添加操作,可以按照以下的方式进行:

(其实这种方式可以先生成一个空的Native C++项目看一下文件夹结构和内容,新添加多数情况下可以直接拷贝模板过来,但是用C++和C写的so文件的语法和格式也是有一些出入的)

- 先在src->main下构建cpp文件夹,以及文件夹下的main.c, CmakeLists.txt,空置




- apk文件夹右键:link c++ project with gradle,路径选择刚刚新建的那个CmakeList.txt。
-



- 添加如下代码至Cmake文件

```
cmake_minimum_required(VERSION 3.4.1)

add_library(
      # Specifies the name of the library.
      native-lib

      # Sets the library as a shared library.
      SHARED

      # Provides a relative path to your source file(s).
      main.c)

```

- 点击右上角小象:


- 然后就可以书写main.c的代码了

```c
#include <jni.h>

JNIEXPORT jstring JNICALL Java_com_example_myapplication_MainActivity_stringFromJNI(JNIEnv *env, jobject thiz) {
    return (*env)->NewStringUTF(env, "hello from c++ language");
}

JNIEXPORT jint JNICALL Java_com_example_myapplication_MainActivity_sum(JNIEnv *env, jobject thiz, jint num1, jint num2){
    return num1+num2;
}
```

在MainActivity中进行native-lib的调用就可以完成了

```java
package com.example.myapplication;

import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;

public class MainActivity extends AppCompatActivity {

    static {
      System.loadLibrary("native-lib");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);

      Log.i("from native-lib", stringFromJNI());
      Log.i("from native-lib", "sum result: " + sum(10, 15));

    }
    public native String stringFromJNI();
    public native int sum(int num1, int num2);


}
```

本文的主要来源:

https://www.52pojie.cn/thread-706568-1-1.html

https://blog.csdn.net/qycc3391/article/details/114241225



下面是我在研究ndk-build的时候成功生成so文件但是闪退的过程,希望有大佬帮我看看哪里出了什么问题:


- 配置NDK:

File->Project Structure


- 加环境变量,确保ndk-build可以运行


- 在对应的要编译为so文件的java文件夹下使用javah,将java文件生成为.h,即c语言头文件

注意新版本的指令,不能用javah了:


- 按照生成的.h文件书写自己的c语言文件,命名为java调用的library的名字。然后在java同级目录下创建一个jni文件夹。将c和h文件放进去



- Android.mk文件书写:

大家都知道在Linux下编辑经常要写一个Makefile文件, 可以把这个Makefile文件理解成一个编译配置文件,它保存着如何编译的配置信息,即指导编译器如何来编译程序,并决定编译的结果是什么。而在Android下的Android.mk文件也是类型的功能,顾名思义,从名字上就可以猜测得到,Android.mk文件是针对Android的Makefile文件.具体来说:该文件是GNU Makefile的一小部分,会被编译系统解析一次或多次。你可以在每一个Android.mk file中定义一个或多个模块,你也可以在几个模块中使用同一个源代码文件。编译系统为你处理许多细节问题。



- 在在控制台中,进入到工程的jni目录下,然后输入ndk-build(如下所示),不出问题即可编译成功(但是在在入口调用会闪退,问题来源暂不明朗)。

余生余你余于余 发表于 2021-5-27 13:28

学习了,以后可能也会用到

ZLJ13697750126 发表于 2021-5-27 15:21


感谢分享,赞一个

8204118 发表于 2021-5-28 11:27

游戏会不会检测模拟器

debug_cat 发表于 2021-5-28 13:43

cmake简单一点吧,至于闪退问题,要有奔溃的时候的log才能定位。

feihu72 发表于 2021-5-28 15:38

感谢楼主分享~

烟花非易冷 发表于 2021-5-28 22:52

就这些图文来说,楼主辛苦了

goda 发表于 2021-5-28 23:08

崩溃直接看日志啊

sornian 发表于 2021-5-29 01:36

想的很详细

ZLJ13697750126 发表于 2021-5-29 15:21

赞一个,感谢分享
页: [1]
查看完整版本: Android Studio 3.6 SO文件生成(CMake)