jni 初步学习

java 特点:一处编译,到处运行

windows 可执行文件后缀名 .:exe

linux 可执行文件后缀名:elf

需要在windows 平台下编译出linux 平台下可执行的二进制文件:交叉编译

交叉编译:在不同平台下午 ,模拟出另一个平台下的编译环境,编译出另一个平台下可执行的文件

在windows 操作系统下模拟出linux 系统的编译环境:cygwin

google 提供了一个最新版本的NDK (native develop kits)


-ndk 工具  编译工具链

nkd -build 把所有的编译环境的文件集合在一起。

-在path 系统环境变量中追加ndk-build 的文件夹目录


JNI 的开发流程

   1,在java代码中声明一个本地方法

   2,在工程目录下创建一个jni 文件夹,在当前文件夹 下创建一个c文件,对java中声明的native 方法进行实现,

#include <stdio.h>
#include <stdlib.h>
#include <jni.h>
//引入jni头文件 ,不引入则报错
//jni.h 中的JNINativeInterface 是一个结构体 JNIEnv 是结构体的指针  这里的env 是二级指针
//java_完整方法名(把。替换_)
//JNIEnv *env  JNINativeInterface 的二级指针,需要用到一些方法都定义在这了 (**env ).CallVoidMethod
//jobject obj  谁调用native ,当前obj就是那个类对象  当前是MainActivity 对象

jstring Java_com_example_jnihelloworld_MainActivity_sayHello(JNIEnv *env,jobject obj){
//c语言是没有字符串  NewStringUTF
	char * text="Hello World!!";
	return (**env).NewStringUTF(env,text);

}

  3,,在jni 目录下创建一个Android.mk 文件,用来编译使用的配置文件。

    LOCAL_PATH := $(call my-dir)


  include $(CLEAR_VARS)


  LOCAL_MODULE    := hello
  LOCAL_SRC_FILES := hello.c


  include $(BUILD_SHARED_LIBRARY)

  4,在工程的目录下执行 ndk_build 命令,此命令执行完毕之后,会在libs/armabi 目录下生成一个.so 文件

ndk-build

 5,,在java代码中.so 文件加载到内存中。

6,,调用native 方法,在调用nativie 方法时,系统会上面加载的.so 文件中找对应匹配方法.

7,修改完c之后,一定要重新编译.so 文件 ndk-build clean

-使用javah命令生成jni 头文件

 jdk 1.7 在工程的scr 下执行命令

   javah -jni com.demo.MainActivity

jdk 1.6 在工程bin/classes 目录下执行一下命令

   javah-jni com.demo.MainActivity


调用System.loadLibrary方法找不到.so 文件

  java.lang.UnsatisfieldLinkError:Con't loadcommonerror.so findLibrary returned null

导致此错误有两种情况

   .so 文件名写错了

   当前是x86的cpu 平台,默认ndk-build编译出来的是arm平台下

   解决方案: 在工程的jni 目录下,创建一个AppLication.mk 文件,制定以下内容

   APP_API:=ALL //兼容所有cpu 平台

jni 简单开发方式

   1,在java代码中声明native 方法

   2,在window->preferences->Android ->NDK 配置ndk的跟目录

   右键 ->android tools->add Androoid native Support 输入一个函数库的名字

实现上面声明native 方法

  javah 生成头文件  把.h文件拷贝到jni目录下,会报错,需要去配置Path and Symbols 

  右键工程Properties->C/C++ General ->paht and symblos ->Inlude ->add 把jni.h 的头文所在的文件夹填写进去。

  实现c代码

在java代码中加载以下库,调用native 


jni 的log 打印

#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <android/log.h>
#define LOG_TAG "cTag"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
JNIEXPORT jint JNICALL Java_com_example_aa_JNI_add(JNIEnv * env, jobject obj,
		jint x, jint y) {

	LOGD("x=%d,y=%d", x, y);
	int result = x + y;
	LOGD("result=%d", result);
	return result;
}


LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := aa
LOCAL_SRC_FILES := aa.c
LOCAL_LDLIBS :=-llog
include $(BUILD_SHARED_LIBRARY)


#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <android/log.h>
#define LOG_TAG "cTag"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
JNIEXPORT jint JNICALL Java_com_example_aa_JNI_add(JNIEnv * env, jobject obj,
		jint x, jint y) {

	LOGD("x=%d,y=%d", x, y);
	int result = x + y;
	LOGD("result=%d", result);
	return result;
}
//jstring to char*
char* _JString2CStr(JNIEnv* env, jstring jstr) {
	 char* rtn = NULL;
	 jclass clsstring = (*env)->FindClass(env, "java/lang/String");
	 jstring strencode = (*env)->NewStringUTF(env,"GB2312");
	 jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
	 jbyteArray barr = (jbyteArray)(*env)->CallObjectMethod(env, jstr, mid, strencode); // String .getByte("GB2312");
	 jsize alen = (*env)->GetArrayLength(env, barr);
	 jbyte* ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
	 if(alen > 0) {
		rtn = (char*)malloc(alen+1); //"\0"
		memcpy(rtn, ba, alen);
		rtn[alen]=0;
	 }
	 (*env)->ReleaseByteArrayElements(env, barr, ba,0);
	 return rtn;
}
JNIEXPORT jstring JNICALL Java_com_example_aa_JNI_sayHelloInc
  (JNIEnv * env, jobject obj, jstring jName){
    char * cName=_JString2CStr(env,jName);
   char * temp=" Hello From C";
   strcat(cName,temp);
   //需要把char * 类型转换成jstring 类型
   return  (**env).NewStringUTF(env,cName);
}
JNIEXPORT jintArray JNICALL Java_com_example_aa_JNI_arrElementsIncrease
  (JNIEnv * env, jobject obj, jintArray jArray){
	//获取数组的长度
   int length=(**env).GetArrayLength(env,jArray);
   //(JNIEnv*, jintArray, jboolean*);
   int *cIntArray = (**env).GetIntArrayElements(env,jArray,JNI_FALSE);
   int i;
   for(i=0;i<length;i++){
	  *(cIntArray+i)+=10;
   }
   return jArray;

}


package com.example.aa;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.View;
import android.widget.Toast;

public class MainActivity extends ActionBarActivity {

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
	}

	public void click(View view) {
		JNI jni = new JNI();
		int add = jni.add(22, 22);
		Toast.makeText(this, add + "", 1).show();
	}
    public void clickSayHello(View view){
	   JNI jni=new JNI();
	   String sayHelloInc = jni.sayHelloInc("zhangsan");
	   Toast.makeText(this, sayHelloInc, 1).show();
    }
    public void clickElementsIncrease(View view){
    	   JNI jni=new JNI();
    	   int[] arr={1,2,3,4,5,6};
    	   jni.arrElementsIncrease(arr);
    	   for (int i : arr) {
		      Log.i("zhouke", ":"+i);
		   }
    }
}


c回调java

    生成方法的签名,在工程bin/classes 下执行以下命令,

bin\classes> javah -s com.example.callback.JNI




代码如下:

package com.example.androidndk;

import android.util.Log;

public class JNI {
	static {
		System.loadLibrary("androidndk");
	}
   public void helloFromJava(){
	   Log.i("zhouke", "Hello From Java,called From C!!!!!! ");
   }
   public int add(int x,int y){
	   Log.i("zhouke", "x="+x+":y="+y);
	   int result=x+y;
	   return result;
   }
   public void printString(String s){
	   Log.i("zhouke", s); 
   }
   public static void sayHelloFromJva(){
	   Log.i("zhouke", "sayHelloFromJva static");
   }
   public native void callbackhelloFromJava();
   public native void callbackadd();
   public native void callbackprintString();
   public native void callbackStaticMethod();
}

package com.example.androidndk;

import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.View;


public class MainActivity extends ActionBarActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }

  public void helloFromJava(View view){
	  JNI jni=new JNI();
	  jni.callbackhelloFromJava();
  }
  public void add(View view){
	  JNI jni=new JNI();
	  jni.callbackadd();
  }
  public void printString(View view){
	  JNI jni=new JNI();
	  jni.callbackprintString();
  }
  public void callStatic(View view){
	  JNI jni=new JNI();
	  jni.callbackStaticMethod();
  }
  //定义本地方法,c端会实现这三个方法,并且回调三个方法
  public native void callbackhelloFromJava();
  public native void callbackadd();
  public native void callbackprintString();
}


#include "com_example_androidndk_JNI.h"
#include <stdlib.h>
#include <stdio.h>
#include <android/log.h>
#define LOG_TAG "cTag"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
JNIEXPORT void JNICALL Java_com_example_androidndk_JNI_callbackhelloFromJava
  (JNIEnv * env, jobject obj){
	//反射回调
		//得到类的字节码
		jclass clazz = (**env).FindClass(env, "com/example/androidndk/JNI");//需要把所有点替换成/

		//得到类中某个方法
		jmethodID methodId = (**env).GetMethodID(env, clazz, "helloFromJava","()V");	//

		//得到一个类的实例对象   jobject     (*AllocObject)(JNIEnv*, jclass);
		jobject jniInstance = (**env).AllocObject(env, clazz);

		//调用方法
		(**env).CallVoidMethod(env, jniInstance, methodId);	//成功调用jni 类中的
}

/*
 * Class:     com_example_androidndk_JNI
 * Method:    callbackadd
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_androidndk_JNI_callbackadd
  (JNIEnv * env, jobject obj){
	//得到类的字节码
		jclass clazz = (**env).FindClass(env, "com/example/androidndk/JNI");//需要把所有点替换成/
		//得到类中某个方法
		jmethodID methodId = (**env).GetMethodID(env, clazz, "add", "(II)I");	  //
		//得到一个类的实例对象   jobject     (*AllocObject)(JNIEnv*, jclass);
		jobject jniInstance = (**env).AllocObject(env, clazz);
		//    jint        (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
		jint result = (**env).CallIntMethod(env, jniInstance, methodId,12, 11);
		LOGD("result=%d", result);
}

/*
 * Class:     com_example_androidndk_JNI
 * Method:    callbackprintString
 * Signature: ()V
 */
JNIEXPORT void JNICALL Java_com_example_androidndk_JNI_callbackprintString
  (JNIEnv * env, jobject obj){
	//得到类的字节码
			jclass clazz = (**env).FindClass(env, "com/example/androidndk/JNI");//需要把所有点替换成/
			//得到类中某个方法
			jmethodID methodId = (**env).GetMethodID(env, clazz, "printString", "(Ljava/lang/String;)V");	  //
			//得到一个类的实例对象   jobject     (*AllocObject)(JNIEnv*, jclass);
			jobject jniInstance = (**env).AllocObject(env, clazz);
			  //void        (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
			jstring jstr=(**env).NewStringUTF(env,"from c string!");
			(**env).CallVoidMethod(env,jniInstance,methodId,jstr);
}
JNIEXPORT void JNICALL Java_com_example_androidndk_JNI_callbackStaticMethod
  (JNIEnv * env, jobject obj){
	//得到类字节码
	jclass clazz = (**env).FindClass(env, "com/example/androidndk/JNI");//需要把所有点替换成/
    // 得到方法ID
//	  jmethodID   (*GetStaticMethodID)(JNIEnv*, jclass, const char*, const char*);
	jmethodID methoid=(**env).GetStaticMethodID(env,clazz,"sayHelloFromJva","()V");
	(**env).CallStaticVoidMethod(env,clazz,methoid);//静态方法调用成功
}



LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := androidndk
LOCAL_SRC_FILES := androidndk.c
LOCAL_LDLIBS :=-llog
include $(BUILD_SHARED_LIBRARY)


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android JNI学习路线可以按照以下步骤进行: 1. 了解JNI的基本概念和作用:JNI(Java Native Interface)是Java提供的一种机制,用于实现Java与其他编程语言(如C、C++)之间的交互。它允许在Java代码中调用本地代码(Native Code),并且可以在本地代码中调用Java代码。 2. 学习JNI的基本语法和规则:JNI使用一组特定的函数和数据类型来实现Java与本地代码之间的交互。你需要学习如何声明本地方法、如何在Java代码中调用本地方法、如何在本地代码中调用Java方法等。 3. 学习JNI的数据类型映射:JNI提供了一套数据类型映射规则,用于将Java数据类型映射到本地代码中的数据类型。你需要学习如何处理基本数据类型、对象类型、数组类型等。 4. 学习JNI的异常处理:在JNI中,Java代码和本地代码之间的异常处理是非常重要的。你需要学习如何在本地代码中抛出异常、如何在Java代码中捕获异常等。 5. 学习JNI的线程处理:JNI允许在本地代码中创建和操作线程。你需要学习如何创建和销毁线程、如何在线程之间进行通信等。 6. 学习JNI的性能优化:JNI涉及到Java代码和本地代码之间的频繁切换,因此性能优化是非常重要的。你需要学习如何减少JNI调用的次数、如何避免不必要的数据拷贝等。 7. 学习JNI的调试和测试:在开发JNI程序时,调试和测试是非常重要的。你需要学习如何使用调试器调试本地代码、如何进行单元测试等。 8. 学习JNI的进阶主题:一旦掌握了基本的JNI知识,你可以进一步学习JNI的进阶主题,如JNI与Java虚拟机的交互、JNI与动态链接库的交互、JNI与多线程的交互等。 总结起来,Android JNI学习路线包括了基本概念、基本语法、数据类型映射、异常处理、线程处理、性能优化、调试和测试以及进阶主题等内容。通过系统地学习这些知识,你将能够更好地理解和应用JNI技术。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值