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

  • 相关阅读:
    298. Binary Tree Longest Consecutive Sequence
    117. Populating Next Right Pointers in Each Node II
    116. Populating Next Right Pointers in Each Node
    163. Missing Ranges
    336. Palindrome Pairs
    727. Minimum Window Subsequence
    211. Add and Search Word
    年底购物狂欢,移动支付安全不容忽视
    成为程序员前需要做的10件事
    全球首推iOS应用防破解技术!
  • 原文地址:https://www.cnblogs.com/Free-Thinker/p/6149842.html
Copyright © 2011-2022 走看看