zoukankan      html  css  js  c++  java
  • 跟我学Android NDK开发(一)

    Android NDK 开发跟其它开发一样,首先需要配置好开发环境,本文以 Ubuntu系统为例介绍如何进行 Android NDK 开发环境的配置

    1. 简介

    什么是 Android NDK 呢? NDK(Native Development Kit) 是一个允许开发者用一些本地语言(C/C++)编写 Android App 的部分功能的工具集。对于一些特定的 App,NDK 非常有利于我们直接使用现成的用 C/C++ 编写的代码库(但对于大多数 App 来说,NDK 是没有必要的)。使用 NDK 进行 C/C++ Android 开发的基本结构和流程如下图(来自shihongzhi博客 ):

    2.开发环境配置

    NDK 开发过程中涉及到将 C/C++ 程序编译为动态库(.so文件),由于Ubuntu系统中已经安装有 C/C++ 的编译工具 gcc/g++,以及 make 工具,所以在此不作介绍。另外还需要安装 JDK,并配置 Java 的环境变量,这个不是本文重点,在此也不作介绍了。

    下面重点讲讲 Android 相关 SDK 的安装和配置,主要涉及到 Android SDK,ADT,NDK等。要进行 Android 开发,首先需要安装 Android SDK,要在 Eclipse 中进行开发的话,还需在 Eclipse 中安装 ADT(Android Develop Tools),在 Android 官网上提供了 SDK 和 包含 ADT 的 Eclipse 的集成开发包,可以一起下载:adt-bundle-linux-x86-20140321.zip。另外,还需要安装 NDK,下载地址:android-ndk-r9d-linux-x86.tar.bz2。下载完这两个压缩包后解压并移动到 /usr/local 目录下: 

    (1)先下载安装NDK。
    下载链接:http://developer.android.com/tools/sdk/ndk/index.html下载后解压缩就可以用了。

    (2)打开Eclipse,点Window->Preferences->Android->NDK,设置NDK路径(如果没有NDK选项,可按照http://jingyan.baidu.com/article/4d58d5413000a09dd4e9c0fe.html进行设置),如下图所示:


    (3)新建一个Android工程(这里为AntiVirus),在工程上右键点击Android Tools->Add Native Support...,然后给我们的.so文件取个名字,例如本例中的:HelloJni。这时候工程就会多一个jni的文件夹,jni下有Android.mk和HelloJni.cpp文件。Android.mk是NDK工程的Makefile,HelloJni.cpp就是NDK的源文件。

    (4)新建并配置一个Builder:
      (a)Project->Properties->Builders->New,新建一个Builder。
      (b)在弹出的【Choose configuration type】对话框,选择【Program】,点击【OK】:
      (c)在弹出的【Edit Configuration】对话框中,配置选项卡【Main】。
           在“Name“中输入新builders的名称(这里为Ndk_Builder)。
           在“Location”中输入nkd-build(windows为nkd-build.cmd)的路径。
          (我的是/usr/local/android-ndk-r10/ndk-build,根据各自的ndk路径设置,也可以点击“Browser File System…”来选取这个路径)。
           在“Working Diretcoty”中输入${workspace_loc:/AntiVirus}(也可以点击“Browse Workspace”来选取AntiVirus目录)。
     
      (d)【Edit Configuration】对话框中,配置选项卡【Refresh】。
          勾选“Refresh resources upon completion”,
          勾选“The entire workspace”,
          勾选“Recuresively include sub-folders”。
     
      (e)【Edit Configuration】对话框中,配置选项卡【Build options】。
          勾选“After a “Clean””,
          勾选“During manual builds”,
          勾选“During auto builds”,
          勾选“Specify working set of relevant resources”。
     
          点击“Specify Resources…”,勾选AntiVirus工程的“jni“目录,点击”finish“。点击“OK“,完成配置。到这里Eclipse就能够自动调用NDK编译jni目录下的C/C++代码了。
    (5)在AntiVirus工程中新建一个TestJni.java类(为了调用C/C++代码),其内容如下:

     1 package com.wat.antivirus;
     2  
     3 public class TestJni {
     4     static { 
     5         System.loadLibrary("HelloJni"); // //加载相应的动态库,相对路径,src的根目录,库名必须是libxxx.so 
     6     } 
     7    
     8     public native String helloSay(); // 返回字符串,使用JNI的关键字native
     9     public native int helloAdd(int a,int b); // 两个整数相加 
    10     public native int helloSub(int a,int b); // 两个整数相减 
    11     public native int helloMul(int a,int b); // 两个整数相乘 
    12     public native int helloDiv(int a,int b); // 两个整数相除 
    13 }

    (6)在 Eclipse 中 build 一下生成对应 .class 文件,然后使用 javah 工具根据该 class 文件自动生成 jni API 的头文件。在 AntiVirus工程根目录下执行如下命令:

    javah -classpath ./bin/classes -d jni com.wat.antivirus.TestJni

    其中 -classpath ./bin/classes 表示类的路径,-d jni 表示生成的头文件存放的目录, com.wat.antivirus.TestJni 则是完整类名。

    注意生成的头文件命名采用:包名+类名 

       JNI头文件一般由三部分组成:

    •    头文件
    •    本地方法的注释
    •    本地方法在头文件中的表示

    执行命令后,在 ~/workspace/AntiVirus/jni 目录下生成了C++头文件 com_wat_antivirus_TestJni.h ,文件内容如下:

     1 /* DO NOT EDIT THIS FILE - it is machine generated */
     2 #include <jni.h>
     3 /* Header for class com_wat_antivirus_TestJni */
     4 
     5 #ifndef _Included_com_wat_antivirus_TestJni
     6 #define _Included_com_wat_antivirus_TestJni
     7 #ifdef __cplusplus
     8 extern "C" {
     9 #endif
    10 /*
    11  * Class:     com_wat_antivirus_TestJni
    12  * Method:    helloSay
    13  * Signature: ()Ljava/lang/String;
    14  */
    15 JNIEXPORT jstring JNICALL Java_com_wat_antivirus_TestJni_helloSay
    16   (JNIEnv *, jobject);
    17 
    18 /*
    19  * Class:     com_wat_antivirus_TestJni
    20  * Method:    helloAdd
    21  * Signature: (II)I
    22  */
    23 JNIEXPORT jint JNICALL Java_com_wat_antivirus_TestJni_helloAdd
    24   (JNIEnv *, jobject, jint, jint);
    25 
    26 /*
    27  * Class:     com_wat_antivirus_TestJni
    28  * Method:    helloSub
    29  * Signature: (II)I
    30  */
    31 JNIEXPORT jint JNICALL Java_com_wat_antivirus_TestJni_helloSub
    32   (JNIEnv *, jobject, jint, jint);
    33 
    34 /*
    35  * Class:     com_wat_antivirus_TestJni
    36  * Method:    helloMul
    37  * Signature: (II)I
    38  */
    39 JNIEXPORT jint JNICALL Java_com_wat_antivirus_TestJni_helloMul
    40   (JNIEnv *, jobject, jint, jint);
    41 
    42 /*
    43  * Class:     com_wat_antivirus_TestJni
    44  * Method:    helloDiv
    45  * Signature: (II)I
    46  */
    47 JNIEXPORT jint JNICALL Java_com_wat_antivirus_TestJni_helloDiv
    48   (JNIEnv *, jobject, jint, jint);
    49 
    50 #ifdef __cplusplus
    51 }
    52 #endif
    53 #endif

    我们拿其中一个方法作具体说明:

    JNIEXPORT jint JNICALL Java_com_wat_antivirus_TestJni_helloAdd(JNIEnv *, jobject, jint, jint);

    1、JNIEXPORT和JNICALL都是JNI的关键字,表示此函数是要被JNI调用的。

    2、jint本地方法返回的数据类型,表示的是java下面的int类型

    3、Java_com_wat_antivirus_TestJni_helloAdd 这个就是被JNICALL调用的部分,也就是Java中的native 方法名,这里起名字的方式比较特别,是:包名+类名+方法名。(Java_为JNI中标识此方法来源于java的标识头)

      注意:类名千万别加下划线,Test_Jni

    4、JNIEnv *  它是一个接口指针,用于定位函数表中的函数,jni.h头文件中存在着大量被封装好的函数,这些函数也是JNI编程中经常被使用到的,要想调用这些函数就需要使用JNIEnv这个对象

    5、jobject  两种情况

      类 对象 实例    TestJni tj  = new TestJni();     tj.helloAdd(10,20);

    •   上述中helloAdd方法是一个非静态方法,在Java中要想调用它必须先实例化对象,然后再用对象调用它,那这个时候jobject就可以看做Java类的一个实例化对象,也就是在本地方法实现上 jobject thiz  这个thiz参数就是指的tj
    •   如果helloAdd是一个静态方法,那么在Java中,它不是属于一个对象的,而是属于一个类的,Java中用TestJni .helloAdd()这样的方式来调用,这个时候jobject就可以看做是java类的本身,也就是说thiz就是TestJni.class

    (7)编辑Android.mk文件,其内容如下:

     1 #返回当前mk的路径,宏函数’my-dir’, 由编译系统提供
     2 LOCAL_PATH := $(call my-dir) 
     3 #CLEAR_VARS由编译系统提供,让GNU MAKEFILE为你清除LOCAL_XXX变量
     4 include $(CLEAR_VARS) 
     5 #生成动态库名
     6 LOCAL_MODULE := HelloJni 
     7 #添加源文件、头文件
     8 LOCAL_SRC_FILES := HelloJni.c 
     9 #生成动态库
    10 include $(BUILD_SHARED_LIBRARY)

    补充说明:

     1 #增加所需的库       
     2 LOCAL_LDLIBS := -llog  
     3 链接的库不产生依赖关系,一般用于不需要重新编译的库,如库不存在,则会报错找不到。默认的路径/android-ndk-r6b/platforms/android-3/arch-arm/usr/lib 链接的都是系统的库
     4 LOCAL_SHARED_LIBRARIES := libexpat libcurl 
     5 会生成依赖关系,当库不存在时会去编译这个库。一般用于编译第三方提供的库
     6 #生成动态库
     7 include $(BUILD_SHARED_LIBRARY)
     8 #生成静态库
     9 #include $(BUILD_STATIC_LIBRARY) 
    10 #生成应用程序
    11 #include $(BUILD_EXECUTABLE)

    (8)将com_wat_antivirus_TestJni.h拷贝到AntiVirus工程的jni目录下, 然后在HelloJni.c文件中完成头文件中函数的实现,其内容如下:  

     1 #include <string.h>
     2 #include <jni.h>
     3 
     4 jstring
     5 Java_com_wat_antivirus_TestJni_helloSay( JNIEnv* env,
     6                                                   jobject thiz )
     7 {
     8 #if defined(__arm__)
     9   #if defined(__ARM_ARCH_7A__)
    10     #if defined(__ARM_NEON__)
    11       #if defined(__ARM_PCS_VFP)
    12         #define ABI "armeabi-v7a/NEON (hard-float)"
    13       #else
    14         #define ABI "armeabi-v7a/NEON"
    15       #endif
    16     #else
    17       #if defined(__ARM_PCS_VFP)
    18         #define ABI "armeabi-v7a (hard-float)"
    19       #else
    20         #define ABI "armeabi-v7a"
    21       #endif
    22     #endif
    23   #else
    24    #define ABI "armeabi"
    25   #endif
    26 #elif defined(__i386__)
    27    #define ABI "x86"
    28 #elif defined(__x86_64__)
    29    #define ABI "x86_64"
    30 #elif defined(__mips64)  /* mips64el-* toolchain defines __mips__ too */
    31    #define ABI "mips64"
    32 #elif defined(__mips__)
    33    #define ABI "mips"
    34 #elif defined(__aarch64__)
    35    #define ABI "arm64-v8a"
    36 #else
    37    #define ABI "unknown"
    38 #endif
    39 
    40     return (*env)->NewStringUTF(env, "Hello from JNI !  Compiled with ABI " ABI ".");
    41 }

    编辑HelloJni.c并保存后,可以看到AntiVirus工程下的obj/local/armeabi目录下将自动生成libAntiVirus.so库。

    (9)在AntiVirus.java中完成对TestJni.java中函数的调用:

     1 package com.wat.antivirus;
     2 
     3 import android.app.Activity;
     4 import android.os.Bundle;
     5 import android.view.View;
     6 import android.widget.Button;
     7 import android.widget.TextView;
     8 
     9 
    10 public class MainActivity extends Activity {
    11 
    12     @Override
    13     protected void onCreate(Bundle savedInstanceState) {
    14         super.onCreate(savedInstanceState);
    15         setContentView(R.layout.activity_main);
    16         
    17         Button btn = (Button)findViewById(R.id.btn_cal);
    18         btn.setOnClickListener(new Button.OnClickListener() {
    19             @Override
    20             public void onClick(View arg0) {
    21                 // TODO Auto-generated method stub
    22                 TextView tv1 = (TextView) findViewById(R.id.textView1);
    23                 TextView tv2 = (TextView) findViewById(R.id.textView2);
    24                 TextView tv3 = (TextView) findViewById(R.id.textView3);
    25     
    26                 int a = Integer.parseInt(tv1.getText().toString());
    27                 int b = Integer.parseInt(tv2.getText().toString());
    28                 TestJni tj = new TestJni();
    29                 int c = tj.helloAdd(a,b);
    30                 String str = tj.helloSay();  
    31                 tv3.setText(str + Integer.toString(c));
    32             }
    33         });
    34     }
    35 }

    (10)运行AntiVirus工程,在模拟器中可以看到界面输出来自HelloJni.c 文件中的“HelloWorld from JNI ! “。

    OK,NDK实例到此完成。后续就可以深入的学习NDK/JNI了,比如C/C++与Java的数据类型转换,Android.mk文件的编写格式等。 

     

  • 相关阅读:
    常用sql
    epoll
    poll
    ShardedJedis的分片原理
    puppet(一种Linux、Unix、windows平台的集中配置管理系统)
    zabbix(一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案)
    1111
    http_load(基于linux平台的一种性能测试工具)
    zenoss(智能监控软件)
    SaltStack(自动化运维工具)
  • 原文地址:https://www.cnblogs.com/goodhacker/p/4051288.html
Copyright © 2011-2022 走看看