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)