package com.zhang.jni.test;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
public class JNITESTActivity extends Activity {
private static final String Tag = "ZhangFang_JNITEST";
private native void nativeMethod();
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
callNativeMathod();
}
/**
*
*/
private void callNativeMathod()
{
Log.v(Tag,"Activity call Native Mehotd:");
nativeMethod();
}
/**
*
*/
private void callBack()
{
Log.v(Tag,"Native method call the Activity methods");
Log.v(Tag,"****************Native Method call back successfully!********");
}
static {
System.loadLibrary("InstanceMethodCall");
}
}
2、把java源码复制到$(Android)/external/路径下
cp -Rf JNI ../Android/source/external/
3、进入$(Android)/external/JNI/bin目录,执行javah com.zhang.Android.JNI.JNIActivity,在bin目录下得到native头文件,这里包含有nativeMethod()接口的C/C++声明
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_zhang_jni_test_JNITESTActivity */
#ifndef _Included_com_zhang_jni_test_JNITESTActivity
#define _Included_com_zhang_jni_test_JNITESTActivity
#ifdef __cplusplus
extern "C" {
#endif
/*
* Class: com_zhang_jni_test_JNITESTActivity
* Method: nativeMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_zhang_jni_test_JNITESTActivity_nativeMethod
(JNIEnv *, jobject);
#ifdef __cplusplus
}
#endif
#endif
注意:在2.3.4源码下,javah在/external/JNITEST/bin/classes目录下执行,本例中得到com_zhang_jni_test_JNITESTActivity.h头文件,如上所示。javah执行的文件要用.号隔开,而不是/隔开。
#include "com_zhang_jni_test_JNITESTActivity.h"
#include <utils/Log.h>
#define LOG_TAG "JNI_C"
/*
* Class: com_zhang_jni_test_JNITESTActivity
* Method: nativeMethod
* Signature: ()V
*/
JNIEXPORT void JNICALL Java_com_zhang_jni_test_JNITESTActivity_nativeMethod
(JNIEnv *env, jobject obj)
{
LOGI("JNI IN nativeMethod");
jclass cls=(*env)->GetObjectClass(env,obj);
static jmethodID mid = NULL;
if(mid == NULL){
mid=(*env)->GetMethodID(env,cls,"callBack","()V");
if(mid == NULL){
return;
}
}
(*env)->CallVoidMethod(env,obj,mid);
}
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
void *venv;
LOGI("JNI_OnLoad!");
if ((*vm)->GetEnv(vm, (void**)&venv, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed");
return -1;
}
return JNI_VERSION_1_4;
}
当java层调用System.loadLibrary加载完JNI动态库之后,紧接着就会查找该库中一个叫JNI_OnLoad函数。如果存在,就调用它,动态注册的工作在这里完成。本文使用的是静态注册,但是好像也必须把该方法加上。就算静态注册没有要求写JNI_OnLoad函数,但是建议最好还是写上,因为有一些初始化工作是可以在这个函数里面完成的。
jmethodID (JNICALL *GetMethodID)
(JNIEnv *env, jclass clazz, const char *name, const char *sig);
clazz代表java类,name表示成员函数的名字,sig表示函数的返回值。
5、在此jni目录下编写Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE_TAGS := optional
LOCAL_SRC_FILES:= com_zhang_jni_test_JNITESTActivity.c
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
LOCAL_MODULE := libInstanceMethodCall
LOCAL_SHARED_LIBRARIES := libutils
LOCAL_PRELINK_MODULE := false
include $(BUILD_SHARED_LIBRARY)
注意 LOCAL_MODULE_TAGS := optional必须添加,否则会报错。 LOCAL_MODULE := libInstanceMethodCall. 如果不加lib,则会生成InstanceMethodCall.so,编译信息如下所示。
system/core/include/cutils/log.h:68:1: warning: this is the location of the previous definition
target SharedLib: InstanceMethodCall (out/target/product/omap3evm/obj/SHARED_LIBRARIES/InstanceMethodCall_intermediates/LINKED/InstanceMethodCall.so)
target Non-prelinked: InstanceMethodCall (out/target/product/omap3evm/symbols/system/lib/InstanceMethodCall.so)
target Strip: InstanceMethodCall (out/target/product/omap3evm/obj/lib/InstanceMethodCall.so)
Install: out/target/product/omap3evm/system/lib/InstanceMethodCall.so
但是我们需要的是libInstanceMethodCall.so,所以必须写成 LOCAL_MODULE := libInstanceMethodCall
target SharedLib: libInstanceMethodCall (out/target/product/omap3evm/obj/SHARED_LIBRARIES/libInstanceMethodCall_intermediates/LINKED/libInstanceMethodCall.so)
target Non-prelinked: libInstanceMethodCall (out/target/product/omap3evm/symbols/system/lib/libInstanceMethodCall.so)
target Strip: libInstanceMethodCall (out/target/product/omap3evm/obj/lib/libInstanceMethodCall.so)
Install: out/target/product/omap3evm/system/lib/libInstanceMethodCall.so
make:离开目录“/home/zhangfang/zf/DM3730/source/android_source/TI_Android_GingerBread_2_3_4Sources”
6、编译
进入$(Anroid/source)
mmm external/JNITEST/jni/
编译信息如上所示。
如果需重新打包系统文件,则执行 make snod.
如果不需要,则把生成的libInstanceMethodCall.so 复制到开发板/system/lib目录下,这样当程序启动需要加载库的时候,就会在此目录下寻找。
7、安装程序
adb install JNITEST.apk
8、adb shell
logcat查看日志
调用成功
I/ActivityManager( 1228): Start proc com.zhang.jni.test for activity com.zhang.jni.test/.JNITESTActivity: pid=1931 uid=10032 gids={}
D/dalvikvm( 1931): Debugger has detached; object registry had 1 entries
I/JNI_C ( 1931): JNI_OnLoad!
V/ZhangFang_JNITEST( 1931): Activity call Native Mehotd:
I/JNI_C ( 1931): JNI IN nativeMethod
V/ZhangFang_JNITEST( 1931): Native method call the Activity methods
V/ZhangFang_JNITEST( 1931): ****************Native Method call back successfully!********
I/ActivityManager( 1228): Displayed com.zhang.jni.test/.JNITESTActivity: +145ms
D/dalvikvm( 1311): GC_EXPLICIT freed 14K, 47% free 3078K/5767K, external 3115K/3867K, paused 25ms
9、实现JNI的动态注册
因为静态注册具有以下缺点:
(1)需要编译所有声明的native函数的java类,每个生成的class文件都的用javah生成头文件,麻烦。
(2)javah生成的头文件特别长,书写不方便。
(3)初次调用native函数时要根据函数名字搜索对应JNI层函数来建立关联关系,运行效率较低。
所以下面尝试动态加载。Android.mk文件不变,只替换原理jni目录下的.h和.c文件。
#include "jni.h"
#include "JNIHelp.h"
#include <utils/Log.h>
const char* className = "com/zhang/jni/test/JNITESTActivity";
void JNI_nativeMethod(JNIEnv *env,jobject obj)
{
LOGI("JNI IN nativeMethod");
jclass cls=(*env)->GetObjectClass(env,obj);
static jmethodID mid = NULL;
if(mid == NULL){
mid=(*env)->GetMethodID(env,cls,"callBack","()V");
if(mid == NULL){
return;
}
}
(*env)->CallVoidMethod(env,obj,mid);
}
static JNINativeMethod gMethods[] = {
{"nativeMethod","()V",(void *)JNI_nativeMethod},
};
static int registerNativeMethods(JNIEnv* env,const char* className,JNINativeMethod* gMethods,int numMethods)
{
jclass clazz;
clazz = (*env)->FindClass(env,className);
if(clazz == NULL){
return JNI_FALSE;
}
if((*env)->RegisterNatives(env,clazz,gMethods,numMethods)<0){
return JNI_FALSE;
}
return JNI_TRUE;
}
static int register_android_methods(JNIEnv *env)
{
return registerNativeMethods(env,className,gMethods,sizeof(gMethods)/sizeof(gMethods[0]));
}
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
JNIEnv* env = NULL;
LOGI("JNI_OnLoad!");
if ((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed");
return -1;
}
if(register_android_methods(env)<0){
LOGE("register_android_methods error.\n");
return -1;
}
return JNI_VERSION_1_4;
}
10、c++ JNI的使用
和JNI调用本地C不同,C++的JNI函数与C 有些不一样,需要查看JNI.h头文件去查看参数。Android.mk中的LOCAL_SRC_FILES 也必须为.cpp文件。
删除安装的程序 adb -s emulator-5554 uninstall com.zhang.Android.JNICPP
#define TAG "_JNICPP_"
#include <utils/Log.h>
jmethodID mid;
JNIEXPORT void JNICALL Java_com_zhang_Android_JNICPP_JNICPP_nativeCppMethod
(JNIEnv *env, jobject obj)
{
LOGI("In C\n");
env->CallVoidMethod(obj,mid);
}
JNIEXPORT void JNICALL Java_com_zhang_Android_JNICPP_JNICPP_initIDs
(JNIEnv *env, jclass cls)
{
mid=env->GetMethodID(cls,"callback","()V");
}
static const char *classPathName="com/zhang/Android/JNICPP/JNICPP";
static JNINativeMethod methods[]={
{"nativeCppMethod","()V",(void *)Java_com_zhang_Android_JNICPP_JNICPP_nativeCppMethod},
tiveCppMethod},
};
static int registerNativeMethods(JNIEnv *env,const char *className,JNINativeMethod* gMethods,int numMethods)
{
jclass clazz;
clazz=env->FindClass(className);
if(clazz==NULL){
LOGE("Native registeration unable to find class %s",className);
return JNI_FALSE;
}
if(env->RegisterNatives(clazz,gMethods,numMethods)<0){
LOGE("RegisterNatives failed for %s",className);
return JNI_FALSE;
}
return JNI_TRUE;
}
static int registerNatives(JNIEnv *env)
{
if(!registerNativeMethods(env,classPathName,methods,sizeof(methods)/sizeof(methods[0]))){
return JNI_FALSE;
}
return JNI_TRUE;
}
typedef union{
JNIEnv *env;
void * venv;
}UnionJNIEnvToVoid;
/* This function will be call when the library first be loaded */
jint JNI_OnLoad(JavaVM* vm, void* reserved)
{
UnionJNIEnvToVoid uenv;
JNIEnv* env = NULL;
LOGI("JNI_OnLoad!");
if (vm->GetEnv((void**)&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed");
return -1;
}
env = uenv.env;;
if (registerNatives(env) != JNI_TRUE) {
LOGE("ERROR: registerNatives failed");
return -1;
}
return JNI_VERSION_1_4;
}