[安卓开发基础]NDK学习笔记 初识jni调用流程

  • 1. 初识NDK

NDK(Native   Develop  Kits  ),NDK 可以将C/C++ 编译成可执行的文件.so,    JNI (java  native interface)  Java  本地开发接口,c/c++与Java通信的协议。,C/C++ 运行效率高一点(C/C++ -----(.exe)),Java运行效率相对低一点(Java---(*.class)---JVM---机器)。  

Java(跨平台)-------jni---------C/C++(硬件操作)

   

arm 架构---x86/armeabi

x86架构----libs/x86

2.ndk 环境配置:

  1. Ndk 自带例子:

  • 1.创建一个工程JNIDDemo1

    声明native 方法,

   static 静态块调用so库。

  static {
        System.loadLibrary("javaCallC");//javaCallC  的名称
    }

package com.example.JNIDDemo1;

/**
 * Created by zengjx on 2018/4/11.
 */
public class JNI {
    static {
        System.loadLibrary("javaCallC");
    }
    public   void   helloFromJava(){

        System.out.println("helloFromJava");

    }
    public     int javaadd(int x,int y){

        System.out.println("javaadd");
        return  x+y;
    }
    public     void print(String  s){

        System.out.println(s);

    }

    //写 native 方法
    public   native   int add(int x,int y);
    public   native   int  del(int a  ,int b);
    public   native   String   getString(String  str);
    public   native   int[]  getIntArray(int[] array);
    public   native   int   getStringLen(String str);


}
  • 2.在src目录下 用javah 生成头文件;

  • 3.创建 jni目录,将头文件剪切放到 jni目录下;
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
/* Header for class com_example_JNIDDemo1_JNI */

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

/*
 * Class:     com_example_JNIDDemo1_JNI
 * Method:    del
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_JNIDDemo1_JNI_del
  (JNIEnv *, jobject, jint, jint);

/*
 * Class:     com_example_JNIDDemo1_JNI
 * Method:    getString
 * Signature: (Ljava/lang/String;)Ljava/lang/String;
 */
JNIEXPORT jstring JNICALL Java_com_example_JNIDDemo1_JNI_getString
  (JNIEnv *, jobject, jstring);

/*
 * Class:     com_example_JNIDDemo1_JNI
 * Method:    getIntArray
 * Signature: ([I)[I
 */
JNIEXPORT jintArray JNICALL Java_com_example_JNIDDemo1_JNI_getIntArray
  (JNIEnv *, jobject, jintArray);

/*
 * Class:     com_example_JNIDDemo1_JNI
 * Method:    getStringLen
 * Signature: (Ljava/lang/String;)I
 */
JNIEXPORT jint JNICALL Java_com_example_JNIDDemo1_JNI_getStringLen
  (JNIEnv *, jobject, jstring);

#ifdef __cplusplus
}
#endif
#endif
  • 4.根据头文件编写C文件文件名称与JNI 的javaCallC 相同。

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

本地函数命名规则  Java_包名_native所在的类名_native函数名

第二个参数:jobject调用 当前函数的对象

第一个参数   JNIEnv   JNINativeInterface    (**env)(*env)->

JNINativeInterface  : 接口函数指针表

基本数据类型: 
这里写图片描述

引用类型: 
这里写图片描述

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "com_example_JNIDDemo1_JNI.h"
#include <android/log.h>
#define  TAG "JNI_TAG"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)

  char* _JString2Cstr(JNIEnv* env,jstring  jstr);

/*
 * Class:     com_example_JNITest2_JNI
 * Method:    add
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL  JNICALL Java_com_example_JNIDDemo1_JNI_add
  (JNIEnv *  env, jobject   jobject, jint x, jint   y){

    int result =0;
    result=x+y;
    LOGI("LOGI----result%d,%d,%d",x,y,result);
    /**************c调用java*****************/
    jclass  cls;
    jmethodID   mid;
    jmethodID   mid_add;
    jmethodID   mid_string;
    cls=(*env)->FindClass(env,"com/example/JNIDDemo1/JNI");
   //找到字节码
   mid= (*env)->GetMethodID(env,cls,"helloFromJava","()V");
   //找到方法

   //创建对象(可选  同一个java类中 不要创建对象jobject )
   //通过对象调用方法
    (*env)->CallVoidMethod(env,jobject,mid);
    //第二个 函数
    //public int javaadd(int, int);
    // descriptor: (II)I
     mid_add= (*env)->GetMethodID(env,cls,"javaadd","(II)I");
    //通过对象调用方法
      int   res  =(*env)->CallIntMethod(env,jobject,mid_add,123,456);
       LOGI("LOGI--c调用 java add--%d",res);

     //第3个
     //  public void print(java.lang.String);
      //  descriptor: (Ljava/lang/String;)V
        mid_string= (*env)->GetMethodID(env,cls,"print","(Ljava/lang/String;)V");
        //通过对象调用方法
        jstring   jstr =(*env)->NewStringUTF(env,"print  from  c");
       (*env)->CallVoidMethod(env,jobject,mid_string,jstr);
     return result;
  }

/*
 * Class:     com_example_JNITest2_JNI
 * Method:    del
 * Signature: (II)I
 */
JNIEXPORT jint JNICALL Java_com_example_JNIDDemo1_JNI_del
  (JNIEnv *env, jobject    jobject, jint   a, jint  b){
    int result =0;
    result=a-b;
    LOGI("LOGI----result%d",result);

     return   result;
  }
  char* _JString2Cstr(JNIEnv* env,jstring  jstr){
      char*  cstr=NULL;
      //获取String.class  字节码
       LOGI("LOGI----resultcstr%s","_JString2Cstr");
       jclass  classstring=(*env)->FindClass(env,"java/lang/String");//找到String 类型的字节码
        LOGI("LOGI----resultcstr%s","_JString2Cstr  2");
      jstring  strencode=(*env)->NewStringUTF(env,"GB2312");
          LOGI("LOGI----resultcstr%s","_JString2Cstr  3");
      //获取 方法 ID 号
      //找到 classstring 的   getByte 方法
      jmethodID  mid =(*env)->GetMethodID(env,classstring,"getBytes","Ljava/lang/String;)[B");
        LOGI("LOGI----resultcstr%s","_JString2Cstr  4");
      jbyteArray  barr=(jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode);
        LOGI("LOGI----resultcstr%s","_JString2Cstr  5");
       //获取数组的长度
      jsize   alen =(*env)->GetArrayLength(env,barr);
      LOGI("LOGI----resultcstr_JString2Cstr  6 %d",alen);
      //获取数组首地址
      jbyte*   ba=(*env)->GetByteArrayElements(env,barr,JNI_FALSE);
      if(alen>0){//是否有内容
      cstr=(char*)malloc(alen+1);
      memcpy(cstr,ba,alen);
      cstr[alen]=0;//"结束符"


      }
     (*env)->ReleaseByteArrayElements(env,barr,ba,0);
      LOGI("LOGI---_JString2Cstr  end :%s",cstr);
         return  cstr;

  }

JNIEXPORT jstring JNICALL Java_com_example_JNIDDemo1_JNI_getString
    (JNIEnv *env, jobject   jobject, jstring   jstr){
     jsize    size;
     char  acbuf[]="wwwwwwwwwwwwwww";
     int   strlen=0;
       char *cstr;
     int  i=0;
   cstr = (*env)->GetStringUTFChars(env, jstr, NULL);//获得字符串转为 字符串指针
   if (cstr == NULL) {//用到分配内存可能会失败
      return NULL; /* OutOfMemoryError already thrown */
   }

    size= (*env)->GetStringUTFLength(env, jstr);
    LOGI("Get string from java size:%d\n",size);
   // strlen=strlen(cstr);
   // LOGI("Get string from java size:%d\n",strlen);
    LOGI("Get string from java :%s\n", cstr);


     for(i=0;i<size;i++){
        LOGI("Get string from java :%c\n", cstr[i]);
       *(cstr+i)+=1;
       LOGI("Get string from java :%c\n", cstr[i]);

     }
    // (*env)->ReleaseStringUTFChars(env, jstr, cstr);//释放空间
  //  return (*env)->NewStringUTF(env,acbuf);
     return (*env)->NewStringUTF(env,cstr);
    }
  • 5.编写Android.mk 文件:
LOCAL_PATH := $(call my-dir)
 include $(CLEAR_VARS)
 LOCAL_MODULE    :=javaCallC
 LOCAL_SRC_FILES := javaCallC.c
 LOCAL_LDLIBS += -llog
 include $(BUILD_SHARED_LIBRARY)
 #include $(BUILD_STATIC_LIBRARY)

Application.mk 文件代码。(看文件中注释)

APP_ABI := all

#APP_ABI:= armeabi armeabi-v7a x86 mips mips-r2 mips-r2-sf
#如果是普通arm处理器的Android手机,使用APP_ABI := armeabi;
#如果是x86处理器的,使用APP_ABI := x86,等等。
#如果APP_ABI := all,会编译所有指令的so

APP_STL := stlport_static

#NDK中C++标准库、STL的配置
#如未配置可能出现错误: fatal error: iostream: No such file or directory
--------------------- 
作者:Ruffian-痞子 
来源:CSDN 
原文:https://blog.csdn.net/u014702653/article/details/51861013 
版权声明:本文为博主原创文章,转载请附上博文链接!

APP_MODULES := javaCallC
APP_ABI := armeabi

---------- cut here ------------------

#当前目录

    LOCAL_PATH := $(call my-dir)

   #清除上次编译的信息

    include $(CLEAR_VARS)

     #在这里指定最终生成的文件名字

LOCAL_MODULE    := hello-jni

# 要编译的c代码文件名

    LOCAL_SRC_FILES := hello-jni.c

     #要生成的是一个动态链接库

    include $(BUILD_SHARED_LIBRARY)

---------- cut here ------------------

  • C代码添加log:

 Android.mk

LOCAL_PATH := $(call my-dir)

  include $(CLEAR_VARS)

  LOCAL_MODULE    :=Hello
  LOCAL_SRC_FILES := Hello.c Hello2.c
  LOCAL_LDLIBS += -llog
  include $(BUILD_SHARED_LIBRARY)
  #include $(BUILD_STATIC_LIBRARY)

加载  liblog.so

liblog.so 在NDK 的路径:

C代码添加:

#include <android/log.h>

#define  TAG "JNI_TAG"

#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, TAG, __VA_ARGS__)

#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)

Log.h  :

Log

优先级

 

 

Prio   优先级      tag标记   可以过滤

 

5.在jni 目录下  执行   ndk-build: 编译生成.so文件。

  • 6.调用:
package com.example.JNIDDemo1;

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

public class MyActivity extends Activity {
    private TextView textView;
    Button  btn;
    /**
     * Called when the activity is first created.
     */
    JNI jni =new JNI();//创建类
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        inittextView();
      //  add();
    }
    public  void  inittextView(){
        textView=(TextView)findViewById(R.id.tv_log);
        btn=(Button)findViewById(R.id.btn);
        btn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                add();
            }
        });
    }
    public   void  add(){
        int   ret=0;

        ret=   jni.add(12,23);//调用native方法。
        Log.i("MyActivity","计算结果:"+jni.add(12,23));
        // textView.setText("计算结果:"+jni.add(12,23));
        textView.setText("计算结果:"+jni.add(12,23)+"字符串:"+jni.getString("abc"));
    }
}

C 调用 java:

    1. Javap  命令  生成   sigenature  签名

  Eclipse:在bin/classes 目录下 :

 

 

Class  路径:

F:\ideaCode\JNIDDemo1\out\production\JNIDDemo1\com\example\JNIDDemo1

在JNI.class 路径下输入 javap -p  -s   JNI  输出如下信息:

 

 

 

Android  studio  :class 文件位置:

F:\AS\AndNDKDemo\app\build\intermediates\classes\debug\com\foundation\zengjx\andndkdemo

 

 

 

 

猜你喜欢

转载自blog.csdn.net/oDianZi1234567/article/details/84959786