(二)Java 调用C 函数

2019-11-22  本文已影响0人  极客天空

java 调用C 函数 通过以下四个案例 1.将传入的两个int 值相加并返回;2.将两个字符串拼接后返回;3.将数组中的每个元素增加10; 4.检查密码是否正确

方法实现

  1. 编写native方法
package domain.com.rrcc.javacallc;

public class JNI {

    {
        System.loadLibrary("javacallc");
    }

    /**
     * 让C代码做加法运算,把结果返回
     * @param x
     * @param y
     * @return
     */
    public native int add(int x, int y);

    /**
     * 从java传入字符串,C代码进程拼接
     *
     * @param s I am from java
     * @return  I am form java add I am from C
     */
    public native String sayHello(String s);

    /**
     * 让C代码给每个元素都加上10
     * @param intArray
     * @return
     */
    public native int[] increaseArrayEles(int[] intArray);
    /*
     * 应用: 检查密码是否正确, 如果正确返回200, 否则返回400
     */
    public native int checkPwd(String pwd);
}

  1. 通过javah命令 生成native方法对应的JNI函数声明头文件
javah -d ../jni domain.com.rrcc.javacallc.JNI

生成了jni目录以及下面的domain_com_rrcc_javacallc_JNI.h 头文件

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

#ifndef _Included_domain_com_rrcc_javacallc_JNI
#define _Included_domain_com_rrcc_javacallc_JNI
#ifdef __cplusplus
extern "C" {
#endif
/*
 * Class:     domain_com_rrcc_javacallc_JNI
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_domain_com_rrcc_javacallc_JNI_add
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     domain_com_rrcc_javacallc_JNI
 * Method:    sayHello
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_domain_com_rrcc_javacallc_JNI_sayHello
  (JNIEnv *, jobject, jstring);

/*
 * Class:     domain_com_rrcc_javacallc_JNI
 * Method:    increaseArrayEles
 * Signature: ([I)[I
 */
JNIEXPORT jintArray JNICALL Java_domain_com_rrcc_javacallc_JNI_increaseArrayEles
  (JNIEnv *, jobject, jintArray);

/*
 * Class:     domain_com_rrcc_javacallc_JNI
 * Method:    checkPwd
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_domain_com_rrcc_javacallc_JNI_checkPwd
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
  1. 在步骤2生成的文件夹(jni)下创建并实现对应C/C++文件
    javacallc.c 文件
//
// Created by mac on 2019/11/21.
//
/**
 * jint:返回值
 * Java_全类名_方法名
 * JNIEnv *env:
 */
#include "domain_com_rrcc_javacallc_JNI.h"
#include <jni.h>
#include <stdlib.h>
#include <string.h>
#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)

/**
 * 把一个jstring转换成一个c语言的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;
}

jint JNICALL Java_domain_com_rrcc_javacallc_JNI_add
  (JNIEnv* env, jobject jo, jint ji, jint jj){
    int result = ji + jj;
    return result;
  }

/*
 * Class:     domain_com_rrcc_javacallc_JNI
 * Method:    sayHello
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
jstring JNICALL Java_domain_com_rrcc_javacallc_JNI_sayHello
        (JNIEnv * env, jobject job, jstring jstr){

    char* fromJava =_JString2CStr(env,jstr);//I am form java add I am from C
    //c:
    char* fromC = "add I am from C12";
    //拼接函数strcat
    strcat(fromJava,fromC);//把拼接的结果放在第一参数里面
    //jstring     (*NewStringUTF)(JNIEnv*, const char*);
    LOGD("fromJava===%s\n",fromJava);
    return  (*env)->NewStringUTF(env,fromJava);
};


/*
 * Class:     domain_com_rrcc_javacallc_JNI
 * Method:    increaseArrayEles
 * Signature: ([I)[I
 */
JNIEXPORT jintArray JNICALL Java_domain_com_rrcc_javacallc_JNI_increaseArrayEles
        (JNIEnv * env, jobject jobj, jintArray jarray){

    //1.得到数组的长度
    //jsize       (*GetArrayLength)(JNIEnv*, jarray);
    jsize size = (*env)->GetArrayLength(env,jarray);
    //2.得到数组元素
    //jint*       (*GetIntArrayElements)(JNIEnv*, jintArray, jboolean*);
    jint*  intArray = (*env)->GetIntArrayElements(env,jarray,JNI_FALSE);
    //3.遍历数组,给每个元素加上10
    int i;
    for(i =0;i<size;i++){
//        *(intArray+i) = *(intArray+i) + 10;
        *(intArray+i) +=  100;
//        LOGD("*(intArray+i) === %d\n",*(intArray+i));
    }
    //4.返回结果
    return  jarray;

};


/*
 * Class:     domain_com_rrcc_javacallc_JNI
 * Method:    checkPwd
 * Signature: (Ljava/lang/String;)I
 */
jint JNICALL Java_domain_com_rrcc_javacallc_JNI_checkPwd
        (JNIEnv * env, jobject jobj, jstring jstr){

    //服务器的密码是123456
    char* origin = "123456";
    char* fromUser = _JString2CStr(env,jstr);

    //函数比较字符串是否相同
    int code =  strcmp(origin,fromUser);
    LOGD("code===%d\n",code);
    if(code==0){
        return 200;
    }else{
        return 400;
    }

};


  1. 配置gradle 文件 指定编译的不同CPU
apply plugin: 'com.android.application'

android {
    compileSdkVersion 29
    defaultConfig {
        applicationId "domain.com.rrcc.javacallc"
        minSdkVersion 16
        targetSdkVersion 29
        versionCode 1
        versionName "1.0"
        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        ndk{
            moduleName "libjavacallc" //so文件: lib+moduleName+.so
            abiFilters "armeabi-v7a", "x86" //cpu的类型
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
    externalNativeBuild {
        ndkBuild {
            path 'src/main/jni/Android.mk'
        }
    }
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
//    implementation 'com.android.support:appcompat-v7:29.+'
    implementation 'com.android.support:appcompat-v7:+'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    testImplementation 'junit:junit:4.12'
    androidTestImplementation 'com.android.support.test:runner:1.0.2'
    androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
}

  1. 通过ndk-build 自动生成so文件

在jni目录下创建Android.mk 文件

LOCAL_PATH := $(call my-dir)

include $(CLEAR_VARS)

LOCAL_MODULE    := libjavacallc
LOCAL_SRC_FILES := javacallc.c
LOCAL_LDLIBS := -lm -llog
LOCAL_LDLIBS += -llog
include $(BUILD_SHARED_LIBRARY)

module 处右键 选择 Link C++ Project with Gradle


001.png

生成so文件、然后拷贝到app的libs文件目录下


002.png
003.png
  1. 在项目具体调用实现
package domain.com.rrcc.javacallc;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;

public class MainActivity extends Activity {
    private JNI jni;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        jni = new JNI();
    }
    public void add(View view){
        int result =jni.add(99, 1);
        Log.e(MainActivity.class.getSimpleName(),"result==="+result);
    }
    public void string(View view){
        String result =jni.sayHello("I am from java ");
        Log.e(MainActivity.class.getSimpleName(), "result===" + result);
    }
    public void array(View view){
        int array[] = {1,2,3,4,5};
        int result[] =jni.increaseArrayEles(array);
        for(int i=0;i<result.length;i++){
            Log.e(MainActivity.class.getSimpleName(),"array["+i+"]==="+array[i]);
        }
    }
    public void checkpw(View view){
        int result =jni.checkPwd("123456");
        Log.e(MainActivity.class.getSimpleName(), "result===" + result);
    }
}

上一篇 下一篇

猜你喜欢

热点阅读