[cpp] view plain copy
  1. 在Eclipse环境下进行JNI环境配置非常的复杂,需要记忆的东西很多,相比较,IDE变为AS之后整个过程都变的更简单,AS2.2版本发布后让我觉得很有用的更新内容之一就是NDK的支持更加便捷,之前的NDK使用需要在Android.mk等文件中进行诸多配置,在java代码中写明了native方法之后要生成cpp文件示例依赖于在终端中编写的代码(这一块我没有做过只是了解可能说明有错误的地方,等我看了这块回来改正)  

现在在AS2.2中进行JNI开发更容易,它将原先的jni文件夹取消,取而代之的是cpp文件夹,并且进行编译的方式也改成了cmake。

首先简要介绍一下一个新的工程,怎么支持AS2.2的新特性

第一步:在AS中下载cmake等工具

tools->android->sdkManager里面勾选CMake、LLDB、NDK这三项,在安装完成之后AS会自动的配好NDK的路径,如果想手动设置可以在项目上app右键选择open module setting里面的sdk设定NDK路径

AndroidStudio2.2+环境下的JNI环境搭建

第二步:新建工程,并勾选对C++的支持选项

AndroidStudio2.2+环境下的JNI环境搭建

下面全部next一路到底,在最后一步可以选择C++规范,这里我选择默认不改变

AndroidStudio2.2+环境下的JNI环境搭建

然后就可以finish了,创建好项目之后的项目结构是这样的:

AndroidStudio2.2+环境下的JNI环境搭建

(Android结构)

AndroidStudio2.2+环境下的JNI环境搭建

(project结构)

说明一下,cpp文件夹是由工程创建的时候勾选的支持C++来完成的,cmakeList.txt也是,如果是自己的老项目,需要手动的建,我认为比较好的方法就是新建一个支持C++的新项目,然后对照着在老项目的project视图里建立cpp和cmakelist.txt,还可以直接把cmakelist里的内容粘贴过来。

第三步:编写(更改)cmakeList的内容

为了要会写或者说会改,首先看一下这个文件里有哪些配置的内容

[cpp] view plain copy
  1. # Sets the minimum version of CMake required to build the native  
  2. # library. You should either keep the default value or only pass a  
  3. # value of 3.4.0 or lower.  
  4.   
  5. cmake_minimum_required(VERSION 3.4.1)  
  6.   
  7. # Creates and names a library, sets it as either STATIC  
  8. # or SHARED, and provides the relative paths to its source code.  
  9. # You can define multiple libraries, and CMake builds it for you.  
  10. # Gradle automatically packages shared libraries with your APK.  
  11.   
  12. add_library( # Sets the name of the library.  
  13.              native-lib  
  14.   
  15.              # Sets the library as a shared library.  
  16.              SHARED  
  17.   
  18.              # Provides a relative path to your source file(s).  
  19.              # Associated headers in the same location as their source  
  20.              # file are automatically included.  
  21.              src/main/cpp/native-lib.cpp )  
  22.   
  23. # Searches for a specified prebuilt library and stores the path as a  
  24. # variable. Because system libraries are included in the search path by  
  25. # default, you only need to specify the name of the public NDK library  
  26. # you want to add. CMake verifies that the library exists before  
  27. # completing its build.  
  28.   
  29. find_library( # Sets the name of the path variable.  
  30.               log-lib  
  31.   
  32.               # Specifies the name of the NDK library that  
  33.               # you want CMake to locate.  
  34.               log )  
  35.   
  36. # Specifies libraries CMake should link to your target library. You  
  37. # can link multiple libraries, such as libraries you define in the  
  38. # build script, prebuilt third-party libraries, or system libraries.  
  39.   
  40. target_link_libraries( # Specifies the target library.  
  41.                        native-lib  
  42.   
  43.                        # Links the target library to the log library  
  44.                        # included in the NDK.  
  45.                        ${log-lib} )  
原始的代码是这样的,把注释拿掉,改成自己的说法

[cpp] view plain copy
  1. cmake_minimum_required(VERSION 3.4.1)#说明要求的cmake最低版本  
  2. add_library( #这里三行从下往上看分别是源文件、生成库类型、生成库名称  
  3.              native-lib  
  4.              SHARED  
  5.              src/main/cpp/native-lib.cpp )  
  6. find_library( #这里是找到两个库  
  7.               log-lib  
  8.               log )  
  9. target_link_libraries( #这里链接了我们上面生成的库  
  10.                        native-lib  
  11.                        ${log-lib} )  

第四步:module里的gradle编写(改写)

[cpp] view plain copy
  1. apply plugin: 'com.android.application'  
  2.   
  3. android {  
  4.     compileSdkVersion 25  
  5.     buildToolsVersion "25.0.1"  
  6.     defaultConfig {  
  7.         applicationId "cn.ndk.jni1"  
  8.         minSdkVersion 22  
  9.         targetSdkVersion 25  
  10.         versionCode 1  
  11.         versionName "1.0"  
  12.         testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"  
  13.         externalNativeBuild {  
  14.             cmake {  
  15.                 cppFlags ""  
  16.             }  
  17.         }  
  18.     }  
  19.     buildTypes {  
  20.         release {  
  21.             minifyEnabled false  
  22.             proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'  
  23.         }  
  24.     }  
  25.     externalNativeBuild {  
  26.         cmake {  
  27.             path "CMakeLists.txt"  
  28.         }  
  29.     }  
  30. }  
  31.   
  32. dependencies {  
  33.     compile fileTree(dir: 'libs', include: ['*.jar'])  
  34.     androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {  
  35.         exclude group: 'com.android.support', module: 'support-annotations'  
  36.     })  
  37.     compile 'com.android.support:appcompat-v7:25.0.1'  
  38.     testCompile 'junit:junit:4.12'  
  39. }  

其中要注意到的地方我用红框框下来了(或者截图了下面一个忘记画框了尴尬)

AndroidStudio2.2+环境下的JNI环境搭建

AndroidStudio2.2+环境下的JNI环境搭建

最后看一下整个调用过程,基本和没有改变编译方式之前是一样的,首先是在java代码里面写一个静态块,用来加载lib库

[java] view plain copy
  1. // Used to load the 'native-lib' library on application startup.  
  2.  static {  
  3.      System.loadLibrary("native-lib");  
  4.  }  

然后是申明一下要调用这个库里的底层函数的名字同名的方法

[java] view plain copy
  1. /** 
  2.      * A native method that is implemented by the 'native-lib' native library, 
  3.      * which is packaged with this application. 
  4.      */  
  5.     public native String stringFromJNI();  

最后是在java代码里调用这个方法就可以了,具体的函数实现交给cpp文件里面去写

[java] view plain copy
  1. @Override  
  2. protected void onCreate(Bundle savedInstanceState) {  
  3.     super.onCreate(savedInstanceState);  
  4.     setContentView(R.layout.activity_main);  
  5.   
  6.     // Example of a call to a native method  
  7.     TextView tv = (TextView) findViewById(R.id.sample_text);  
  8.     tv.setText(stringFromJNI());//这一行调用了这个底层函数对应的方法  
  9. }  

然后具体的这个函数会在cpp里面实现

[cpp] view plain copy
  1. #include <jni.h>  
  2. #include <string>  
  3.   
  4. extern "C"  
  5. jstring  
  6. Java_cn_ndk_jni1_MainActivity_stringFromJNI(  
  7.         JNIEnv *env,  
  8.         jobject /* this */) {  
  9.     std::string hello = "Hello from C++";  
  10.     return env->NewStringUTF(hello.c_str());  
  11. }  

这里可以看到方法的后半段和java的方法名字是一样的,这个函数的名字和基本内容是可以自动生成的,这一块留到下面讲,集中说一下解决的问题和可以使用的技巧。


解决的问题和学到的技巧:

1.想到的第一个问题就是如果我要自己往里面加cpp怎么办,毕竟上面都是点点点没有自己建一个cpp啊,那自己建cpp要怎么做呢

首先建文件这些操作我建议到project视图里去做,因为这个视图才是真正的项目文件结构,如果出现了建了文件之后没有编写gradle和cmakelist没有刷新android结构的时候可以看到文件在哪里,心里踏实,然后可以看到在src/main/下面有cpp文件夹,我们自己建立的cpp就放在这里面,我这里以一个test.cpp为例,并且给他一个头文件,头文件选项就在建立cpp文件的时候可以勾选(虽然我不用头文件主要是试一下万一以后要用),建立后是这样的

AndroidStudio2.2+环境下的JNI环境搭建

但是这时候如果你切回去会发现在android视图里还是没有这个文件夹,不截图了,看到这里的可以自己试一下,这就是第二个问题了

2.为什么我建了cpp它不见了啊?

这个问题常见于在android视图下就建立这个cpp文件的情况,这样会更直观,建完了cpp文件在cpp文件夹下面,结果什么都没有发生(最怕空气突然安静),这是因为你在cmakelist里面还没有加上这个源文件呢,这时候你要这样做:

[cpp] view plain copy
  1. cmake_minimum_required(VERSION 3.4.1)#说明要求的cmake最低版本  
  2. add_library( #这里三行从下往上看分别是源文件、生成库类型、生成库名称  
  3.              native-lib  
  4.              SHARED  
  5.              src/main/cpp/native-lib.cpp src/main/cpp/test.cpp)  
  6. find_library( #这里是找到两个库  
  7.               log-lib  
  8.               log )  
  9. target_link_libraries( #这里链接了我们上面生成的库  
  10.                        native-lib  
  11.                        ${log-lib} )  
在源文件那里添加一下这个新加进来的cpp文件,最后回到你建立好的cpp文件编辑界面,会发现它说没有同步(sy..那个),这时候你要点击一下让它自己去同步AndroidStudio2.2+环境下的JNI环境搭建

进来啦!完美,这个问题困扰我很久,是在网上找到的一个小伙伴的方法救了我,我一时找不到当时那个页面了,等我找到了我会把原链接贴上来,感谢这个小天使!

特别说明一下,这是把这两个cpp都合到一个库里面去了,没有生成两个库,如果是生成两个库,我做了尝试,就是直接把add_lib那里复制了一遍,并且把源文件改为新的cpp文件,把生成的库文件名字改了一下,在link那里把新的库也添加上,这样生成的项目结构是(android视图中)会有两个子文件夹,都是shared的库,然后每个文件夹下面有一个cpp,分别是这两个库引入的cpp文件,说的罗嗦,看代码和图

[cpp] view plain copy
  1. add_library( # Sets the name of the library.  
  2.              native-lib  
  3.   
  4.              # Sets the library as a shared library.  
  5.              SHARED  
  6.   
  7.              # Provides a relative path to your source file(s).  
  8.              # Associated headers in the same location as their source  
  9.              # file are automatically included.  
  10.              src/main/cpp/native-lib.cpp )  
  11. add_library( # Sets the name of the library.  
  12.              mylib  
  13.   
  14.              # Sets the library as a shared library.  
  15.              SHARED  
  16.   
  17.              # Provides a relative path to your source file(s).  
  18.              # Associated headers in the same location as their source  
  19.              # file are automatically included.  
  20.              src/main/cpp/test.cpp )  

[cpp] view plain copy
  1. target_link_libraries( # Specifies the target library.  
  2.                        native-lib  
  3.                         mylib  
  4.                        # Links the target library to the log library  
  5.                        # included in the NDK.  
  6.                        ${log-lib} )  

AndroidStudio2.2+环境下的JNI环境搭建

3.解决好了添加cpp问题,再来看cpp示例的自动生成问题,我现在有一个新的方法交返回值为String的叫做JNI的方法,怎么让软件自动生成一个对应的c++函数呢,这样做:

首先在java代码里写你的native方法名字,仿照原来的那个写就好,当然这时候肯定是标红的,毕竟你静态调用进来的库里面并没有会这么个函数对应啊,自然就调用不到了,这时候等..别方,等到这个native方法的前面出现一个红色的小灯泡(总觉得小灯泡有槽要吐)这时候点一下,就会自动创建一个cpp的标准啦,对应的参数也会帮我们填上去啦,很方便对吧。(亲测要把原先的那个直接复制下来,然后改名字之后更容易出现这个红色的小灯泡,比较奇怪,可能有什么原因)AndroidStudio2.2+环境下的JNI环境搭建

会在cpp文件里添加这一段

[cpp] view plain copy
  1. JNIEXPORT jstring JNICALL  
  2. Java_cn_ndk_jni1_MainActivity_jni(JNIEnv *env, jobject instance) {  
  3.   
  4.     // TODO  
  5.   
  6.   
  7.     return env->NewStringUTF(returnValue);  
  8. }  
最后连上之后两边的行号栏都会有一个双向箭头,点击可以在两边切换。

我还一个更极端的情况,用来测试这种自动生成方式,首先是有两个cpp,这两个cpp分别用add_lib那里生成两个库,然后在link里面把两个库都链接上,再在java代码里用静态块把两个库都导入进来,这时候再写底层函数对应的方法,用自动生成的方法,会发现会自动生成到某一个cpp文件里去(没找到规律),这时候只要把这一段复制粘贴到我们自己想要它在的cpp里就可以了(也就是不知道自动会生成到哪里去,但是可以手动调整),亲测复制粘贴到任意的cpp里都会自动链接上去的,双向箭头都在哦,当然啊这种比较复杂的情况是后面需要考虑的,如果再出现什么问题,我会回来更正的。


最后的最后以一个比较蠢蠢的问题结束:

AndroidStudio2.2+环境下的JNI环境搭建

为什么提示我们说这段代码无效呢,明明就是c++代码,为什么还是灰色的,也不像自带的那样有编辑器的颜色呢,其实,这是写的语句没有意义造成的,只要改成这样子就好了:

test.h

[cpp] view plain copy
  1. //  
  2. // Created by Sli.D on 2016/12/30.  
  3. //  
  4.   
  5. #ifndef JNI1_TEST_H  
  6.   
  7. #include <iostream>  
  8. #define JNI1_TEST_H  
  9.   
  10. #endif //JNI1_TEST_H  
test.cpp

AndroidStudio2.2+环境下的JNI环境搭建

当然真正JNi的的cpp是不能这样写的,是像前面那样子的,不过问题原因找到了。


Android studio 2.2+下的JNi环境搭建就是这样,欢迎一起交流学习。

相关文章: