Android jni/NDK 開發流程(注意事項)

1:配置NDK環境變量,將ndk根目錄配置到path當中。java

2:新建Android工程,在local.properties中配置ndk路徑,好比:android

ndk.dir=H\:\\opt\\android-sdk-windows19-24\\ndk-bundle

    在gradle.properties文件中加上:windows

android.useDeprecatedNdk=true(爲了適配低版本的ndk,不加編譯可能會不過),Android Studio3.0以後改爲
android.useDeprecatedNdk=true

 

3:新建Jni類,並寫好本地方法,好比:app

package com.zhy.youku;

/**
*   生成頭文件以前要運行  set classpath=E:\Android\Studio_App\Jni_Demo\app\src\main\java
 * Created by kzp on 2017/3/7.
 */
public class Jni {
    public native int add(int x, int y);
}

4:在cmd中,到工程的java目錄(src\main\java)執行 javah com.zhy.youku.Jni,則會生成一個頭文件,如:ide

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

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

#ifdef __cplusplus
}
#endif
#endif

新建一個文件夾,取名jni,並將編譯好的頭文件剪切進去。(生成頭文件的另外一個方法:make後在build\intermediates\classes\debug目錄下執行javah -classpath . -jni 包名.類名)便可生成。gradle

 

5:在jni文件夾中,新建一個C的資源文件,文件名字任意取,並include頭文件, 完成C方法,如圖:ui

#include "com_zhy_youku_Jni.h"


JNIEXPORT jint JNICALL Java_com_zhy_youku_Jni_add
        (JNIEnv *env, jobject obj, jint jint1, jint jint2){

    return jint1 + jint2;
}

6:在項目的build.gradle文件中添加ndk配置:this

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"

    defaultConfig {
        applicationId "com.zhy.youku"
        minSdkVersion 15
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"

        ndk{
            moduleName "jni_test"  //動態庫的名稱
            abiFilters "armeabi-v7a", "x86", "armeabi" //支持的CPU版本
            ldLibs "log"  //增長打印信息
        }
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:25.2.0'
}

7:最後new Jni(),就能夠調用C方法:(不要忘了在調用以前要動態加載本地庫,庫名要和build.gradle中配置的名字相同);spa

static {
    System.loadLibrary("jni_test");
}

調用:debug

Log.e("m_tag", "result=" + new Jni().add(1,1));

正確打印結果:03-07 14:35:50.186 18069-18069/com.zhy.youku E/m_tag: result=2。

二:上面講的是java調用C的過程,下面將的是C經過java的反射機制調用java類當中的方法的過程,生成頭文件的方法和上面是同樣的,不一樣的是C方法中實現步驟的不一樣。

1:Jni文件夾爲:

package zhy.ccalljava;

import android.util.Log;

/**
 * Created by kzp on 2017/3/1.
 */

public class Jni {

    static {
        System.loadLibrary("ccalljava");
    }

    /**
     * 當執行這個代碼的時候,讓C代碼調用 add(int x, int y)
     */
    public native void callbackAdd();

    /**
     * 當執行這個代碼的時候,讓C代碼調用 helloFromJava()
     */
    public native void callbackHelloFromJava();

    /**
     * 當執行這個代碼的時候,讓C代碼調用 printString()
     */
    public native void callbackPrintString();

    /**
     * 當執行這個代碼的時候,讓C代碼調用靜態方法 sayHello(String s)()
     */
    public native void callbackSayHello();

    public int add(int x, int y) {
        Log.e("m_tag", "add() x=" + x + " y=" + y);
        return x + y;
    }

    public void helloFromJava() {
        Log.e("m_tag", "helloFromJava()");
    }

    public void printString(String s) {
        Log.e("m_tag","C中輸入的:" + s);
    }

    public static void sayHello(String s){
        Log.e("m_tag",  "我是java代碼中的JNI." + "java中的sayHello(String s)靜態方法,我被C調用了:"+ s);
    }

}

2:C的實現步驟爲:

//
// Created by kzp on 2017/3/1.
//

//獲取java方法簽名:在build->intermediates->classes->debug 目錄下  javah -s 報名.類名

#include "zhy_ccalljava_Jni.h"
#include <stdlib.h>
#include <stdio.h>

#include <android/log.h>
#define LOG_TAG "m_tag" //增長打印 app.gradld中也要配置
#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__)


/**
 * add(int x, int y)
 */
JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackAdd(JNIEnv *env, jobject obj){

    //獲得字節碼
    jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni");

    //獲得方法  (*GetMethodID)(JNIEnv*, jclass, const char*, const char*);  //參數:env, 類, 方法名, 方法簽名
    jmethodID methodId = (*env)->GetMethodID(env, jclazz, "add", "(II)I");
    //實例化該類
    jobject jobject1 = (*env)->AllocObject(env, jclazz);
    //調用方法 (*CallIntMethod)(JNIEnv*, jobject, jmethodID, ...);
    jint value = (*env)->CallIntMethod(env, jobject1, methodId, 1, 1); //成功調用

    LOGE("value=%d\n", value);

}

/**
 * helloFromJava()
 */
JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackHelloFromJava
(JNIEnv *env, jobject obj){
jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni");
jmethodID methodId = (*env)->GetMethodID(env, jclazz, "helloFromJava", "()V");
jobject jobject1 = (*env)->AllocObject(env, jclazz);
(*env)->CallVoidMethod(env, jobject1, methodId);

}


/**
 * 讓C代碼調用printString(String s)
 */
JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackPrintString
(JNIEnv *env, jobject obj){

jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni");
jmethodID methodId = (*env)->GetMethodID(env, jclazz, "printString", "(Ljava/lang/String;)V");
jobject jobject1 = (*env)->AllocObject(env, jclazz);

jstring jstring1 = (*env)->NewStringUTF(env, "i am ard");

(*env)->CallVoidMethod(env, jobject1, methodId, jstring1);

}


/**
 * 讓C代碼調用sayHello(String s)
 * 該方法是靜態的
 */
JNIEXPORT void JNICALL Java_zhy_ccalljava_Jni_callbackSayHello
(JNIEnv *env, jobject obj){

jclass jclazz = (*env)->FindClass(env, "zhy/ccalljava/Jni");
jmethodID methodId = (*env)->GetStaticMethodID(env, jclazz, "sayHello", "(Ljava/lang/String;)V");

//jobject jobject1 = (*env)->AllocObject(env, jclazz); sayHello方法是靜態的 此步驟省掉

jstring jstring1 = (*env)->NewStringUTF(env, "i am static mothod");

(*env)->CallStaticVoidMethod(env, jclazz, methodId, jstring1);
}

3:在MainActivity中調用的步驟爲:

package zhy.ccalljava;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    Jni jni;

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

        jni = new Jni();

        findViewById(R.id.add).setOnClickListener(this);
        findViewById(R.id.string_void).setOnClickListener(this);
        findViewById(R.id.print).setOnClickListener(this);
        findViewById(R.id.static_hello).setOnClickListener(this);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()){
            case R.id.add:
                jni.callbackAdd();
                break;

            case R.id.string_void:
                jni.callbackHelloFromJava();
                break;

            case R.id.print:
                jni.callbackPrintString();
                break;

            case R.id.static_hello:
                jni.callbackSayHello();
                break;
        }

    }
}

 

補充:獲取java方法簽名:在build->intermediates->classes->debug 目錄下  javah -s 報名.類名

在C方法中打印log信息的方法:1;導庫文件

#include <android/log.h>
#define LOG_TAG "m_tag"
#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__)

 2;在build.gradle的ndk配置中加log,如:

apply plugin: 'com.android.application'

android {
    compileSdkVersion 25
    buildToolsVersion "25.0.2"

    defaultConfig {
        applicationId "zhy.ccalljava"
        minSdkVersion 17
        targetSdkVersion 25
        versionCode 1
        versionName "1.0"

        testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

        ndk{
            moduleName "ccalljava"
            ldLibs "log"


        }

    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    androidTestCompile('com.android.support.test.espresso:espresso-core:2.2.2', {
        exclude group: 'com.android.support', module: 'support-annotations'
    })
    compile 'com.android.support:appcompat-v7:25.1.0'
    testCompile 'junit:junit:4.12'
}

3;打印信息

LOGE("value=%d\n", value);
相關文章
相關標籤/搜索