Android与JNI(一)

原文:http://www.cnblogs.com/eddy-he/archive/2012/08/08/2628676.html

软件版本:
  ubuntu10.04
  java version "1.6.0_30-ea"
  eclipse
  android-ndk-r5b

目录:

  1. JNI 开发的基本步骤
  2. 创建一个 android 工程
  3. 生成 jni 的头文件
  4. 编写 c/c++ 代码
  5. 编译生成动态库
  6. 测试
  7. 参考资料

1. JNI 开发的基本步骤

  在 JAVA 程序中调用 c/c++ 函数的方法一般是:

  1、创建一个类(HelloWorld.java)或者在原来的类中声明本地方法。
  2、使用 javac 编译源文件 HollowWorld.java,生成 HelloWorld.class。
  3、使用 javah –jni 来生成头文件(HelloWorld.h),这个头文件里面包含了本地方法的函数原型。
  4、编写 c/c++ 代码(HelloWorld.c)实现头文件中的函数原型。
  5、将 HelloWorld.c 编译成一个动态库,生成 Hello-World.dll 或者 libHello-World.so。
  6、使用 java 命令运行 HelloWorld 程序,类文件 HelloWorld.class 和本地库(HelloWorld.dll 或者 libHelloWorld.so)在运行时被加载。

  如图所示:

2. 创建一个 android 工程

  创建一个新的 android 工程 HelloJNI 便于演示:

 1 package com.example.hellojni; 2  3 import android.os.Bundle; 4 import android.app.Activity; 5 import android.view.Menu; 6 import android.view.MenuItem; 7 import android.widget.TextView; 8 import android.support.v4.app.NavUtils; 9 10 public class HelloJNI extends Activity {11 12     @Override13     public void onCreate(Bundle savedInstanceState) {14         super.onCreate(savedInstanceState);15         setContentView(R.layout.hello_jni);16         getActionBar().setDisplayHomeAsUpEnabled(true);17         18         /* Create a TextView and set its content.19          * the text is retrieved by calling a native20          * function.21          */22         TextView  tv = new TextView(this);23         tv.setText( stringFromJNI() );24         setContentView(tv);25     }26 27     @Override28     public boolean onCreateOptionsMenu(Menu menu) {29         getMenuInflater().inflate(R.menu.hello_jni, menu);30         return true;31     }32 33     34     @Override35     public boolean onOptionsItemSelected(MenuItem item) {36         switch (item.getItemId()) {37             case android.R.id.home:38                 NavUtils.navigateUpFromSameTask(this);39                 return true;40         }41         return super.onOptionsItemSelected(item);42     }43 44     /* A native method that is implemented by the45      * 'HelloJNI' native library, which is packaged46      * with this application.47      */48     public native String  stringFromJNI();49 50     /* This is another native method declaration that is *not*51      * implemented by 'HelloJNI'. This is simply to show that52      * you can declare as many native methods in your Java code53      * as you want, their implementation is searched in the54      * currently loaded native libraries only the first time55      * you call them.56      *57      * Trying to call this function will result in a58      * java.lang.UnsatisfiedLinkError exception !59      */60     public native String  unimplementedStringFromJNI();61 62     /* this is used to load the 'HelloJNI' library on application63      * startup. The library has already been unpacked into64      * /data/data/com.example.HelloJni/lib/libHelloJNI.so at65      * installation time by the package manager.66      */67     static {68         System.loadLibrary("HelloJNI");69     }70 }

  代码很简单,主要是调用本地方法返回一个字符串,显示在屏幕上。有两点需要针对说明一下:

1 static {2         System.loadLibrary("HelloJNI");3 }

  上面这几行代码是用来加载动态库 libHelloJNI.so 。那么是在什么时候加载呢?当第一次使用到这个类的时候就会加载。

1 public native String stringFromJNI();2 public native String unimplementedStringFromJNI(); 

  使用关键字 native 声明本地方法,表明这两个函数需要通过本地代码 c/c++ 实现。

  通过 eclipse 编译代码,生成 .class 文件。为生成 jni 的头文件做好准备。

3. 生成 jni 的头文件

  现在的问题是要怎么样实现 stringFromJNI 和 unimplementedStringFromJNI 这两个函数呢?这两个函数要怎么命名?直接用这两个名字行不行?要解决这些疑问,就要用到 javah 这个命令。

  在你新建成的工程根目录下,键入以下命令:

$javah -classpath bin/classes -d jni com.example.hellojni.HelloJNI

  先简单说一下这个命令有什么用。这个命令是用来生成与指定 class 想对应的本地方法的头文件。

  -classpath:指定类的路径。
  -d:输出目录名。
  com.example.hellojni.HelloJNI:完整的类名。

  命令的结果是在本地生成一个名为 jni 的目录,里面有一个名为 com_example_hellojni_HelloJNI.h 的头文件。这个文件就是我们所需要的头文件,他声明了两个函数

$lsAndroidManifest.xml  assets  bin  gen  ic_launcher-web.png  jni  libs  obj  proguard-project.txt  project.properties  res  src
 1 /* DO NOT EDIT THIS FILE - it is machine generated */ 2 #include <jni.h> 3 /* Header for class com_example_hellojni_HelloJNI */ 4  5 #ifndef _Included_com_example_hellojni_HelloJNI 6 #define _Included_com_example_hellojni_HelloJNI 7 #ifdef __cplusplus 8 extern "C" { 9 #endif10 /*11  * Class:     com_example_hellojni_HelloJNI12  * Method:    stringFromJNI13  * Signature: ()Ljava/lang/String;14  */15 JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJNI_stringFromJNI16   (JNIEnv *, jobject);17 18 /*19  * Class:     com_example_hellojni_HelloJNI20  * Method:    unimplementedStringFromJNI21  * Signature: ()Ljava/lang/String;22  */23 JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJNI_unimplementedStringFromJNI24   (JNIEnv *, jobject);25 26 #ifdef __cplusplus27 }28 #endif29 #endif

  上面代码中的 JNIEXPORT 和 JNICALL 是 jni 的宏,在 android 的 jni 中不需要,当然写上去也不会有错。

  函数名比较长,不过是有规律的,按照:java_pacakege_class_method 形式来命名。调用 stringFromJNI() 就会执行JNICALL Java_com_example_hellojni_HelloJNI_stringFromJNI() 。

  还有一个地方需要注意一下,那就是第13行

  Signature: ()Ljava/lang/String;
  
()表示函数的参数为空(这里为空是指除了JNIEnv *, jobject 这两个参数之外没有其他参数,JNIEnv* 和 jobject 是所有 jni 函数必有的两个参数,分别表示 jni 环境和对应的 java 类(或对象)本身);
  Ljava/lang/String; 表示函数的返回值是 java 的 String 对象。

4. 编写 c/c++ 代码

  按照 com_example_hellojni_HelloJNI.h 中声明的函数名,在 jni 目录下建立一个 HelloJNI.c 文件实现其函数体。

 1 /* 2  * Copyright (C) 2009 The Android Open Source Project 3  * 4  * Licensed under the Apache License, Version 2.0 (the "License"); 5  * you may not use this file except in compliance with the License. 6  * You may obtain a copy of the License at 7  * 8  *      http://www.apache.org/licenses/LICENSE-2.0 9  *10  * Unless required by applicable law or agreed to in writing, software11  * distributed under the License is distributed on an "AS IS" BASIS,12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13  * See the License for the specific language governing permissions and14  * limitations under the License.15  *16  */17 #include <string.h>18 #include <jni.h>19 #include "com_example_hellojni_HelloJNI.h"20 21 /* This is a trivial JNI example where we use a native method22  * to return a new VM String. See the corresponding Java source23  * file located at:24  *25  *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java26  */27 jstring Java_com_example_hellojni_HelloJNI_stringFromJNI(JNIEnv *env, jobject this)28 {29     return (*env)->NewStringUTF(env, "Hello from JNI !");30 }

  这里只实现了 Java_com_example_hellojni_HelloJNI_stringFromJNI() ,函数很简单,返回一个字符串。但是由于函数定义中的返回值是 java 的 String 类,所以不能简单的返回一个字符串,需要通过 JNI 函数 NewStringUTF 在本地创建一个新的 java.lang.String 对象。这个新创建的字符串对象拥有一个与给定的 UTF-8 编码的 C 类型字符串内容相同的 Unicode 编码字符串。这里抛出了一个问题,就是说我们在写 JNI 的时候,有些数据类型是需要做转换的。

5. 编译生成动态库

  如果你还没有下载 ndk 的话,请到这里来下载。ndk 有什么用?就是用来编译 jni 代码的。安装方法很简单,只要把 ndk-build 命令加入到环境变量 PATH 中就可以使用了。验证方法就是键入 ndk-build 命令后,不会出现 command not found 的字样。

  在 jni 目录下创建一个名为 Android.mk 的文件,并输入以下内容:

 1 # Copyright (C) 2009 The Android Open Source Project 2 # 3 # Licensed under the Apache License, Version 2.0 (the "License"); 4 # you may not use this file except in compliance with the License. 5 # You may obtain a copy of the License at 6 # 7 #      http://www.apache.org/licenses/LICENSE-2.0 8 # 9 # Unless required by applicable law or agreed to in writing, software10 # distributed under the License is distributed on an "AS IS" BASIS,11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.12 # See the License for the specific language governing permissions and13 # limitations under the License.14 #15 LOCAL_PATH := $(call my-dir)16 17 include $(CLEAR_VARS)18 19 LOCAL_MODULE    := HelloJNI20 LOCAL_SRC_FILES := HelloJNI.c21 22 include $(BUILD_SHARED_LIBRARY)

  然后在 jni 目录下输入 ndk-build 命令就可以编译了。

$ndk-build Compile thumb  : HelloJNI <= HelloJNI.cSharedLibrary  : libHelloJNI.soInstall        : libHelloJNI.so => libs/armeabi/libHelloJNI.so

  将会在 HelloJNI/libs/armeabi 目录下生成一个名为 libHelloJNI.so 的动态库。编译生成 apk 时会把这个库一起打包。

6. 测试

  7. 参考资料

  [1]. Android: NDK编程入门笔记
  [2]. JNI及Android JNI 开发基本知识和具体操作步骤
  [3]. 如何在Android下使用JNI

更多相关文章

  1. Android下pm 命令详解
  2. linux下配置android sdk
  3. Android(安卓)pm命令详解
  4. Android生命周期
  5. android程序编写的小问题
  6. Android平台开发-Power management-电源管理
  7. webrtc 针对 android 平台的编译和运行
  8. Android(安卓)SSL BKS证书的生成过程
  9. 箭头函数的基础使用

随机推荐

  1. 服务定期执行
  2. android利用handler实现打地鼠游戏
  3. Android Activity页面跳转动画
  4. Android(安卓)根据EditText搜索框ListVie
  5. Android 双卡发送短信
  6. 关于ANR的解释与避免方式
  7. android中获取设备的型号、手机厂商以及
  8. Active调用及传值(Intent、Bundle)
  9. Android 操作Sqlite
  10. Android修改状态栏颜色即状态栏字体颜色