android JNI native 编程

来源:互联网 发布:mac能打的网游 编辑:程序博客网 时间:2024/06/05 00:17
android native编程,既android中使用java与c进行沟通编程。以便完成一些java中无法完成,或者不想暴露在java中实现的方法,如算法加密等。

注:下一篇要讲如何通过jni做到android app被卸载后弹出卸载反馈网页,或者做一些其他事情,所以本文章没有使用demo程序来做演示,里面的方法名什么的,不要在意。

编译环境
首先 native编程需要有ndk支持,在android studio中 ndk 在project structure界面就可以看到,如果没有ndk的话,会自动出现下载ndk字样,点击自动安装即可。






Gradle配置
       
  1.   先在project最外层的 gradle.properties里面添加 :
     android.useDeprecatedNdk=true

  1. 然后在app 层的gradle, android.defaultConfig {….}里面
     ndk{             moduleName “uc”     //编译出来之后so文件的名字,会被自动追加上 lib字样即 libuc.so,不要删除掉lib             abiFilters "armeabi", "armeabi-v7a", “x86”     //架构类型 可以不配置,一般就是这三种就够,x86都很少。       } 

     ndk自动编译配置。

  1. 下面这一段不用指定,根据需求配置:
sourceSets {    main {        jni.srcDirs = [….]        jniLibs.srcDirs = ['src/main/libs']    }}

指定jni的src文件目录,以及jniLibs 也就是存放so文件的目录,这一段可以不用配置,默认jni src文件夹在 src/main/jni, jni文件放在这里就行,so文件默认放置在 src/main/jniLibs 如下所示。



编译的时候,默认的so文件不会自动被放置在jniLibs,android studio会讲编译出的so,跟其他编译的文件一样,放在build目录下,so基本路径为 build/intermediates/jniLibs/googledefault/release/*/*.so  其中*号对应各个架构系统。
将编译出的so文件,拿出来就可供其他开发者使用,跟aar及jar包原理相同。





native方法编写及native文件生成
对于java来说,是不能直接与c进行对话的,需要jni作为桥梁,全程Java Native Interface,所以在.java文件中,所定义的就是一个未实现的 native 方法接口,例:
private static native void startMonitoring(String packageNameString webAddress);
前面的修饰符  static 并不重要,static与非static在生成 header文件时参数会有区别,不过影响不大。最重要的就是native修饰符,这个修饰符标志着这个方法是一个调用c代码的native方法,所以在c文件里,会有一个跟这个相匹配的方法。

方法定义完成之后,就可以进行头文件生成。头文件,.h中一般放的是同名.c文件中定义的变量、数组、函数的声明,需要让.c外部使用的声明。对于这个不多讲,我们不必太深入,感兴趣的可以自己百度。因为头文件可以自动生成,所以我们android开发不必太过关心这个东西。
要生成头文件,首先要确保你的某一java类中有上述native修饰的方法,然后在 src/main/java 文件夹下打开终端,执行 javah  完整类名命令,如下:

ArthurdeiMac:java ihandysoft$ javah com.ihs.inputmethod.uninstallchecker.UninstallChecker

com.ihs.inputmethod.uninstallchecker.UninstallChecker 这一段是我包含Native方法的类的完整类名,可以通过右键点击类名,选择 copy reference 来取得。
然后就会生成一个类似:
com_ihs_inputmethod_uninstallchecker_UninstallChecker.h
的文件,这里面的东西都不要更改,否则会造成找不到jni方法。

讲.h文件移动到  src/main/jni 文件夹下,打开可以看到如下所示:

/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_ihs_inputmethod_uninstallchecker_UninstallChecker */

#ifndef _Included_com_ihs_inputmethod_uninstallchecker_UninstallChecker
#define _Included_com_ihs_inputmethod_uninstallchecker_UninstallChecker
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     com_ihs_inputmethod_uninstallchecker_UninstallChecker
 * Method:    startMonitoring
 * Signature: (Ljava/lang/String;Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_ihs_inputmethod_uninstallchecker_UninstallChecker_startMonitoring
  (JNIEnv *jclassjstringjstring);

/*
 * Class:     com_ihs_inputmethod_uninstallchecker_UninstallChecker
 * Method:    stopMonitoring
 * Signature: (Ljava/lang/String;)V
 */
JNIEXPORT void JNICALL Java_com_ihs_inputmethod_uninstallchecker_UninstallChecker_stopMonitoring
  (JNIEnv *jclassjstring);

#ifdef __cplusplus
}
#endif
#endif


中间这两条 
JNIEXPORT void JNICALL Java_com_ihs_inputmethod_uninstallchecker_UninstallChecker_stopMonitoring
  (JNIEnv *jclassjstring);
就是能跟 java沟通的条件,类名与java中定义的 native方法 完全匹配。.h文件中仍然只是定义,没有实现,想要实现的话需要在c文件中实现,接下来的编程思路,已经与c相同。

新建一个.c文件,名字随意,同样是在jni目录下,然后c文件中 使用 include “” 来包括头文件。
#include "com_ihs_inputmethod_uninstallchecker_UninstallChecker.h"
此时已经可以使用 头文件中定义好的接口方法,然后实现该接口。因为h文件中方法名较长,所以最好直接复制到c文件中:
JNIEXPORT void JNICALL Java_com_ihs_inputmethod_uninstallchecker_UninstallChecker_stopMonitoring
  (JNIEnv *jclassjstring);
下面就是c,编程了,我这里就不写了,提供一个简单测试的给大家用:
  1. 在c文件最上面添加 #include <android/log.h> 
  2. JNIEXPORT void JNICALLJava_com_ihs_inputmethod_uninstallchecker_UninstallChecker_stopAllSelf(JNIEnv *env,                                                                       jclass instance,                                                                       jstring packageName_) {    __android_log_print(ANDROID_LOG_DEBUG, "JNIMsg", " Log from jni");}

  3. 这句话的意思是调用 android的 Log.d(“JNIMsg”," Log from jni); 来打印log。
这样一个简单地jni方法就被实现了。 当然因为我这里的return值为void,所我不需要return数据给java,如果需要return,请将void改为 c中的java类型,常用的如下所示:
Java类型  本地类型  字节(bit)
boolean   jboolean   8, unsigned
byte    jbyte    8
char    jchar    16, unsigned
short    jshort    16
int     jint     32
long    jlong    64
float    jfloat    32
double   jdouble   64
void    void     n/a  
用法就是 java文件中方法定义return boolean 在c方法里面 return就会变为 jboolean。


创建mk文件,编译so。
源代码都已经齐全了,现在就可以进行编译或者测试了。在jni目录下创建Android.mk文件。里面的配置复制我下面的即可:
LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := uc
LOCAL_SRC_FILES := UninstallChecker.c

LOCAL_C_INCLUDES := $(LOCAL_PATH)/include
LOCAL_LDLIBS := -L$(SYSROOT)/usr/lib -llog

include $(BUILD_SHARED_LIBRARY)

其中
LOCAL_MODULE    := uc
LOCAL_SRC_FILES := UninstallChecker.c
这两个需要自行修改为跟你匹配的,上面gradle也提到过module名字,匹配就行。


此时基本jni框架已经完成,可以直接运行了。
android studio会将编译出的so,跟其他编译的文件一样,放在build目录下,so基本路径为 build/intermediates/jniLibs/googledefault/release/*/*.so  其中*号对应各个架构系统。
将编译出的so文件,拿出来就可供其他开发者使用,跟aar及jar包原理相同。

0 0
原创粉丝点击