AndroidStudio编译.SO库文件心得一

文本心得通过以下三篇文章习来:
Android Studio开发JNI工程
Android Sutdio开发NDK工程
在Android Studio中直接编译C/C++文件
谢谢三位作者的分享,才能学到这么宝贵的知识。
一直以来都是用的别的SDK提供的so库,编译so库文件跟调用so库的方法都是未曾尝试过。以前看博客了解的是使用Cygwin进行编译so. 现在有了AndroidStudio这个IDE,我们就可以直接使用这个工具就行编译了。
我们先准备好工具,先安装好NDK。然后开始本文的第一步:

编译.so文件

1.在这里我们先简单写一个方法,返回一个字符串(在项目中我们可以将关键的信息,如秘钥打在so库中,虽然也有被反编译的风险,但是还是相比定义在Java层,或者文件中会安全一点),那这里我们就先在Java层中定义一个要调用的本地方法。

public class Math {
    public native static String getStringFromNative();
}

然后我们在点击Build -Make Project进行编译
AndroidStudio编译.SO库文件心得一_第1张图片
编译完之后,就会在
AndroidStudio编译.SO库文件心得一_第2张图片
生成Math.class文件

2.我们点击Terminal将路径跳转
进入app_path/app/build/intermediates/classes/debug下,或
app_path/app/src/main/java
在我这里app_path就是F:\demo\JniDemo>
然后输入javah -classpath . com.csf.jnidemo.Math生成对应的.h文件
这个是c或者c++ 的头文件。
AndroidStudio编译.SO库文件心得一_第3张图片
执行完毕之后就会在对应的路径下生成
com_csf_jnidemo_Math.h。

  1. 编写本地方法。
    我们在main下建立一个jni的文件夹,将上面的.h文件拷贝过来,拷不拷贝都无所谓,我这里就是看着顺眼而已,我们要用到的主要是.h中声明的方法。它生命的方法具有一定的格式,我们接下来需要编写.c或者.cpp文件,函数名就是这个.h中定义的。
    我们在jni下建立Math.c文件
    代码如下:
/* DO NOT EDIT THIS FILE - it is machine generated */
#include 
#include 

#ifndef LOG_TAG
#define LOG_TAG "ANDROID_LAB"
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
#endif

/* Header for class com_csf_jnidemo_Math */

#ifndef _Included_com_csf_jnidemo_Math
#define _Included_com_csf_jnidemo_Math
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class: com_csf_jnidemo_Math
 * Method: getStringFromNative
 * Signature: ()Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_csf_jnidemo_Math_getStringFromNative
  (JNIEnv * env, jobject jObj){
      LOGE("log string from ndk.");
      return (*env)->NewStringUTF(env,"Hello From JNI!");
  }

#ifdef __cplusplus
}
#endif
#endif

其中一些语句我还不太理解,主要是从上面三篇文章拷贝过来的,但是具体内容看懂就OK了,
Java_com_csf_jnidemo_Math_getStringFromNative
在这个方法中我们使用JNI的NewStringUTF生成一个UTF的jstring ,值为”Hello From JNI!”,返回到Java层,同时打下一句Log。

注:
上面的生成.h跟.c方法我们可以使用AndroidStudio来直接生成.c文件,方法如下,直接在native方法上按alt+enter生成c层的方法
AndroidStudio编译.SO库文件心得一_第4张图片

4.编译.so文件
我们先配置工程的ndk路径,不然就会出现以下错误

Error:Execution failed for task ':app:compileDebugNdk'. 
> NDK not configured. 
Download the NDK from http://developer.android.com/tools/sdk/ndk/.Then add ndk.dir=path/to/ndk in local.properties. 
(On Windows, make sure you escape backslashes, e.g. C:\\ndk rather than C:\ndk)

我们在local.properties文件中配置如下信息指定ndk路径
AndroidStudio编译.SO库文件心得一_第5张图片

在gradle.properties中加入如下语句

android.useDeprecatedNdk = true

接着修改我们app的gradle文件,在android下增加如下信息
AndroidStudio编译.SO库文件心得一_第6张图片

ndk{
            moduleName "JniTest"    //lib的名称,对应LOCAL_MODULE
            //stl "stlport_shared"    //对应APP_STL
            ldLibs "log", "z", "m"  //链接时使用到的库,对应LOCAL_LDLIBS
            //cFlags 编译gcc的flag,对应LOCAL_CFLAGS
        }

这样就能编译出各种ABI的so库,如果要指定编译某种CPU架构的so文件,在ndk标签下增加如下语句

abiFilters "armeabi", "armeabi-v7a", "x86"  

下面前期准备工作就做的差不错了,不过还有一个问题,我没有去验证,
上面几篇博客都有提到的在执行”Build->Rebuild Project”,编译so文件
的时候,在Windows平台下会出现下面这个问题

Error:Execution failed for task ':app:compileDebugNdk'.
> com.android.ide.common.internal.LoggedErrorException: Failed to run command:
 D:\Mission\adt-bundle-windows\ndk-r10b\ndk-build.cmd NDK_PROJECT_PATH=null APP_BUILD_SCRIPT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\Android.mk APP_PLATFORM=android-21 NDK_OUT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj NDK_LIBS_OUT=C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\lib APP_ABI=armeabi,armeabi-v7a,x86
Error Code:
 2
Output:
 make.exe: *** No rule to make target `C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj/local/armeabi/objs/JniTest/C_\Users\sodinochen\AndroidstudioProjects\JniTest2\app\src\main\jni', needed by `C:\Users\sodinochen\AndroidstudioProjects\JniTest2\app\build\intermediates\ndk\debug\obj/local/armeabi/objs/JniTest/C_\Users\sodinochen\AndroidstudioProjects\JniTest2\app\src\main\jni\main.o'. Stop.

解决方法是在jni下面建立一个空的.c文件。
最后编译后的so就存在以下目录下
AndroidStudio编译.SO库文件心得一_第7张图片

文件编译出来是不是很高兴,可是应该怎么使用呢

6.调用so库文件
我们创建一个Android module(Test,下文简称T工程),将上面lib下的文件拷贝到到这个新工程下lib文件夹下
AndroidStudio编译.SO库文件心得一_第8张图片
之后我们要编辑一下T工程的的gradle脚本,在android标签下增加以下脚本

sourceSets {
        main {
            jniLibs.srcDir(['libs'])
        }
    }

那前期的准备工作就完成了,下面就是调用的了

我们在T工程下的Activity中编写如下代码
AndroidStudio编译.SO库文件心得一_第9张图片

这个时候我们会发现编译器根本就找不到com.csf.jnidemo.Math.getStringFromNative这个包名下的这个方法。
这是因为我们没有编写对应的Java层声明代码(以前都是直接用的别人的jar包跟so文件,别人的Java层声明代码就是写在jar包上),那应该怎么声明方法呢。这个是有一定的要求的,Java层申明getStringFromNative方法,他的类名,包名要跟.so中的想对应。我们这里采用最简单的方法,因为so是我们自己编译的,我们其实已经有Java层声明代码了,看下图
AndroidStudio编译.SO库文件心得一_第10张图片
我们直接将上面编译so工程的Math类及其包名目录拷贝到我们的T工程上就OK 了,其实我们可以将这个代码打出Jar包在T工程中引用,这样才是比较稳妥的。

那就运行T工程吧。结果就出来了。
AndroidStudio编译.SO库文件心得一_第11张图片

这里写图片描述

好了,本文到此就完结了,上面只是用到的最简单的从so库取出一个字符串,以后我们再学习探讨下传参进native方法中处理后return到Java层的方法以及调用其他现成so库的方法。大家一起学习进步吧。
本文如果有存在什么错误的话,请指明下,谢谢!

你可能感兴趣的:(android,jni,so,android,studio)