• [转]Android与JNI(一)


    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     @Override
    13     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 native
    20          * function.
    21          */
    22         TextView  tv = new TextView(this);
    23         tv.setText( stringFromJNI() );
    24         setContentView(tv);
    25     }
    26 
    27     @Override
    28     public boolean onCreateOptionsMenu(Menu menu) {
    29         getMenuInflater().inflate(R.menu.hello_jni, menu);
    30         return true;
    31     }
    32 
    33     
    34     @Override
    35     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 the
    45      * 'HelloJNI' native library, which is packaged
    46      * 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 that
    52      * you can declare as many native methods in your Java code
    53      * as you want, their implementation is searched in the
    54      * currently loaded native libraries only the first time
    55      * you call them.
    56      *
    57      * Trying to call this function will result in a
    58      * java.lang.UnsatisfiedLinkError exception !
    59      */
    60     public native String  unimplementedStringFromJNI();
    61 
    62     /* this is used to load the 'HelloJNI' library on application
    63      * startup. The library has already been unpacked into
    64      * /data/data/com.example.HelloJni/lib/libHelloJNI.so at
    65      * 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 的头文件。这个文件就是我们所需要的头文件,他声明了两个函数。

    $ls
    AndroidManifest.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 #endif
    10 /*
    11  * Class:     com_example_hellojni_HelloJNI
    12  * Method:    stringFromJNI
    13  * Signature: ()Ljava/lang/String;
    14  */
    15 JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJNI_stringFromJNI
    16   (JNIEnv *, jobject);
    17 
    18 /*
    19  * Class:     com_example_hellojni_HelloJNI
    20  * Method:    unimplementedStringFromJNI
    21  * Signature: ()Ljava/lang/String;
    22  */
    23 JNIEXPORT jstring JNICALL Java_com_example_hellojni_HelloJNI_unimplementedStringFromJNI
    24   (JNIEnv *, jobject);
    25 
    26 #ifdef __cplusplus
    27 }
    28 #endif
    29 #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, software
    11  * 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 and
    14  * 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 method
    22  * to return a new VM String. See the corresponding Java source
    23  * file located at:
    24  *
    25  *   apps/samples/hello-jni/project/src/com/example/HelloJni/HelloJni.java
    26  */
    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, software
    10 # 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 and
    13 # limitations under the License.
    14 #
    15 LOCAL_PATH := $(call my-dir)
    16 
    17 include $(CLEAR_VARS)
    18 
    19 LOCAL_MODULE    := HelloJNI
    20 LOCAL_SRC_FILES := HelloJNI.c
    21 
    22 include $(BUILD_SHARED_LIBRARY)
    复制代码

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

    $ndk-build 
    Compile thumb  : HelloJNI <= HelloJNI.c
    SharedLibrary  : libHelloJNI.so
    Install        : libHelloJNI.so => libs/armeabi/libHelloJNI.so

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

    6. 测试

      7. 参考资料

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

     

     
  • 相关阅读:
    C# 如何保证对象线程内唯一:数据槽(CallContext)【转载】
    关于面试!(简历篇)
    以Windows服务方式运行ASP.NET Core程序【转载】
    .Net Core 项目在Windows服务中托管【转载】
    【转载】Ocelot网关的路由热更新
    Kafka的配置文件详细描述
    C#枚举中的位运算权限分配浅谈
    CSS 三角形
    Entity Framework 7 动态 DbContext 模型缓存 ModelCaching
    ASP.NET5 MVC6 利用Middleware 创建可访问HttpContext 的业务类工厂。(代替HttpContext.Current)
  • 原文地址:https://www.cnblogs.com/blackcatx/p/6001514.html
Copyright © 2020-2023  润新知