jni的调用过程
1)安装和下载Cygwin,下载Android NDK
2)在ndk项目中JNI接口的设计
3)使用C/C++实现本地方法
4)JNI生成动态连接库.so文件
5)将动态连接库复制到java工程,在java工程中调用,运行java工程便可java
NDKlinux
1.NDK是一系列工具的集合,帮助开发者迅速的开发C/C++的动态库,并能自动将so和java应用打成apk包android
2.NDK集成了交叉编译器,并提供了相应的mk文件和隔离cpu、平台等的差别,开发人员只需简单的修改mk文件就能够建立出sowindows
交叉编译
在一个平台下,编译出另外一个平台可以执行的二进制的代码
平台:windows,mac os,linux
处理器:x86,arm,mips
交叉编译的原理
源代码->编译->连接->可执行程序
模拟其余平台的特性
交叉编译的工具链
多个工具的集合,一个工具使用完后接着调用下一个工具数组
常见工具
NDK:native developement kit:开发jni必备,就是模拟其余平台特性来编译代码的工具
CDT:C/C++ developement tools:高亮显示c语言关键字
cygwin:一个模拟器,能够再windows下运行linux指令架构
NDK目录结构
docs:帮助文档
build/tools:linux的批处理文件
platforms:编译c代码须要使用的头文件和类库
prebuilt:预编译使用的二进制可执行文件
sample:jni的使用例子
source:ndk的源码
toolchains:工具链
ndk-build.cmd:编译打包c代码的一个指令ide
* 使用C语言实现本地方法函数
1. 在项目根目录下建立jni文件夹
2. 在jni文件中建立一个c文件
3. 在java代码中,建立一个本地方法helloFromC工具
//native关键字
public native String helloFromC();
4. 在jni中定义函数实现这个方法,函数名必须为ui
//返回值 报名_类名_方法名(参数参照例子)
jstring Java_com_itheima_helloworld1_MainActivity_helloFromC(JNIEnv* env, jobject obj)
5. 返回一个字符串,用c定义一个字符串
char* cstr = "hello from c";
6. 把c的字符串转换成java的字符串
jstring jstr = (*env)->NewStringUTF(env, cstr); return jstr;
7. 在jni中建立Android.mk文件
8. 在c文件中添加<jni.h>头文件
9. 在jni文件夹下执行ndk-build.cmd指令
10. java代码中加载so类库,调用本地方法(so是C语言的类库)
static{ //加载打包完毕的so类库
System.loadLibrary("hello"); }
代码
MainActivity
public class MainActivity extends Activity { static{ //加载打包完毕的so类库
System.loadLibrary("hello"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v){ Toast.makeText(this, helloFromC(), 0).show(); } //定义一个本地方法,方法体由c语言实现
public native String helloFromC(); }
Hello.c
#include <stdio.h> #include <stdlib.h> #include <jni.h> jstring Java_com_itheima_helloworld1_MainActivity_helloFromC(JNIEnv* env, jobject obj){ //c语言的字符串
char* cstr = "hello from c"; //把C语言的字符串转换成java的字符串 // jstring (*NewStringUTF)(JNIEnv*, const char*); // jstring jstr = (*(*env)).NewStringUTF(env, cstr);
jstring jstr = (*env)->NewStringUTF(env, cstr); return jstr; }
Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) #编译生成的文件的类库叫什么名字 LOCAL_MODULE := hello #要编译的c文件 LOCAL_SRC_FILES := Hello.c include $(BUILD_SHARED_LIBRARY)
* 实现两个数相加
public class MainActivity extends Activity { static{ System.loadLibrary("hello"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v){ Toast.makeText(this, "3+5的和为" + add(3, 5), 0).show(); } public native int add(int i, int j); }
Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello LOCAL_SRC_FILES := Hello.c include $(BUILD_SHARED_LIBRARY)
Hello.c
#include <jni.h> jint Java_com_itheima_helloworld2_MainActivity_add(JNIEnv* env, jobject obj, jint i, jint j){ return i + j; }
Application.mk
APP_ABI := all //支持arm、mips、x86等全部架构
* javah
1.7:在src目录下执行javah 包名.类名
1.6:在bin/classes目录下执行
而后工程刷新,复制里面已经自动生成的方法名,就能够粘贴到c文件中使用
* 配置NDK路径
perference->android->ndk
右键->android tools->add native support能够自动生成jni和mk文件
关联源码:properties->C/C++ general->paths and symbols(源码路径:ndk\platforms\android-18\arch-arm\usr\include)
* 传递整形数组
public class MainActivity extends Activity { static{ System.loadLibrary("hello"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } int[] arr = {1,2,3,4,5}; public void click(View v){ arrayEncode(arr); for (int i : arr) { System.out.println(i); } } public native void arrayEncode(int[] arr); }
Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_MODULE := hello LOCAL_SRC_FILES := hello.c include $(BUILD_SHARED_LIBRARY)
hello.c
#include <jni.h> JNIEXPORT void JNICALL Java_com_itheima_array_MainActivity_arrayEncode (JNIEnv * env, jobject obj, jintArray jintarr){ //拿到整型数组的长度以及第0个元素的地址 //jsize (*GetArrayLength)(JNIEnv*, jarray);
int length = (*env)->GetArrayLength(env, jintarr); //jint* (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
int* arrp = (*env)->GetIntArrayElements(env, jintarr, 0); int i; for(i = 0;i < length; i++){ *(arrp + i) += 10; } }
* 调用美图秀秀的本地方法(只需复制粘贴美图秀秀的so文件,并不须要建立jni文件)
public class MainActivity extends Activity { private Bitmap bm; private ImageView iv; static{ System.loadLibrary("mtimage-jni"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); bm = BitmapFactory.decodeFile("sdcard/aneiyi.jpg"); iv = (ImageView) findViewById(R.id.iv); iv.setImageBitmap(bm); } public void click(View v){ int width = bm.getWidth(); int height = bm.getHeight(); //用于保存全部像素信息
int[] pixels = new int[width * height]; //获取图片的像素颜色信息,保存至pixels
bm.getPixels(pixels, 0, width, 0, 0, width, height); JNI jni = new JNI(); //arg0:保存了全部像素颜色信息的数组 //arg1:图片的宽 //arg2:图片的高 //此方法是经过改变pixels的像素颜色值来实现美化效果
jni.StyleLomoC(pixels, width, height); Bitmap bmNew = Bitmap.createBitmap(pixels, width, height, bm.getConfig()); iv.setImageBitmap(bmNew); } }
* 在C代码中打印log日志
第一步:Android.mk文件增长LOCAL_LDLIBS += -llog
第二步:C代码中增长
#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG, __VA_ARGS__) LOGI("info\n"); LOGD("debug\n");
* 在C中用反射调用JAVA中方法
public class MainActivity extends Activity { static{ System.loadLibrary("hello"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); } public void click(View v){ helloC(); } public native void helloC(); public void show(String message){ Builder builder = new Builder(this); builder.setTitle("БъЬт"); builder.setMessage(message); builder.show(); } }
Android.mk
LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) LOCAL_LDLIBS += -llog LOCAL_MODULE := hello LOCAL_SRC_FILES := hello.c include $(BUILD_SHARED_LIBRARY)
hello.c
#include <jni.h> #include <android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__) JNIEXPORT void JNICALL Java_com_itheima_ccalljava_MainActivity_helloC (JNIEnv * env, jobject obj){ LOGD("hello!"); LOGI("hello!"); //jclass (*FindClass)(JNIEnv*, const char*);
jclass clazz = (*env)->FindClass(env, "com/itheima/ccalljava/MainActivity"); //jmethodID (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);
jmethodID methodID = (*env)->GetMethodID(env, clazz, "show", "(Ljava/lang/String;)V"); //void (*CallVoidMethod)(JNIEnv*, jobject, jmethodID, ...);
(*env)->CallVoidMethod(env, obj, methodID, (*env)->NewStringUTF(env, "是时候再黑一波小志了")); }