Android Eclipse JNI开发示例

转自博客《 android下NDK开发环境搭建及TestJNI入门实例完整过程 》 

http://blog.csdn.net/adaixiaoxiao/article/details/51822744

前提:在搭建Eclipse和NDK的开发环境基础上进行JNI开发。


1.创建Android工程
这里写图片描述
2首先建立一个名为TestJni的Android工程,包名默认为com.example.hellojni,src目录下自动创建MainActivity.java。
设计JNI接口,新建一个java文件,TestJni.java.如图
这里写图片描述
—工程目录图
TestJni.java代码如下

package com.example.hellojni;

public class TestJni {
    public native boolean init();
    public native int add(int x,int y);
    public native void destory();
}
 
 
  • 1

3.编译JNI
在E:\workplace\TestJni\bin\classes\com\example\hellojni文件夹下会生成TestJni.class文件。
这里写图片描述

在bin文件夹下,如果没有则创建目录:/com/example/jni,并把TestJNI.class复制到/bin/com/example/jni目录下。然后在终端里进入工程的bin目录,输入javah -jni com.example.jni.TestJNI,此时会生成一个com_example_jni_TestJNI.h文件。

注意:我在输入javah -jni com.example.jni.TestJNI的命令时会出错,改为
javah -classpath E:/software_tool/android/adt-bundle-windows-x86_64-20140702/sdk/platforms/android-20/android.jar;bin/classes -d jni com.example.hellojni.TestJni

这里写图片描述

我通过测试在bin文件夹下输入javah -jni com.example.jni.TestJNI的命令编译OK的,可以生成com_example_hellojni_TestJni.h,然后将com_example_hellojni_TestJni.h拷贝到项目的jni文件夹下,如果没有jni文件夹,自动新建即可。


4.用C/C++实现JNI
有了JNI的C/C++头文件,就可以在C层实现JNI接口了。首先在工程目录下创建一个jni目录,这个目录就是专门用来放C/C++代码的。把com_example_hellojni_TestJni.h文件复制到jni目录下,并在这里创建一个com_example_jni_TestJni.cpp文件。

由于我想用C++来实现JNI,所以上面两个文件我只是用来作为动态链接库的接口,具体的实现我希望放在一个类里面来完成,因此我再添加两个文件:Add.h和Add.cpp。如上操作结果见工程目录图

com_example_hellojni_TestJni.h

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_hellojni_TestJni */

#ifndef _Included_com_example_hellojni_TestJni
#define _Included_com_example_hellojni_TestJni
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_example_hellojni_TestJni
 * Method:    init
 * Signature: ()Z
 */
JNIEXPORT jboolean JNICALL Java_com_example_hellojni_TestJni_init
  (JNIEnv *, jobject);

/*
 * Class:     com_example_hellojni_TestJni
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_hellojni_TestJni_add
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     com_example_hellojni_TestJni
 * Method:    destory
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_hellojni_TestJni_destory
  (JNIEnv *, jobject);

#ifdef __cplusplus
}
#endif
#endif

 
 
  • 1

com_example_hellojni_TestJni.cpp

#include <stdio.h>
#include <stdlib.h>
#include "com_example_hellojni_TestJni.h"
#include "Add.h"

CAdd *pCAdd = NULL;
JNIEXPORT jboolean JNICALL Java_com_example_hellojni_TestJni_init(JNIEnv *env,jobject obj) {
   if (pCAdd == NULL) {
        pCAdd = new CAdd;
    }
   return pCAdd != NULL;
}
JNIEXPORT jint JNICALL Java_com_example_hellojni_TestJni_add(JNIEnv *env, jobject obj,
        jint x, jint y) {
        int res = -1;
       if (pCAdd != NULL) {
          res = pCAdd->add(x, y);
        }
       return res;
}
JNIEXPORT void JNICALL Java_com_example_hellojni_TestJni_destory(JNIEnv *env, jobject obj)
{    if (pCAdd != NULL)
    {
        pCAdd = NULL;
    }
}
 
 
  • 1

Add.cpp

#include "Add.h"
 CAdd::CAdd() {
 }
 CAdd::~CAdd() {
 }
 int CAdd::add(int x, int y) {
 return x + y;
}
 
 
  • 1

Add.h

#ifndef _TEST_JNI_ADD_H_
#define _TEST_JNI_ADD_H_
class CAdd {
    public:
        CAdd();
        ~CAdd();
        int add(int x,int y);
};
#endif

 
 
  • 1

到此C/C++代码实现结束

5、创建Android.mk
JNI实现了之后就要把C/C++代码编译成动态链接库.so文件,这样Java程序才能调用JNI的接口。要编译so文件,需要写Android.mk和Application.mk两个文件。我们先来写Android.mk。

先在工程目录的jni下创建一个Android.mk文件:

LOCAL_PATH := $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE := TestJni
LOCAL_SRC_FILES := com_example_hellojni_TestJni.cpp
LOCAL_SRC_FILES += Add.cpp
include $(BUILD_SHARED_LIBRARY)
 
 
  • 1

其中LOCAL_PATH是C/C++代码所在目录,也就是我们的jni目录。
LOCAL_MODULE是要编译的库的名称。编译器会自动在前面加上lib,在后面加上.so。

LOCAL_SRC_FILES是要编译的C/C++文件。

现在我们在工程的根目录下创建一个Application.mk文件,并输入如下内容:

APP_PROJECT_PATH := $(call my-dir)
APP_MODULES := TestJni
 
 
  • 1

6、编译动态链接库(.so文件) 

默认在Windows7下配置好了NDK开发环境,在cmd窗口切换到hellojni\jni目录下,执行ndk-build


如何有错误会提示,编译成功后会在工程目录的libs/armeabi目录下生成一个libTestJni.so文件。

7 在Java中调用JNI

现在我们的Android应用可以调用JNI计算加法的代码,如下:
MainActivity.java

package com.example.hellojni;

import android.support.v7.app.ActionBarActivity;
import android.app.Activity;
import android.app.SearchManager.OnCancelListener;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;


public class MainActivity extends Activity{

    static{
        System.loadLibrary("TestJni");
    }
    EditText tvX = null;
    EditText tvY = null;
    EditText tvSum = null; 
    Button   btnAdd = null;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        tvX = (EditText)findViewById(R.id.et_x);
        tvY = (EditText)findViewById(R.id.et_y);
        tvSum = (EditText)findViewById(R.id.et_sum);
        btnAdd = (Button)findViewById(R.id.btn_add);
        btnAdd.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                int x = Integer.valueOf( tvX.getText().toString());
                int y = Integer.valueOf( tvY.getText().toString());
                int sum = 0;
                TestJni jni = new TestJni();
                boolean flag = jni.init();
                if(flag){
                sum = jni.add(x, y);
                }
                tvSum.setText(String.valueOf(sum));
            }
        });
    }





}
 
 
  • 1

8.XML文件(activity_main.xml)

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    >

    <EditText
        android:id="@+id/et_x"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
         >

    </EditText>

    <TextView
        android:id="@+id/tv1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"        
        android:text="+" >

     </TextView>
      <EditText
        android:id="@+id/et_y"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
         >
      </EditText>
      <Button 
          android:id = "@+id/btn_add"
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="="
          />
       <EditText
        android:id="@+id/et_sum"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
         >

      </EditText>

</LinearLayout>

 
 
  • 1

程序运行结果如图
这里写图片描述

总结一下JNI开发基本步骤
①编写.java类
②生成.class文件
③生成.h文件
③查阅.h文件
  JNIEXPORT 和 JNICALL 是jni的宏
  函数前的注释Signature: ()Ljava/lang/String;中的括号含义为:表示函数的参数为空,Ljava/lang/String表示函数的返回值是java的String对象
④编写.c/cpp文件
⑤创建 Android.mk文件(和.c文件同级目录)
⑥编译生成.so文件
这里写图片描述

NDK开发中的一些常见错误及解决办法

一、常见的几个错误及其解决办法
1. android.mk文件不存在
错误代码形如:
AndroidNDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk
/cygdrive/h/heima6/jni2/ziliao/android-ndk-r7b/build/core/add-application.mk:133:* Android NDK: Aborting… 。 停止。
解决办法:
创建Android.mk文件。

2.android.mk文件的配置信息有错误
错误代码形如:
***Android NDK: Missing LOCAL_MODULE before including BUILD_SHARED_LIBRARY injni/Android.mk 。 停止。
解决办法:
修改Android.mk文件中的错误。有时候可能是一些看不见的特色字符导致,可以删除后重新建立一个。

3.c代码语法出现错误,编译不通过Error1.
错误代码形如:
make:* [obj/local/armeabi/objs/Hello/Hello.o]Error 1
解决办法:
检查C程序代码

4.java层c代码库没有找到
错误代码形如:
Causedby: java.lang.UnsatisfiedLinkError:Library Hell0 not found静态加载代码库的时候代码库没有找到.(调用库时出错)
解决办法:
检查库库的名字,看是否写错了。看

5.c代码函数签名出现错误(函数名出错)
错误代码形如:
Causedby: java.lang.UnsatisfiedLinkError: hello_from_c
解决办法:
C代码中函数名不合特定规范,改过来就行了。

  1. 其他隐含错误
    如:在C代码中视图去访问已经释放了的内存空间。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值