Android NDK之静态/动态注册Native方法

一、简介

关于NDK有两种方法注册:静态注册动态注册

  • 静态注册: 就是直接在Java文件里写个native方法 然后再c/c++文件中实现这个方法就行了;
  • 动态注册: 就是为了不要写很长的方法名,用JNI_OnLoad方法实现预注册,即当执行System.LoadLibrary()方法时候就把需要调用的方法给注册了,效率要高 Android就是采用此方法;

下面就以示例来说明静态注册动态注册以及C层调用Java层方法

二、代码实现

Github:NDKDemo

Java层Native方法定义

package com.cloudwise.ndk;
import android.util.Log;

public class NDKUtil {

    // 静态注册Native方法
    public native static String stringFromJNI();

    // 静态注册Native方法
    public native static void setJNILogEnable(int type);

    // 动态注册Native方法
    public native static int getVersionCode();

    // 动态注册Native方法
    public native static String getVersion(int code);

    // 动态注册Native方法,并且C回调Java方法
    public native static void callJavaString();

    // 动态注册Native方法,并且C回调Java方法
    public native static void callJavaVoid();


    // C调用Java方法(带返回值)
    public static String getStringToC(String name){
        Log.e("CLOUDWISE", "name : " + name);
        return "C From Java";
    }

    // C调用Java方法(不带返回值)
    public static void getVoidToC(int id, String name){
        Log.e("CLOUDWISE", "id : " + id + " ---- name : " + name);
    }
}

C层代码

common.h

#ifndef NDKDEMO_COMMON_H
#define NDKDEMO_COMMON_H

#ifdef __cplusplus
extern "C" {
#endif

#define LOGOPEN 1 //日志开关,1为开,其它为关
#define LOG_TAG    "[CLOUDWISE-NDK]"
#if(LOGOPEN==1)
    #define LOGI(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
    #define LOGE(...)  __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
    #define LOGD(...)  __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
#else
#define LOGI(...) NULL
    #define LOGE(...) NULL
    #define LOGD(...) NULL
#endif

#ifdef __cplusplus
}
#endif

#endif //NDKDEMO_COMMON_H

native-lib.c

#include <jni.h>
#include <string.h>
#include <android/log.h>
#include <pthread.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
#include "common.h"

static JavaVM *j_vm = NULL;
static jclass j_class = NULL;

static jint debug = 1;

/**
 * 静态注册的Native方法
 * @param env
 * @param arg
 * @return
 */
JNIEXPORT jstring JNICALL Java_com_cloudwise_ndk_NDKUtil_stringFromJNI(
        JNIEnv *env,
        jclass arg) {
    jstring hello = "Hello ---- from ---- C";
    if(debug == 1){
        LOGE("stringFromJNI ---- Hello ---- from ---- C");
    }

    return (*env)->NewStringUTF(env, hello);
}

/**
 * 静态注册的Native方法
 * @param env
 * @param arg
 * @param type
 */
JNIEXPORT void JNICALL Java_com_cloudwise_ndk_NDKUtil_setJNILogEnable(
        JNIEnv *env,
        jclass arg, jint type){
    if(type == 1){
        debug = 1;
    } else {
        debug = 0;
    }
}

/**
 * 动态注册的Native方法
 * @param env
 * @param arg
 * @return
 */
JNIEXPORT jint JNICALL getVersionCode(JNIEnv *env, jclass arg){
    if(debug == 1){
        LOGE("getVersionCode ----------- 10");
    }
    return 10;
}

/**
 * 动态注册的Native方法
 * @param env
 * @param arg
 * @param code
 * @return
 */
JNIEXPORT jstring JNICALL getVersion(JNIEnv *env, jclass arg, jint code){
    if(debug == 1){
        LOGE("getVersion ----------- 1.2.6");
    }
    jstring ver = "1.2.6";
    return (*env)->NewStringUTF(env, ver);
}

/**
 * C调用Java方法(带参数不带返回值)
 */
void callVoidFromJava(){
    JNIEnv *env;
    (*j_vm)->AttachCurrentThread(j_vm, &env, NULL);
    jmethodID methodid = (*env)->GetStaticMethodID(env, j_class, "getVoidToC", "(ILjava/lang/String;)V");
    (*env)->CallStaticVoidMethod(env, j_class, methodid, 10, (*env)->NewStringUTF(env, "C-Name"));
    if(debug == 1) {
        LOGE("callVoidFromJava Java To C");
    }
}

/**
 * C调用Java方法(带参数带返回值)
 */
void callStringFromJava(){
    JNIEnv *env;
    (*j_vm)->AttachCurrentThread(j_vm, &env, NULL);
    jmethodID methodid = (*env)->GetStaticMethodID(env, j_class, "getStringToC", "(Ljava/lang/String;)Ljava/lang/String;");
    jstring str = (jstring)(*env)->CallStaticObjectMethod(env, j_class, methodid,(*env)->NewStringUTF(env, "C-Name"));
    char* java = (char*)(*env)->GetStringUTFChars(env, str, NULL);
    if(debug == 1) {
        LOGE("callStringFromJava Java To C : %s", java);
    }
}

/**
 * 动态注册的Native方法,然后调用Java方法
 * @param env
 * @param arg
 */
JNIEXPORT void JNICALL callJavaString(JNIEnv *env, jclass arg) {
    callStringFromJava();
}

/**
 * 动态注册的Native方法,然后调用Java方法
 * @param env
 * @param arg
 */
JNIEXPORT void JNICALL callJavaVoid(JNIEnv *env, jclass arg) {
    callVoidFromJava();
}

static JNINativeMethod mMethods[] = {
        {"getVersionCode", "()I", (void*)getVersionCode},
        {"getVersion", "(I)Ljava/lang/String;", (void*)getVersion},
        {"callJavaString", "()V", (void*)callJavaString},
        {"callJavaVoid", "()V", (void*)callJavaVoid}
};

static int registerNativeMethods(JNIEnv* env, const char* className, const JNINativeMethod* gMethods, int numMethods){
    jclass clazz;
    clazz = (*env)->FindClass(env, className);

    if(clazz == NULL){
        return -1;
    }

    j_class = (jclass)(*env) -> NewGlobalRef(env, (jobject)clazz);

    if((*env)->RegisterNatives(env, clazz, gMethods, numMethods) < 0){
        return -1;
    }

    return 0;
}

static int registerNative(JNIEnv* env){
    return registerNativeMethods(env, "com/cloudwise/ndk/NDKUtil", mMethods, sizeof(mMethods)/ sizeof(mMethods[0]));
}

JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
    LOGE("JNI_OnLoad ------------------------------ ");
    JNIEnv* env = NULL;
    jint result = -1;
    /*
     * JavaVM::GetEnv 原型为 jint (*GetEnv)(JavaVM*, void**, jint);
     * GetEnv()函数返回的  Jni 环境对每个线程来说是不同的,
     * 由于Dalvik虚拟机通常是Multi-threading的。每一个线程调用JNI_OnLoad()时,
     * 所用的JNI Env是不同的,因此我们必须在每次进入函数时都要通过vm->GetEnv重新获取
     */
    if((*vm)->GetEnv(vm, (void**)&env, JNI_VERSION_1_4) != JNI_OK){
        return -1;
    }

    if(registerNative(env) != JNI_OK){
        return -1;
    }

    j_vm = vm;

    //cloudwise_init(1);

    result = JNI_VERSION_1_4;
    return result;
}

Java层调用代码

package com.cloudwise.ndk;

import android.app.ProgressDialog;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.io.InputStream;

public class CrashActivity extends AppCompatActivity implements View.OnClickListener {

    TextView txt;
    Button btn, btn_debug, btn_close, btn_ver_code, btn_ver, btn_logcat;
    // Used to load the 'native-lib' library on application startup.
    static {
        System.loadLibrary("native-lib");
    }

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

        // Example of a call to a native method
        txt = (TextView) findViewById(R.id.sample_text);
        btn = (Button)findViewById(R.id.btn);
        btn.setOnClickListener(this);

        btn_debug = (Button)findViewById(R.id.btn_debug);
        btn_debug.setOnClickListener(this);

        btn_close = (Button)findViewById(R.id.btn_close);
        btn_close.setOnClickListener(this);

        btn_ver = (Button)findViewById(R.id.btn_ver);
        btn_ver.setOnClickListener(this);

        btn_ver_code = (Button)findViewById(R.id.btn_ver_code);
        btn_ver_code.setOnClickListener(this);

        btn_logcat = (Button)findViewById(R.id.btn_string);
        btn_logcat.setOnClickListener(this);

        btn = (Button)findViewById(R.id.btn_void);
        btn.setOnClickListener(this);
    }

    private void btnClick(){
        txt.setText(NDKUtil.stringFromJNI());
        //Log.e("CLOUDWISE", "stringFromJNI-------");
    }

    private void btnDebug(){
        NDKUtil.setJNILogEnable(1);
    }

    private void btnClose(){
        NDKUtil.setJNILogEnable(0);
    }

    private void btnVer(){
        txt.setText(NDKUtil.getVersion(1));
    }

    private void btnVerCode(){
        txt.setText(NDKUtil.getVersionCode()+"");
    }

    private void btnString(){
        NDKUtil.callJavaString();
    }

    private void btnVoid(){
        NDKUtil.callJavaVoid();
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.btn:
                btnClick();
                break;
            case R.id.btn_debug:
                btnDebug();
                break;
            case R.id.btn_close:
                btnClose();
                break;
            case R.id.btn_ver:
                btnVer();
                break;
            case R.id.btn_ver_code:
                btnVerCode();
                break;
            case R.id.btn_string:
                btnString();
                break;
            case R.id.btn_void:
                btnVoid();
                break;
        }
    }
}

Java层布局代码

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">
            <TextView
                android:id="@+id/sample_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_margin="10dp"
                android:text="" />

            <Button
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:text="JNI调用"
                android:id="@+id/btn"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginTop="10dp"/>

            <Button
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:text="VersionCode"
                android:id="@+id/btn_ver_code"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginTop="10dp"/>

            <Button
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:text="Version"
                android:id="@+id/btn_ver"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginTop="10dp"/>

            <Button
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:text="开启JNI日志"
                android:id="@+id/btn_debug"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginTop="10dp"/>

            <Button
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:text="关闭JNI日志"
                android:id="@+id/btn_close"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginTop="10dp"/>

            <Button
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:text="C调Java(带返回值)"
                android:id="@+id/btn_string"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginTop="10dp"/>

            <Button
                android:layout_width="match_parent"
                android:layout_height="50dp"
                android:text="C调Java(不带返回值)"
                android:id="@+id/btn_void"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="10dp"
                android:layout_marginTop="10dp"/>
        </LinearLayout>
    </ScrollView>

</LinearLayout>

布局界面

效果图

运行结果

[CLOUDWISE-NDK]: stringFromJNI ---- Hello ---- from ---- C
[CLOUDWISE-NDK]: getVersionCode ----------- 10
[CLOUDWISE-NDK]: getVersion ----------- 1.2.6
CLOUDWISE: name : C-Name
[CLOUDWISE-NDK]: callStringFromJava Java To C : C From Java
CLOUDWISE: id : 10 ---- name : C-Name
[CLOUDWISE-NDK]: callVoidFromJava Java To C
发布了100 篇原创文章 · 获赞 45 · 访问量 64万+

猜你喜欢

转载自blog.csdn.net/wangzhongshun/article/details/101273128
今日推荐