Android——JNI On Android

1.为什么使用JNI
    很长时间我就想写关于这方面的东西,其实弄android已经有段时间了,虽然不太喜欢它,但是还是有然我兴奋的地方,那就是JNI,因为那里有我熟悉的C和LInux。JNI简单的说就是一种能够让你在Java里使用C/C++代码的一种技术。
    在Android里,Java代码运行在Java虚拟机里(应该是叫做Dalvik),这个虚拟机可以说是java世界的神,但同时也是拖android世界后退的家伙。但是android里你可以有另一种选择,那就是JNI技术,它可以让你在高速公路上尽情的玩耍,而不是乡间小路上。
   这种机制允许你将耗时的工作使用C/C++来完成,剩余的部分使用Java,使用JNI来完成Java对C/C++的调用。
2.如何使用JNI
    首先,你应该很明确,你为什么使用JNI,怎么学习JNI,当然互联网上有许多这样的教学,包括android NDK的实例。
    JNI的基本思想很简单,你可以写一个C/C++文件,然后导出里面的方法,然后在Java的文件里进行调用这些方法。虽然很简单,但是为了完成这样的任务,你需要遵守一些约定。
   一个好的开始就是通过一个简单的工程来演示这样技术:
首先是简单的java代码:(通用Activity在屏幕上写一些字符 )
package com.Hello;
import android.app.Activity;
import android.os.Bundle;
import android.widget.TextView;
import android.util.Log;
public class Hello extends Activity 
{    
static 
{
 try {
 Log.i("JNI", "Trying to load libHello.so");
 System.loadLibrary("Hello");
 }
 catch (UnsatisfiedLinkError ule)
 {
 Log.e("JNI", "WARNING: Could not load libHello.so");
 }
 }    // Important part : this method is native, as in imported from C++     
native private int add(int a, int b);
 @Override    
public void onCreate(Bundle savedInstanceState) 
{        
super.onCreate(savedInstanceState);
 // Call simple native method 
       int res = add(3,5);
 // Just print the result on screen
 TextView tv = new TextView(this);
 tv.setText("C++ tells you that 3+5 = " + res);
 setContentView(tv);
 }
}
代码很简洁,这里有两个新的东西一个是System.loadLibrary(“Hello”),它的意思就是加载Hello.so库;另个就是native关键字,
这个关键字,告诉Java这里的add方法在额外的原生库中查找。
将上面的文件保存为Hello.java文件。Android.mk文件内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := samples
LOCAL_PACKAGE_NAME := Hello
LOCAL_SRC_FILES := Hello.java
LOCAL_SDK_VERSION := current
include $(BUILD_PACKAGE)
另外AndroidManifest.xml文件内容如下:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.Hello">
 <application android:label="Hello">
 <activity android:name="Hello">
 <intent-filter>
 <action android:name="android.intent.action.MAIN"/>
 <category android:name="android.intent.category.LAUNCHER"/>
 </intent-filter>
 </activity>
 </application>
</manifest>
接下来放这3个文件到同一个目录,然后设置android编译环境如:(source build/envsetup.sh),之后在该目录下执行mm,这样你的
Hello.apk就编译好了。
接下来就是C++语言代码:
#define LOG_TAG "Hello"
#include "utils/Log.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include "jni.h"
/* * Trivial method : add two numbers * */
static jint Hello_add(JNIEnv* env, jobject thiz, jint a, jint b)
{    
return (jint)(a + b);
}
/* * Array of methods. * * Each entry has three fields: the name of the method, the method * signature,
 and a pointer to the native implementation. */
static const JNINativeMethod gMethods[] = {
 { "add", "(II)I", (void*)Hello_add }
};
/* * Explicitly register all methods for our class. * * Returns 0 on success. */
static int registerMethods(JNIEnv* env)
 {
 static const char* const kClassName = "com/Hello/Hello";
 jclass clazz;    /* look up the class */
 clazz = env->FindClass(kClassName);
 if (clazz == NULL) {
 LOGE("Can't find class %s\n", kClassName);
 return -1;
 }
 /* register all the methods */
 if (env->RegisterNatives(clazz, gMethods,            sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK)
 {
 LOGE("Failed registering methods for %s\n", kClassName);
 return -1;
 } 
   return 0;
}
/* * This is called by the VM when the shared library is first loaded. */
jint JNI_OnLoad(JavaVM* vm, void* reserved)
 {
 JNIEnv* env = NULL;
 jint result = -1;
 if (vm->GetEnv((void**) &env, JNI_VERSION_1_4) != JNI_OK)
 {
 LOGE("ERROR: GetEnv failed\n");
 goto bail;
 }
 assert(env != NULL);
     if (registerMethods(env) != 0)
 { 
       LOGE("ERROR: native registration failed\n");
 goto bail;
 }
 /* success -- return valid version number */ 
   result = JNI_VERSION_1_4;
bail:    return result;
}
   这里关于add函数很简单,相加后返回和。JNI的约定意味着该函数的前两个参数(JNIEnv是一个与线程相关的变量,实际上就是提供一
些JNI系统函数,通过这些函数可以调用Java的函数,操作jobject)隐示从Java中传过来,Java代码仅仅调用 add(a,b)。
   然后你需要声明这个方法在这个数组里面,当然这里有些奇怪的标签相关的内容可以看这里:http://docs.oracle.com/javase/1.5.0/docs/guide/jni/
spec/types.html#wp16432。JNINativeMethod类中,地一个变量为函数的名称,第二个参数为参数和返回值的说明,第三个是JNI层对应
的函数指针。这个数组最后通过这个类型的注册方法RegisterNatives注册到JNI层中。注册的时候首先通过它的名字(kClassName),获
得它的类型名(clazz)。
   当然这里还有一个问题,就是add方法在什么时候,什么地方被动态调用注册的呢?答案是:当Java层通过System.loadLibary加载JNI
动态库后,紧接着查找一个叫做JNI_OnLoad的函数。如果有,就调用它,而动态注册工作就是在这里完成的也就是上面源码中JNI_OnLoad的
作用。
同样,Android.mk文件的内容如下:
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := samples
LOCAL_MODULE:= libHello
LOCAL_SRC_FILES:= Hello.c
# Additional libraries, maybe more than actually needed
LOCAL_SHARED_LIBRARIES := \
 libandroid_runtime \
 libnativehelper \
 libcutils \
 libutils
# JNI headers
LOCAL_C_INCLUDES += $(JNI_H_INCLUDE)
LOCAL_PRELINK_MODULE := falseinclude $(BUILD_SHARED_LIBRARY)
将这两文件放在同一个文件夹下面,然后运行mm,就生成libHello.so文件。
3.运行:
  首先安装Hello.apk到系统中,然后复制libHello.so到/system/lib目录下,或者使用System.load()代替System.loadLibrary(),
通过指定完全路径,你可以将库放到任何你想放的目录下。前提是玩过Android我想你懂得!
后继:1.上面是通过动态方法进行的JNI的调用,当然相对于动态还有一种就是静态,静态方法大致就先编译java代码,生成.class文件,然后
使用javac -o output packagename.classname,这样就生成一个output.h的JNI层头文件,只要实现里面的对应的函数即可
     2.另外在编译的时候,所放的目录是在android源码的某一个目录下,当然make,mmm都是可以的,make就是全编译,比较慢,mmm会
将依赖全部编译,当然也不错。
注:代码翻译自http://www.upche.org/doku.php?id=wiki:jni

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值