我们要使用某服务的SDK,但是对方只有纯c++风格的第三方的arm架构的动态库(.so),因此无法直接在java app中直接使用,需要将原始so库封装成符合JNI命名规范的so库(以下简称JNI so库),然后才能通过JNI在java app中使用。我的需求并非直接在Android app中使用JNI so库,而是再把JNI so库封装一层,封装成Android app能直接使用的jar包或aar包,其实就是做成Android SDK发布。所以实质是将c++的SDK封装为Android SDK。
环境搭建
安装jdk
安装Android studio
下载SDK、NDK
(添加环境变量)新建AS项目和AAR Moudle
a. File -> New -> New Project, 选择add no activity,命名为 JNITest
b. local.properties中添加ndk路径,如ndk.dir=D:\android-ndk-r16-beta1
c. gradle.properties中添加 android.useDeprecatedNdk=true
d. File -> New -> New Moudle -> Android Library 命名为ALibrary在AAR Moudel中生成JNI so库和aar包
a. 在ALibrary的build.gradle的android.defaultConfig中添加ndk { moduleName "clib" stl "stlport_static" ldLibs "log", "z", "m" abiFilters "armeabi-v7a", "x86" }
b. 创建native接口JNIInterface.java
package com.lp.alibrary; /** * Created by 10256 on 2017/9/27. */ public class JNIInterface { static { System.loadLibrary("clib"); // 与ALibrary.build.gradle.android.defaultConfig.ndk.moduleName的值一致 } public native String name(); }
然后build项目生成class文件。
c. 通过javah命令生成符合JNI规范的c++头文件
打开cmd,cd到ALibrary所在目录,然后运行E:\ShareFolder\project\AndroidStudioProjects\JNITest\alibrary>javah -classpath build\intermediates\classes\debug -jni -d src\main\jni com.lp.alibrary.JNIInterface
比如我项目是在E:\ShareFolder\project\AndroidStudioProjects\JNITest,注意-classpath后面跟的不是.class文件路径,只要写类package所在路径就行,不要在后面添加\com\lp\alibrary,-d告诉.h文件放到哪,最后的目标要写类全路径名。
然后在src\main\jni下生成了符合JNI规范的.h文件。d. 在jni下新建c++源文件实现接口
#include "com_lp_alibrary_JNIInterface.h" #include <Android/log.h> #define TAG "MainActivity" #define LOGI(...) __android_log_print(ANDROID_LOG_INFO,TAG ,__VA_ARGS__) /* * Class: com_lp_alibrary_JNIInterface * Method: name * Signature: ()Ljava/lang/String; */ JNIEXPORT jstring JNICALL Java_com_lp_alibrary_JNIInterface_name (JNIEnv *env, jobject jobj) { LOGI("Java_com_lp_alibrary_JNIInterface_name"); return env->NewStringUTF("Hello From JNI!"); }
后面我会在这里调用第三方so库。然后rebuild,在build/intermediates/ndk/debug/lib下生成JNI so库,在build/output/aar下生成aar包。
在Android app中使用aar包
然后可以把这个aar包当成普通的jar文件给别的模块用了。a. 打开任意一个Android app,File -> New Module -> Import .JAR/.AAR Package 然后FileName浏览到刚才生成的.aar文件
b. File -> Project Structure -> Moudle.app -> Dependencies -> add -> Moudle Dependence -> alibrary-debug
c. 在MainActivity中使用AAR包中的native接口public class MainActivity extends AppCompatActivity { public static final String TAG = "MainActivity"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Log.i(TAG, new JNIInterface().name()); } }
然后make && run,控制台里输出
09-27 19:41:07.915 22781-22781/com.lp.vehicle I/MainActivity: Java_com_lp_alibrary_JNIInterface_name 09-27 19:41:07.915 22781-22781/com.lp.vehicle I/MainActivity: Hello From JNI!
大功告成。
有些人可能还在用eclipse开发Android,所以他不认aar包,只认jar包,那么在 alibrary.build.gradle中添加
task makeJar(type: Copy) {
delete 'build/libs/alib.jar'
from('build/intermediates/bundles/release/')
into('build/libs/')
include('classes.jar')
rename('classes.jar', 'alib.jar')
}
makeJar.dependsOn(build)
然后打开右侧的Gradle窗口 -> 选中 :alibrary -> Execute gradle task -> Command line 中输入 makeJar ,然后点ok,然后看到alibrary\build\libs下生成了alib.jar。
在其他项目使用该模块时,将jar包复制到app\libs下,同时要把build/intermediates/bundles/release/jni下的JNI so库复制到app\src\main\jniLibs下,jniLibs不存在则新建。这样就能通过jar包的方式使用该模块了。
参考:
android studio 实现类似百度地图 发布SDK机制实现(jni实现调用C++的so库,并封装jar包)
android 解决studio生成aar包并在其他工程引用aar包的坑,不需要任何gradle配置
ndroid tips(八)–>Android Studio打包apk,aar,jar包
AndroidStudio如何引入so包