Android学习——第一个NDK程序

  在前面的学习中,咱们已经讲解了关于NDK编程的环境搭建流程,简单的使用咱们也经过官网自己自带的例子进行说明了。但是相信你们必定还存在这么的一个疑惑:“若是我要本身利用NDK编写一个Android应用,具体应该怎么作?有什么要求”。OK,别担忧,下面就让咱们一块儿来利用NDK来编写一个简单的Android应用。html

1 建立一个新的Project

   1) 经过如下命令建立一个新的Android Project (详细的使用方法,你们能够回去从新参考博文《Android学习次日-android经常使用命令》)java

android create project -n myfirstndk -t 1 -p ./myfirstndk -k cn.uc.myjni -a MainActivity

 当Project建立成功后,咱们能够经过查看文件夹发现它的大致架构以下图:android

  

2 开始Coding

   1) 进入该项目的MainActivity.java所在的目录下,新建一个定义本地方法的类 NumberSum.java ,输入如下代码:编程

 1 package cn.uc.myjni;
 2 
 3 public class NumberSum {
 4 
 5     // 声明一个本地方法
 6     public native int add (int a, int b);
 7 
 8     // 加载名为 libnumber_sum.so的库
 9     // 根据Unix的规则,系统为自动为number_sum
10     // 添加上lib前缀和.so后缀
11     static {
12         System.loadLibrary("number_sum");
13     }
14 }

 

   2) 调用如下命令,编译NumberSum.javawindows

javac -encoding UTF-8 NumberSum.java

编译成功后,控制台并无特别的输出,同时,咱们能够在目录下发现Number.class文件架构

  

这里须要注意的是,个人命令中之因此指定源文件使用的编码是由于我使用的是UTF-8编码,在windows中直接经过javac NumberSum.java 进行编译的话,会出现如下错误:app

 

   3) 返回项目源代码文件夹下,好比个人就是返回到src目录下,经过javah命令,生成头文件:ide

javah cn.uc.myjni.NumberSum

执行成功后,控制台没有输出什么特别的内容,同时,咱们能够发如今src目录下多了一个头文件:函数

  

   javah生成的头文件知识讲解:学习

  ① 从头文件的名字咱们能够看出,它是由编译的调用本地方法的类名及其所在的包名经过下划线"_"分割组成。如咱们的程序中是编译cn.uc.myjni.NumberSum生成的,因此就算cn_uc_myjni_NumberSum

  ② 头文件中的内容可能不少,可是咱们只关注这个方法的声明:

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

  在这个声明中,咱们能够清晰的看到方法名的最后面add就是在NumberSum类中声明的本地方法add,而前面的是 cn_uc_myjni_NumberSum就算完整的包名和类名,由此咱们能够知道,一个完整的JNI函数名有3部分组成:Java、定义native方法的类的全名(包名+类名)、实际的函数名。这三部分用"_"进行链接。

 

   4) 在Project的根目录下,新建一个文件夹jni (必须叫作jni),将咱们以前生成的头文件移动到jni文件夹下,如图:

  

  

   5) 新建一个c文件,名字为 number_sum.c,输入如下代码:

1 #include <jni.h>  
2 
3 // 此处的方法签名与头文件中声明的方法签名是相同的
4 // 建议直接从头文件中复制过来 
5 JNIEXPORT jint JNICALL Java_cn_uc_myjni_NumberSum_add(JNIEnv *env,jobject obj,jint a,jint b)  
6 {  
7     return (a+b);  
8 } 

   下面讲解下上面的一些相关内容:

  ① JNIEXPORT jint JNICALL JNIEnv 等都是在jni.h中定义的;

  ② 其中上面参数中的env表示JNI的调用环境,obj表示定义native方法的Java类的对象自己。

  

 6) 新建一个Android.mk 文件,建议直接从官方文档给的例子 hello-jni 中复制出来进行修改LOCAL_MODUE 和 LOCAL_SRC_FILES,代码以下:

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := number_sum
LOCAL_SRC_FILES := number_sum.c

include $(BUILD_SHARED_LIBRARY)

   下面咱们简单讲解一下上面几个参数的含义吧:

  ① LOCAL_PATH:Android.mk 的第一行必须是LOCAL。用于指定参与编译的C/C++源文件的位置。在上面例子中,宏函数mydir 是由系统提供的,用来返回当前目录的路径。也就是包含Android.mk文件的目录的路径。

  ② include ${CLEAR_VARS}:CLEAR_VARS变量是在系统中定义的,用来指定一个特殊的GNU Make文件。该文件用来清空不少以LOCAL_开头的变量,例如LOCAL_MODULE、LOCAL_SRC_FILES、LOCAL_STATIC_LIBRARIES 等。但这些变量不包括LOCAL_PATH。之因此要清空这些变量,是由于这些都是全局变啦ing。同时这些变量又要在不一样的GNU Make文件中使用,为了多个GNU Make文件不相互影响,就须要在执行每个GNU make文件(Android.mk文件)以前先清空这些变量。

  ③ LOCAL_MODULE := number_sum :在每个模块中必须定义 LOCAL_MODULE变量,用来指定生成的模块名。该变量的值必须是惟一的,并且不能包含任何空白分隔符。实际上,LOCAL_MODULE 变量的值就是生产共享库的文件名(不包括lib和.so),在编译时,系统会自动在文件名的先后添加上lib 和.so 。若是该模块名前缀加了lib,在生产共享库的时候不会进行添加。

  ④ LOCAL_SRC_FILES := number_sum.c:用来指定一个C/C++源文件列表,这里不须要指定头文件,系统会自动计算当前C/C++源文件 include的头文件。系统就直接将LOCAL_SRC_FILES变量指定的源文件传给编译器。C++源文件的默认扩展名是.cpp,但能够经过LOCAL_DEFAULT_CPP_EXTENSION 变量改变 C++文件默认拓展名,例如将该变量的值设成".cxx",注意不要忘记了" . "

  ⑤ include ${BUILD_SHARED_LIBRARY} : BUILD_SHARED_LIBRARY是在系统中定义的,用来指定一个GNU Make脚本文件。该脚本文件会根据以LOCAL_开头的变量来生成共享库文件。若是想生成静态库文件,可使用BUILD_STATIC_LIBRARY变量。

 

   7) 为了可以把咱们调用共享代码库执行的程序结果显示出来,咱们对生成的代码界面进行必定的修改:

     I. 打开 res/layout/main.xml 文件,给TextView添加上一个Id,方便咱们在后台经过id获取组件进行显示内容的修改:

 1 <?xml version="1.0" encoding="utf-8"?>
 2 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 3     android:orientation="vertical"
 4     android:layout_width="fill_parent"
 5     android:layout_height="fill_parent"
 6     >
 7 <!-- 给TextView 添加了ID -->
 8 <TextView  
 9     android:id="@+id/info_text"
10     android:layout_width="fill_parent" 
11     android:layout_height="wrap_content" 
12     android:text="Hello World, MainActivity"
13     />
14 </LinearLayout>

    II. 给咱们的主界面MainActivity.java的onCreate() 方法添加上将结果显示到界面上的逻辑代码:

 1 package cn.uc.myjni;
 2 
 3 import android.app.Activity;
 4 import android.widget.TextView;
 5 import android.os.Bundle;
 6 
 7 public class MainActivity extends Activity
 8 {
 9     /** Called when the activity is first created. */
10     @Override
11     public void onCreate(Bundle savedInstanceState)
12     {
13         super.onCreate(savedInstanceState);
14         setContentView(R.layout.main);
15 
16         // 经过Id获取TextView组件
17         TextView textView=(TextView) findViewById(R.id.info_text);  
18         NumberSum sum = new NumberSum();  
19         // 调用本地方法 
20         int result=sum.add(10, 20);  
21         textView.append("\n 10+20="+result); 
22     }
23 }

3 编译共享代码库

   1) 打开cmd,将路径转移到项目的根目录myfirstndk下,执行命令ndk-build (详情你们能够参考博文《Android学习——windows下搭建NDK_r9环境》的 第1节),执行后控制台输出以下图,同时咱们能够在\obj\local\armeabi路径下查看到libnumber_sum.so共享库:

    

4 打包验证

    1) 一样在cmd中,直接执行命令 ant debug 进行打包:

  

  打包成功后,咱们能够在bin文件夹下看到相应的myfirstndk-debug.apk

  

   2) 经过 emulator 命令打开你的模拟器(命令详情能够参考博文《Android学习第三天-打包经常使用命令》 第3节)

   

   3) 经过 adb 命令将apk安装到模拟器中(命令详情能够参考博文《Android学习第一天-adb经常使用命令》 第3节),如图表示安装成功。

  

   4) 打开模拟器中的程序MainActivity 显示如图则表示安装成功。

     

相关文章
相关标签/搜索