zoukankan      html  css  js  c++  java
  • AndroidStudio实现JNI的示例详解

    1. NDK简介
    Android NDK 是在SDK前面又加上了“原生”二字,即Native Development Kit,因此又被Google称为“NDK”


    1.1 NDK产生的背景
    Android平台从诞生起,就已经支持C、C++开发。
    众所周知,Android的SDK基于Java实现,这意味着基于Android SDK进行开发的第三方应用都必须使用Java语言。
    但这并不等同于“第三方应用只能使用Java”。
    在Android SDK首次发布时,Google就宣称其虚拟机Dalvik支持JNI编程方式,
    也就是第三方应用完全可以通过JNI调用自己的C动态库,
    即在Android平台上,“Java+C”的编程方式是一直都可以实现的。


    不过,Google也表示,使用原生SDK编程相比Dalvik虚拟机也有一些劣势,Android SDK文档里,找不到任何JNI方面的帮助。
    即使第三方应用开发者使用JNI完成了自己的C动态链接库(so)开发,但是so如何和应用程序一起打包成apk并发布?
    这里面也存在技术障碍。比如程序更加复杂,兼容性难以保障,无法访问Framework API,Debug难度更大等。
    开发者需要自行斟酌使用。
     
    于是NDK就应运而生了。NDK全称是Native Development Kit。
    NDK的发布,使“Java+C”的开发方式终于转正,成为官方支持的开发方式。
    NDK将是Android平台支持C开发的开端。


    1.2 为什么使用NDK
        1.代码的保护。由于apk的java层代码很容易被反编译,而C/C++库反编译难度较大。
        2.可以方便地使用现存的开源库。大部分现存的开源库都是用C/C++代码编写的。
        3.提高程序的执行效率。将要求高性能的应用逻辑使用C开发,从而提高应用程序的执行效率。
        4.便于移植。用C/C++写得库可以方便在其他的嵌入式平台上再次使用。


    1.3 NDK简介
    1.NDK是一系列工具的集合
    NDK提供了一系列的工具,帮助开发者快速开发C(或C++)的动态库,并能自动将so和java应用一起打包成apk。
    NDK集成了交叉编译器,并提供了相应的mk文件隔离CPU、平台、ABI等差异,开发人员只需要简单修改mk文件
    (指出“哪些文件需要编译”、“编译特性要求”等),就可以创建出so。
     
    2.NDK提供了一份稳定、功能有限的API头文件声明
    Google明确声明该API是稳定的,在后续所有版本中都稳定支持当前发布的API。
    从该版本的NDK中看出,这些API支持的功能非常有限,
    包含有:C标准库(libc)、标准数学库(libm)、压缩库(libz)、Log库(liblog)。


    1.4 NDK的安装
    见《 Ubuntu14.04下最新Android NDK安装 》


    1.5 NDK的目录结构说明
     . build:    该目录存放的使用NDK的mk脚本,mk脚本指定了编译参数
     . docs:     该目录存放的是NDK的使用帮助文档
     . platforms:这里面存放的是与各个Android版本相关的平台(x86,arm,mips)相关C语言库和头文件
     . prebuilt: 预编译工作目录
     . samples:  存放的是演示程序
     . sources:  存放的是NDK工具链的C语言源码
     . tests:    测试相关的文件
     . toolchains:工具链,存放了三种架构的静态库等文件
     . ndk-build.cmd:Window平台使用NDK的命令
     . ndk-build:Linux平台使用NDK的命令


    2. JNI入门
    2.1 新建一个Android工程
    这一步很简单,只需要命名下工程的名字,一路向下即可,最后编译并运行测试下;


    2.2 修改 MainActivity.java文件
    修改 app->src->main->java->MainActivity,
    定义一个native方法:


    ...
    public class MainAcitivity extends AppCompatActivity {
      // 新定义的native方法,意思是该方法的具体实现交给c语言实现
      public native String helloC();


      ...




    2.3 创建jni目录及文件
    切换到“Project”视图,
    在 app->src->main下,右键选择“New->Directory”
    填入目录名"jni", 并点击"OK".
    在jni目录下创建hello.c源文件,代码清单如下:


    // 引入头文件
    #include <stdio.h>
    #include <jni.h>
    #include "hello.h"


    // 定义在MainActivity.java类中的helloC对应的C语言函数
    jstring Java_com_example_luis_jnihello_MainActivity_helloC(JNIEnv* env, jobject obj){
      char* str = "hello from C";


      // 调用 jni.h中定义的创建字符串函数
      jstring string = (*(*env)).NewStringUTF(env, str);
      return string;
    }


    NOTE:
    上面的代码虽然简单但是关于jni.h头文件和方法名必须单独说明; 


    JNI中C源文件方法名的命名规则
    这里的命名规则指用于跟java文件中native方法对应的C语言方法,而C语言中的其他方法命名只要符合C语言规则就行。


    jstring Java_com_example_luis_jnihello_MainActivity_helloC(JNIEnv* env, jobject obj) 中,
     . jstring是方法返回值类型,我们可以把jstring看成是java中String跟C语言中char*类型的一个中间转换类型,
       java跟C语言的数据类型是不一样的,他们之间要想互相调用就必须通过一种中介来实现,这个中介就是在jni.h头文件中定义的。


     . 关于更多的转换类型,在本文档的第2章会有更详细的说明。


     . 方法名第一个字母必须是Java,首单词大写,然后下划线_,
        然后是将该方法所在的包、类、方法用“_”连接起来,比如com.sample.luis.jnihello.MainActivity类中的helloC方法,
        转变成C语言中的方法名为Java_com_sample_luis_jnihello_MainActivity_helloC。
        方法的形参有两个是必须的也就是不管java中的方法是否有形参,但是C语言中对应的方法必须有JNIEnv* env,和jobject obj,
        如果java方法中还用其他形参,那么在C语言中严格按照顺序排在jobject obj参数的后面即可。


     . 上面的env代表指向JVM的指针,obj是调用该方法的java对象。


    2.4 使用NDK编译生成hello.so文件
    从终端进入jni目录:
    $ cd jni
    $ ls
    hello.c
    hello.h


    从NDK安装目录的sample/hello-jni/jni目录复制文件
    $ cp /opt/android-ndk-r10e/samples/hello-jni/Application.mk ./
    $ cp /opt/android-ndk-r10e/samples/hello-jni/Android.mk  ./


    再在Android中修改Android.mk
    修改后的Android.mk文件清单如下,
    LOCAL_PATH := $(call my-dir)


    include $(CLEAR_VARS)


    LOCAL_MODULE := hello
    LOCAL_SRC_FILES := hello.c


    include $(BUILD_SHARED_LIBRARY)


    我们只需要修改LOCAL_MODULE和LOCAL_SRC_FILES两个参数即可。
    LOCAL_MODULE参数是指定编译后的目标文件的名称,其实编译好的目标文件名为libhello.so,
    LOCAL_SRC_FILES指定了要编译的源文件。


    还可以通过修改Application.mk文件来指定生成的动态库的类型:
    如按以的修改则只会生成一种动态库:
    # Build both ARMv5TE and ARMv7-A machine code.
    APP_ABI := armeabi x86


    也可所设置成生成所有平台都支持的动态库:
    APP_ABI :=all


    在终端运行命令:
    $ ndk-build


    命令运行后,它会


    2.4 修改jni的库目录 
    将app->src->main->libs改成
    app->src->main->jniLibs


    NOTE:
    每次运行后ndk-build后,都需要修改这个目录名,否则对动态库的修改不会生效;


    2.5 修改 gradle->gradle.properties
    在文件的最末行添加:
    android.useDeprecateNdk=true


    2.6 配置项目NDK目录
    选择菜单
    File->Project Structure->Android NDK location:
    填入NDK的安装路径;


    2.7 在MainActivity.java中调用 C语言
    代码清单如下:


    ...
    public class MainAcitivity extends AppCompatActivity {
      // 新定义的native方法,意思是该方法的具体实现交给c语言实现
      public native String helloC();
      
      // 加载libhello.so动态库,但是我们在加载时必须去掉lib和后缀
      static {
        System.loadlibrary("hello");
      }


      @Override
      protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        // 调用并显示
        TextView tv = new TextView(this);
        tv.setText(helloC());
        setContentView(tv);
      }


      ...


    3. 编译运行
    运行后,会显示:


    hello from C


    表示测试成功!

    参考文档:
    http://bbs.itheima.com/thread-189661-1-1.html

  • 相关阅读:
    VS2005中Ajax控件作用说明
    DevExpress 2.0 GridControl 使用方法
    主机信息
    sql2005中运用一条sql语句完成数据导出到Excel中
    跟我学做c#皮肤美化
    (转)QT事件传递与事件过滤器
    (转)主成分分析(Principal components analysis)最大方差解释
    这是填充首页的
    (转)C++中extern “C”含义深层探索
    (转)如何配置Qt使用VS2010进行开发
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/6149842.html
Copyright © 2011-2022 走看看