外行搞应用-Android Studio 调用C程序
去年同事给了个图像解码的.so库,让搞进app里,研究了一下搞定了。没有及时总结。最近又给了个sm2验签的源码搞到app里,发现之前学会的东西已经完全忘了,所以这次赶紧记下来。
环境
用Android Studio开发的话需要先装LLDB,CMake,NDK这三个SDK Tools。
新建Java类
新建一个Java类,写下面的代码。这里是引入lib_sum这个库,声明了一个本地方法sum。
package com.example.ndkdemo;
public class JniSum {
static {
System.loadLibrary("lib_sum");
}
public static native int sum(int a,int b);
}
C代码
新建C代码目录,我建到了app/src/main/jni/里。在目录先新建一个空的sum.c文件。
在Terminal中先cd到JniSum 类的目录里,然后用javah -jni com.example.ndkdemo.JniSum
生成一个头文件,并把头文件剪切到C代码目录里。这相当于自动生成了本地方法sum对应的jni接口的C原型。如果不使用javah,直接自己写也是可以的。
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_ndkdemo_JniSum */
#ifndef _Included_com_example_ndkdemo_JniSum
#define _Included_com_example_ndkdemo_JniSum
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_example_ndkdemo_JniSum
* Method: sum
* Signature: (II)I
*/
JNIEXPORT jint JNICALL Java_com_example_ndkdemo_JniSum_sum
(JNIEnv *, jclass, jint, jint);
#ifdef __cplusplus
}
#endif
#endif
接下来根据这个原型来写C函数,这里测试代码很简单,直接输出两个输入之和。要搞数组之类比较复杂的操作时需要研究一下jni接口的使用方法。
#include "com_example_ndkdemo_JniSum.h"
JNIEXPORT jint JNICALL Java_com_example_ndkdemo_JniSum_sum (JNIEnv * env, jclass obj, jint a, jint b){
return a+b;
}
CMakeLists
CMakeLists的功能相当于Makefile。
新建CMakeLists.txt文件,随便放哪,我这里放到了工程根目录下。
然后加入以下内容。
- .so库名,最终编译产生的.so库就是这个名字
- 源文件目录,这个目录是源文件和CMakeLists的相对路径,多文件的话就都写进来
- 别忘了修改target_link_libraries中的.so库名
# For more information about using CMake with Android Studio, read the
# documentation: https://d.android.com/studio/projects/add-native-code.html
# Sets the minimum version of CMake required to build the native library.
cmake_minimum_required(VERSION 3.4.1)
# Creates and names a library, sets it as either STATIC
# or SHARED, and provides the relative paths to its source code.
# You can define multiple libraries, and CMake builds them for you.
# Gradle automatically packages shared libraries with your APK.
add_library( # Sets the name of the library.
lib_sum #.so库名 可自定义
# Sets the library as a shared library.
SHARED
# Provides a relative path to your source file(s).
app/src/main/jni/sum.c ) #源文件所在目录,多文件的话就都加进来
# Searches for a specified prebuilt library and stores the path as a
# variable. Because CMake includes system libraries in the search path by
# default, you only need to specify the name of the public NDK library
# you want to add. CMake verifies that the library exists before
# completing its build.
find_library( # Sets the name of the path variable.
log-lib
# Specifies the name of the NDK library that
# you want CMake to locate.
log )
# Specifies libraries CMake should link to your target library. You
# can link multiple libraries, such as libraries you define in this
# build script, prebuilt third-party libraries, or system libraries.
target_link_libraries( # Specifies the target library.
lib_sum #.so库名 可自定义
# Links the target library to the log library
# included in the NDK.
${log-lib} )
- c程序需要调用.so的话就再写一个add_library():
add_library(mysum SHARED IMPORTED)
set_target_properties(mysum PROPERTIES IMPORTED_LOCATION
${CMAKE_SOURCE_DIR}/app/src/main/jniLibs/${ANDROID_ABI}/libmysum.so)
这个mysum是我之前编译好的一个不带jni接口的.so库,add_library中说明要导入这个库, set_target_properties中有这个库的路径。别忘了最后target_link_libraries中把这个库link上。这样就可以直接在c文件中调用了。
建议所有的.so库都放到app/src/main/jniLibs/中,这个是默认路径,用起来比较方便。
编译
编译时先右键app,点击Link C++ Project with Gradle,然后选择CMakelists的路径。这样就根据CMakeLists的内容自动在gradle中添加了配置。
build.gradle中自动加入以下内容:
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
externalNativeBuild {
cmake {
path file('../CMakeLists.txt')
}
}
最后在MainActivity随便写个代码测试一下:
public class MainActivity extends AppCompatActivity {
private TextView txv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
txv = findViewById(R.id.txv);
int a = JniSum.sum(1,2);
txv.setText(a+"");
}
}
在虚拟机上试一下,工作正常。