android jni 编程

1.引言
我们知道,Android系统的底层库由c/c++编写,上层Android应用程序通过Java虚拟机调用底层接口,衔接底层c/c++库与Java应用程序间的接口正是JNI(JavaNative Interface)。本文描述了如何在ubuntu下配置AndroidJNI的开发环境,以及如何编写一个简单的c函数库和JNI接口,并通过编写Java程序调用这些接口,最终运行在模拟器上的过程。
2.环境配置
2.1.安装jdk1.6
2.2.安装android应用程序开发环境 eclipse adt sdk
2.3.安装NDK
NDK是由android提供的编译android本地代码的一个工具。1)从androidndk官网http://wear.techbrood.com/tools/sdk/ndk/
(2)解压ndk到工作目录:
(3)设置ndk环境变量

exportPATH=/usr/local/ndk:$PATH

3.JNI实现
我们需要定义一个符合JNI接口规范的c/c++接口,这个接口不用太复杂,例如输出一个字符串。接下来,则需要把c/c++接口的代码文件编译成共享库(动态库).so文件,并放到模拟器的相关目录下。最后,启动Java应用程序,就可以看到最终效果了。
3.1.编写Java应用程序代码

(1)启动Eclipse,新建android工程

Project:JNITest

Package:org.tonny.jni(注意包名不能带“_”符号)

Activity:JNITest

(2)编辑资源文件

编辑res/values/strings.xml文件如下:

    <?xmlversionxmlversion="1.0"encoding="utf-8"?>  
    <resources>  
    <stringnamestringname="hello">HelloWorld, JNITestActivity!</string>  
    <stringnamestringname="app_name">JNITest</string>  
    <stringnamestringname="btn_show">Show</string>  
    </resources>  

编辑res/layout/main.xml文件如下:

<?xmlversionxmlversion="1.0"encoding="utf-8"?>  
<LinearLayoutxmlns:androidLinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"  
android:layout_width="fill_parent"  
android:layout_height="fill_parent"  
android:orientation="vertical">  
<TextView  
android:layout_width="fill_parent"  
android:layout_height="wrap_content"  
android:text="@string/hello"/>  
<EditText  
android:id="@+id/ed_name"  
android:layout_width="match_parent"  
android:layout_height="wrap_content"  
android:layout_gravity="center_horizontal"  
android:layout_marginLeft="5dp"  
android:layout_marginRight="5dp"/>  
<Button  
android:id="@+id/btn_show"  
android:layout_width="109dp"  
android:layout_height="wrap_content"  
android:layout_gravity="center_horizontal"  
android:text="@string/btn_show"/>  
</LinearLayout> 

我们在主界面上添加了一个EditText控件和一个Button控件。
(3)编辑JNITest.java文件

package com.example.jnitest;

import com.example.jnitest_1.R;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;

public class JNITest extends Activity {
    static{  
        System.loadLibrary("JNITest");  
        } 

    public native String GetReply();  
    private EditText edtName;  
    private Button btnShow;  
    String reply;

    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.main);  
        reply= GetReply();  
        edtName = (EditText)this.findViewById(R.id.ed_name);  
        btnShow = (Button)this.findViewById(R.id.btn_show);  
        btnShow.setOnClickListener(new View.OnClickListener() {  

        public void onClick(View v) {  
            edtName.setText(reply);  
            }  
        }); 
    }
}

我们看这一段代码:

    static{  
    System.loadLibrary("JNITest");  
    }  

static表示在系统第一次加载类的时候,先执行这一段代码,在这里表示加载动态库libJNITest.so文件。
再看这一段:

private native String GetReply();

native表示这个方法由本地代码定义,需要通过jni接口调用本地c/c++代码。
(4)编译工程,生成.class文件。
3.2.用javah工具生成符合JNI规范的c语言头文件
在终端中,进入android工程所在的bin目录

$cd ~/project/Android/JNITest/bin 

我们用ls命令查看,可以看到bin目录下有个classes目录,其目录结构为classes/org/tonny/jni,即classes的子目录结构是android工程的包名org.tonny.jni。请注意,下面我们准备执行javah命令的时候,必须进入到org/tonny/jni的上级目录,即classes目录,否则javah会提示找不到相关的java类。

下面继续:

$cd classes  
$javah org.tonny.jni.JNITest  
$ls  
org org_tonny_jni_JNITest.h

执行javahorg.tonny.jni.JNITest命令,在classes目录下会生成org_tonny_jni_JNITest.h头文件。如果不进入到classes目录下的话,也可以这样:

$javah -classpath ~/project/Android/JNITest/bin/classesorg.tonny.jni.JNITest  

-classpath 参数表示装载类的目录。

javah 编译时,会报错,Error: cannot access android.app.Activity
class file for android.app.Activity not found
原因:不认识android.app.Activity
解决方法:在CLASSPATH中添加sdk中的android.jar路径,就可以了!

export SDK_HOME=~/tool/adt-bundle-linux-x86_64-20140321/sdk/platforms/android-19
export 

CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib:${SDK_HOME}/android.jar

3.3.编写c/c++代码

生成org_tonny_jni_JNITest.h头文件后,我们就可以编写相应的函数代码了。下面在android工程目录下新建jni目录,即~/project/Android/JNITest/jni,把org_tonny_jni_JNITest.h头文件拷贝到jni目录下,并在jni目录下新建org_tonny_jni_JNITest.c文件,编辑代码如下:

    #include<jni.h>  
    #include<string.h>  
    #include"org_tonny_jni_JNITest.h"  


    JNIEXPORTjstring JNICALLJava_org_tonny_jni_JNITest_GetReply  
    (JNIEnv *env, jobject obj){  
    return(*env)->NewStringUTF(env,(char*)"Hello,JNITest");  
    }  

我们可以看到,该函数的实现相当简单,返回一个字符串为:”Hello,JNITest”
3.4.编写Android.mk文件

在~/project/Android/JNITest/jni目录下新建Android.mk文件,android可以根据这个文件的编译参数编译模块。编辑Android.mk文件如下:

LOCAL_PATH:= $(call my-dir)  
include $(CLEAR_VARS)  
LOCAL_MODULE := libJNITest  
LOCAL_SRC_FILES:= org_tonny_jni_JNITest.c  
include $(BUILD_SHARED_LIBRARY)  

在使用NDK之初遇到这个一个问题,Android.mk文件我自己编写一遍,没想在编译过程中一直编译不过,报错如下:
Android.mk:3: * missing separator. Stop.

报错提示:提示缺省分隔符
于是找了好一会儿才找到问题原因所在:
$符号前面必须加一个空格。

“No rule to make target”
Android.mk 写的一定有问题。空格之类的问题

LOCAL_MODULE表示编译的动态库名称

LOCAL_SRC_FILES 表示源代码文件
3.5.用ndk工具编译并生成.so文件

进入到JNITest的工程目录,执行ndk-build命令即可生成libJNITest.so文件。
$cd ~/project/Android/JNITest/  
$ndk-build  
Invalidattribute name:  
package  
Install : libJNITest.so => libs/armeabi/libJNITest.so  
可以看到,在工程目录的libs/armeabi目录下生成了libJNITest.so文件。
3.6.在模拟器上运行

(1)首先,我们把android模拟器启动起来。进入到emulator所在目录,执行emulator命令:
$cd ~/software/android/android-sdk-linux/tools  
$./emulator @AVD-2.3.3-V10 -partition-size 512  
2)接下来,我们需要把libJNITest.so文件拷贝到模拟器的/system/lib目录下,执行以下命令:
$cd ~/project/Android/JNITest/libs/armeabi/  
$adb remount  
$adb push libJNITest.so /system/lib  
80 KB/s (10084 bytes in 0.121s) 

“`
当在终端上看到有80 KB/s (10084 bytes in 0.121s)传输速度等信息的时候,说明拷贝成功。
(3)在终端上执行JNITest程序

在这里我还要补充下:
在我们开发过程中,改一个c/c++的文件,我们都要手动去编译一下有点儿麻烦。这里我们可以使用让eclipse帮助我们自己编译。

右击app工程的properties–>Builders–>NEW –>;Program 可以看到以下内容:

这里写图片描述

ubutun环境下第一个填的是/bin/bash
第二个空白填的是/bin
第三个空白填
–login -c “cd /home/eclipse_workspace/JNItest && $NDK ndk-build”

接着切换到Refresh 标签页
这里写图片描述

接着切换到Build Options标签页
这里写图片描述

这样就完成了配置,点击确定可看到控制台自动编译程序了
这里写图片描述

4.参考文章
http://blog.youkuaiyun.com/xnwyd/article/details/7086384

http://bbs.51cto.com/thread-948244-1.html###zoom

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值