android JNI (NDK)的故事

来源:互联网 发布:腾讯高级php面试题 编辑:程序博客网 时间:2024/05/16 17:51

在讲故事之前,先要普及下概念。(懂的人跳过~)

C/C++是需要经过编译,连接,一个.c文件需经过至少两步之后才生产一个可执行文件。用.c进行举例。


 

文件后缀(Window平台)

文件后缀(Linux平台)

源文件

.c

.c

头文件

.h

.h

中间件

.object

.o

静态库

.lib

.a

动态库

.dll

.so

可执行文件

.exe

一般无后缀


因为跟Android相关,主要就介绍linux下的C程序

静态库:就是.o进行打包,相当于多个.o组合在一起成了.a,相当于把应该有的东西都包含了,好处是运行块,缺点是体积庞大,替换麻烦

动态库:也是由.o而来,不过运行时才加载的,体积小,可替换,缺点可能是运行稍慢。

在生成各个文件时,需要正确的组织,linux下是通过 makefile文件来组织的。


本文主讲通过命令行来操作生成各种文件!!!

Android studio原生JNI使用CMake,暂且跳过


做以下步骤之前,需要下载过NDK(ndk-build),并将其加入到系统的环境变量里面。

下面开始步入正题,只要有看过JNI里面对应的C函数的人,都可以看到起函数名字是很长,很奇怪的。


步骤一:生成native函数对应的.h文件


假如有一个包名:com.app.demo.jni 包含类Test.java,如果下所示:

package com.app.demo.jni;public class Test {    public static native String sayHello();}
其在JNI中对应的函数名是这样的:

JNIEXPORT jstring JNICALL Java_com_app_demo_jni_Test_sayHello
  (JNIEnv *, jclass);

看这个结构是,是与java代码中的是有对应关系,完全可以手动写出这样的函数名。但是,这简直就是要了老命,多麻烦啊,所以就有java自带的工具,一般在命令行中使用javah这个命令。看看是怎么做的。

首先需要把.java文件进行编译,生成.class文件,在android studio中使用Buid->make project这个按钮,生成的.class文件如下所示:

(不管是在哪种IDE中,反正先找到生成的.class文件的位置)

跳转到包含包名.class文件的目录上,再用javah 包名+类名的操作进行生成.h,其效果如下:


说明:这里是通过IDE直接生成了.class文件,只是可以用javac命令来生产

总之,过程就是先生成.class,再用javah生成头文件


步骤二:生成.so文件(不包含其它的.so的简单使用)


1.先创建一个jni目录,这个目录随便放在哪里(是真的随便放在哪里,没必要跟项目撤上关系)

   我放在D:\csdn\jni这个目录下

2.将生成的.h文件放在jni目录下

3.创建一个.c文件,其中代码如下:

#include "com_app_demo_jni_Test.h"JNIEXPORT jstring JNICALL Java_com_app_demo_jni_Test_sayHello  (JNIEnv * env, jclass object){       return  (*env)->NewStringUTF(env, "JNI:hello world");  }

上面只是 函数声明中加上了形参,函数中的形参名字是随便起的,不过一般就这么叫。

java中的代码是可以C/C++中的代码互相调用的,java的对象类型在C中都有映射关系。

详细了解:https://www3.ntu.edu.sg/home/ehchua/programming/java/JavaNativeInterface.html


4.创建一个Android.mk文件,内容如下

# Copyright (C) 2008 The Android Open Source Project## Licensed under the Apache License, Version 2.0 (the "License");# you may not use this file except in compliance with the License.# You may obtain a copy of the License at##      http://www.apache.org/licenses/LICENSE-2.0## Unless required by applicable law or agreed to in writing, software# distributed under the License is distributed on an "AS IS" BASIS,# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.# See the License for the specific language governing permissions and# limitations under the License.LOCAL_PATH := $(call my-dir)include $(CLEAR_VARS)LOCAL_SRC_FILES := Test.cLOCAL_MODULE := testinclude $(BUILD_SHARED_LIBRARY)
这里是生成一个.so库,模块名叫test,最终生成的文件会是libtest.so

Android.mk详细了解:http://android.mk/


5.跳转到jni目录,执行ndk-build,效果如下

执行完了后,再回到D:\csdn,会发现多了两个目录libs 和 obj ,两个下面有libtest.so文件,随便选择一个就生成的.so文件。这个时候默认生成的是一个armeabi平台的.so

需要说明是,可以通过NDK_OUT =./test 这样的方式制定obj存放的目录,通过NDK_LIBS_OUT=./mylibs 这样的方式来存放生成的libs。这种参数的指定可以直接将生成so库放在项目中jniLibs中(这种好处就是不要手动的拷贝so库了)

6.使用Application文件(这一步可选,没有执行ndk-build则默认生成armeabi平台的)

APP_OPTIM:=debugAPP_STL:=stlport_static#stlport_sharedAPP_MODULES:=test#for c++11, gnustl_static is bigger than stlport#APP_STL := gnustl_static#APP_CPPFLAGS += -std=c++11APP_ABI := armeabi armeabi-v7a 
最后的APP_ABI是用来指定平台的。这里指定了两个平台。


7.回到自己的IDE主项目,建立JniLibs,导入.so

8.进行调用

package com.app.demo.jni;import android.os.Bundle;import android.support.v7.app.AppCompatActivity;import android.util.Log;public class MainActivity extends AppCompatActivity {    static {        System.loadLibrary("test");   //defaultConfig.ndk.moduleName    }    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        Log.i("test",Test.sayHello());    }}
9,运行出结果



大功告成。本文中生成.so的地方和调用的地方是分开的。

10。如果需要向其他人提供包含Native调用 的jar。你就需要导出一个jar和.so。提供给他人使用。

      这就是为什么你看到很多跟视频编解码相关的项目都提供的一个.jar和一个.so库了。


这里只是最简单的过程,不包含使用其他的.so或者.a库

整个流程如下:




Android.mk和JNI详情,只给了官网地址,可能后续会慢慢说明,欢迎交流!!


====2017.1.6补充====

以上方式来弄,JNI部分无法很好的调试,实用与小型的简单项目。这个,大神不用IDE也能调好,,只能说,你开心就好!


之前的方式都是采用一个实验性的gradle插件来进行调试,推荐一篇文章(需要翻墙):NDK调试实验性插件


强烈推荐的是Android studio原生支持的调试(CMake 相关):Android studio原生支持NDK





0 0
原创粉丝点击