JNI

一.为什么用JNI
1.JNI扩展了java虚拟机的能力,驱动开发(wifi-hotspot)。操作手机网卡,把手机网卡的状态置为混杂模式
2.Native code 效率高,数学运算,实时渲染的游戏上上,音视频压缩
      android下的极品飞车。重力引擎,碰撞效果的模拟
3.复用一些大公司开发好的类库如 (人脸识别,文件压缩)
        开源的CC代码库,openv intel开发的图形和视频实时处理的方法库,人脸识别
       rmvb 视屏AVI 视屏和音频编解码的代码库 ffmpeg
      opengl c 代码库 用于游戏界面
4.特殊的业务场景
 
二.初识NDK
1.创建一个android工程
2.JAVA代码中声明native方法   public native String helloFromJNI();
3.创建jni目录,编写C代码,方法名字要对应
4.编写Android.mk文件(编译的配置文件)
5.NDK编译生成动态库。
6.JAVA代码load动态库,调用native代码。
 
用javah -jni 生成JNI样式的头文件
 
Android,mk  对C语言的代码库编译的Makefile文件的书写
# 交叉编译器 在编译C代码C++代码依赖的配置文件 linux下makefile的语法的子集
# LOCAL_PATH是定义的一个变量:(call my-dir)把当前 Android.mk的路径获取出来付给LOCAL_PATH
  LOCAL_PATH := $(call my-dir)
# 变量的初始化操作。特点:不会初始化LOCAL_PATH的变量  (也就是不会初始化这句声明前的代码中的变量)
  include $(CLEAR_VARS)
#LOCAL_MODULE:生成模块的名字;libHello.so加lib前缀和.so后缀是linux下makefile约定的。
  LOCAL_MODULE     :=   Hello
  LOCAL_SRC_FILES :=   Hello.c 
  include $(BUILD_SHARED_LIBRARY)
这个是指定你要编译哪些文件
不需要指定头文件,引用哪些依赖,因为编译器会自动找到这些依赖 自动编译
include $(BUILD_SHARED_LIBRARY) BUILD_STATIC_LIBRARY
编译后生成的库类型,如果是静态库.a配置include $(BUILD_STATIC_LIBRARY)
别的参数
LOCAL_CPP_EXTENSION := cc     //指定C++文件的扩展名
LOCAL_MODULE    :=    ndkfoo
LOCAL_SRC_FILES := ndkfoo.cc
LOCAL_LDLIBS += -llog  -lvmsagent -lmpnet - lmpxml -lH26Android //指定加载一些别的什么库 

 
NDK常见错误的处理
1.由于粗心大意将 android.mk写错,这将导致linux模拟器中的ndk-build工具找不到编译文件
$ ndk-build
Android NDK: Your APP_BUILD_SCRIPT points to an unknown file: ./jni/Android.mk   
/cygdrive/f/NDK/build/core/add-application.mk:133: *** Android NDK: Aborting...       。 停止。

2.  android.mk文件中的配置信息 不全或出现错误。
$ ndk-build
/cygdrive/f/NDK/build/core/build-shared-library.mk:23: *** Android NDK: Missing LOCAL_MODULE before including BUILD_SHARED_LIBRARY in jni/Android.mk       。 停止。
3.C语言语法出现了错误,编译不会通过。
$ ndk-build
Compile thumb   : Hello <= Hello.c
jni/Hello.c: In function 'Java_com_example_ndkhelloworld_MainActivity_helloFromC':
jni/Hello.c:9: error: 'err' undeclared (first use in this function)
jni/Hello.c:9: error: (Each undeclared identifier is reported only once
jni/Hello.c:9: error: for each function it appears in.)
jni/Hello.c:9: error: expected ';' before 'c'
/cygdrive/f/NDK/build/core/build-binary.mk:240: recipe for target `obj/local/armeabi/objs/Hello/Hello.o' failed
make: *** [obj/local/armeabi/objs/Hello/Hello.o] Error 1
4.java层的提示C代码库没有找到,C代码库没有加载进来。(静态代码库出现了问题)
Caused by: java.lang.UnsatisfiedLinkError: Library Hel1o not found
5.C代码函数签名出现错误。(仔细检查代码名书写的错误)
Caused by: java.lang.reflect.InvocationTargetExceptio n
Caused by: java.lang.UnsatisfiedLinkError: hello_From_C
6.逻辑性的错误,比如使用了已经回收的内存空间,访问了没权限访问的内存空间。
断点执行找到出错点。通过log方式来观察错误点。
在C语言中使用logcat
Android.mk文件增加LOCAL_LDLIBS += -llog
C代码中增加
#include<android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS __ );
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS __ );
LOGI("info\n");
LOGN("debug\n");
 
NDK中处理中文问题
将C语言写的代码换成UTF8格式的编码,GBK的代码会产生异常。
1.ndk-r4-crystal4版本一样会产生错误。因为低版本以iso-8859-1读取数据,在代码中显示的转码。
  例:Toast.makeText(this, new String(getStringFromC().getBytes("iso-8859-1"),  "utf-8"), 1).show();
//将iso-8859-1编码的字符串转化为 utf-8编码格式 

java和C间互传数据
1.将java语言中提供的String类型数据转化成C语言中 *char类型的转化函数
char*     Jstring2CStr(JNIEnv*     env,     jstring     jstr)
{
   char*     rtn     =     NULL;
   jclass     clsstring     =     (*env)->FindClass(env,"java/lang/String");        //找到string的class
   jstring     strencode     =     (*env)->NewStringUTF(env,"GB2312");          //得到一个java字符串"GB2312"
   jmethodID     mid     =     (*env)->GetMethodID(env,clsstring,     "getBytes",     "(Ljava/lang/String;)[B");       
      //从String类包找到名为"getBytes"的方法,这个方法的签名:(参数)返回值,参数:String 返回值:[B    bytes类型的数组
   jbyteArray     barr=     (jbyteArray)(*env)->CallObjectMethod(env,jstr,mid,strencode); // String .getByte("GB2312");
        //参数1,:env,参数二:要转换的字符串,参数三:上面取得的函数ID,参数四:要转换的数据类型。转换成Byte数组
   jsize     alen     =     (*env)->GetArrayLength(env,barr);                //得到数组的长度
   jbyte*     ba     =     (*env)->GetByteArrayElements(env,barr,JNI_FALSE);                //获取byte数组中的每一个元素,并放入一个内存空间里
   if(alen     >     0)
   {
     rtn     =     (char*)malloc(alen+1);                 //"\0"      申请一块内存空间,这块内存空间的大小为:alen+1既多加一个“\0”的长度
     memcpy(rtn,ba,alen);                                              //将ba里面的内容拷贝到char * rtn这个指针所指的内存里
     rtn[alen]=0;                                                                              //把rtn数组里的最后一个元素赋值为0
   }
   (*env)->ReleaseByteArrayElements (env,barr,ba,alen+1);
   //释放ByteArray数组所占的空间   JNIenv    env , jinArray barr ba(刚才申请的空间赋给了谁)   alen+1数组的长度
   return rtn;                                                                                        //返回char * rtn的指针 地址
}
2.NDK中提供的取得INT数组长度的函数:int len = (*env)->GetArrayLength(env,intArray);
3.NDK中定义一个int类型数组的函数:jint* intarr = (*env)->GetIntArrayElements(env,intArray,0);// 取得数组的首地址
//env为传进来的参数(java虚拟机结构体C实现的指针 包含有很多jni方法),intArray为已有的一个数组,0表示可以拷贝。
//jobject   obj   代表的是调用这个C代码的java对象 
//对于在java中声明的静态方法,传来的参数是jclass .传来的是类
C语言   申请的空间如果显示的释放掉了,但是在java中又引用
 
企业用JNI开发的流程
例:阿里旺旺安装版的程序安全级别比较高,登入模块它里面使用C,C++来写的并把这些模块编译成代码库,
login(char *usernam, char *passworld){
       
       
}
javah 生成native方法的签名
jint Java_xxx_xxx_xxx_xxx_Login(JNIENV *env,jobject obj,jstring username,jstring password){
//生成名由Java+java中定义本地接口所在的包名+类名+方法名
        char * cusername =     Jstring2CStr( env,     username)
        char * cuserpassword =     Jstring2CStr(env,     password)
       int   result =   login(char *usernam, char *passworld);
        if(result == 200){
                        //验证成功
        }else if(result == 400){
                        //验证失败
        }
}
 
C语言函数模块调用java空方法
使用javap 打印出java中函数的签名。
 public class DataProvider {
 
 public void helloFromJava(){
  System.out.println("hello from java");
 }
 
 public int Add(int x,int y){
  return x+y;
 }
 
 public void printString(String s){
  System.out.println("java"+s);
 }
 public  static void printStaticStr(String s){
  System.out.println("java"+s);
 }
 //让C代码调用对应的java代码
 public native void callImethod1();
 public native void callImethod2();
 public native void callImethod3();
 //调用静态的C代码
 public native void callImethod4();
}
C语言中的实现
#include<stdio.h>
#include<jni.h>
#include "com_example_ndkcallback_DataProvider.h"
#include<android/log.h>
#include<malloc.h>
#define LOG_TAG "System.out.c"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callImethod1
  (JNIEnv *env, jobject obj){
 //在C代码里面调用java代码里面的方法
 //java 反射
 //1.找到java代码的class文件jclass (*FindClass)(JNIEnv*, const char *)
 jclass dpclass = (*env)->FindClass(env,"com/example/ndkcallback/DataProvider");
 if(dpclass == 0){
  LOGI("find class error");
  return ;
 }
 LOGI("find class !");
 //2.寻找里面的方法  jmethodID (*GetMethodID)(JNIEnv *,jclass, const char *, const char *);
 jmethodID methodID = (*env)->GetMethodID(env,dpclass,"helloFromJava()","()V");
 if(methodID == 0){
   LOGI("find method error");
   return ;
  }
 LOGI("find method !");
 //第一个参数为env,第二个参数为取得的类名,第三个参数为函数名,第四个参数为()为函数所带的参数,V为返回值为空
 //3.调用里面的方法 void (*callVoidMethod)(JNIEnv *,jobject, jmethodID,...)
 (*env)->CallVoidMethod(env,obj,methodID);
 //第一个参数:env,第二个为obj,第三个为取得的方法名
};

JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callImethod2
  (JNIEnv *env, jobject obj){
 //在C代码里面调用java代码里面的方法
  //java 反射
  //1.找到java代码的class文件jclass (*FindClass)(JNIEnv*, const char *)
  jclass dpclass = (*env)->FindClass(env,"com/example/ndkcallback/DataProvider");
  if(dpclass == 0){
   LOGI("find class error");
   return ;
  }
  LOGI("find class !");
  //2.寻找里面的方法  jmethodID (*GetMethodID)(JNIEnv *,jclass, const char *, const char *);
  jmethodID methodID2 = (*env)->GetMethodID(env,dpclass,"Add","(II)I");
  if(methodID2 == 0){
    LOGI("find method error");
    return ;
   }
  LOGI("find method !");
  //第一个参数为env,第二个参数为取得的类名,第三个参数为函数名,第四个参数为()为函数所带的参数,V为返回值为空
  //3.调用里面的方法jint (*CallIntMethod)(JNIEnv *,jobject,jmethodID,...);
  jint result = (*env)->CallIntMethod(env,obj,methodID2,5,10);
  //第一个参数:env,第二个为obj,第三个为取得的方法名
  LOGI("result = %d",result);
};

JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callImethod3
  (JNIEnv *env, jobject obj){
 //在C代码里面调用java代码里面的方法
 //java 反射
 //1.找到java代码的class文件jclass (*FindClass)(JNIEnv*, const char *)
 jclass dpclass = (*env)->FindClass(env,"com/example/ndkcallback/DataProvider");
 if(dpclass == 0){
  LOGI("find class error");
  return ;
 }
 LOGI("find class !");
 //2.寻找里面的方法  jmethodID (*GetMethodID)(JNIEnv *,jclass, const char *, const char *);
 jmethodID methodID3 = (*env)->GetMethodID(env,dpclass,"printString","(Ljava/lang/String;)V");
 if(methodID3 == 0){
  LOGI("find method error");
  return ;
 }
 LOGI("find method !");
 //第一个参数为env,第二个参数为取得的类名,第三个参数为函数名,第四个参数为()为函数所带的参数,V为返回值为空
 //3.调用里面的方法 void (*callVoidMethod)(JNIEnv *,jobject, jmethodID,...)
 (*env)->CallVoidMethod(env,obj,methodID3,(*env)->NewStringUTF(env,"Hello in C"));
  //第一个参数:env,第二个为obj,第三个为取得的方法名
 //LOGI("result = %d",result);
};

JNIEXPORT void JNICALL Java_com_example_ndkcallback_DataProvider_callImethod4
  (JNIEnv * env, jobject obj){
 //在C代码里面调用java代码里面的方法
 //java 反射
 //1.找到java代码的class文件jclass (*FindClass)(JNIEnv*, const char *)
 jclass dpclass = (*env)->FindClass(env,"com/example/ndkcallback/DataProvider");
 if(dpclass == 0){
  LOGI("find class error");
  return ;
 }
 LOGI("find class !");
 //2.寻找里面的方法  jmethodID (*GetMethodID)(JNIEnv *,jclass, const char *, const char *);
 //注意:如果要寻找的方法是静态的方法那就不能直接获取method的ID
 //jmethodID methodID4 = (*env)->GetMethodID(env,dpclass,"printStaticStr","(Ljava/lang/String;)V");
 jmethodID (*GetStaticMethodID)(JNIEnv *,jclass,const char *,const char*);
 jmethodID StaticMethod = (*env)->GetStaticMethodID(env,dpclass,"printStaticStr","(Ljava/lang/String;)V");
 if(StaticMethod == 0){
  LOGI("find method error");
  return ;
 }
 LOGI("find method !");
 //第一个参数为env,第二个参数为取得的类名,第三个参数为函数名,第四个参数为()为函数所带的参数,V为返回值为空
 //3.调用里面的方法 void (*callStaticVoidMethod)(JNIEnv *,jclass, jmethodID,...)
 (*env)->CallStaticVoidMethod(env,dpclass,StaticMethod,(*env)->NewStringUTF(env,"Hello static in C"));
 //第一个参数:env,第二个为obj,第三个为取得的方法名
 //LOGI("result = %d",result);
};

 复杂多类中调用native方法
jobject (*AllocObject)(JNIEnv *, jclass
(*env)->AllocObject (env,dpclass);
 
 
(?????)ANDROID音频采集回放旅程.    基本流程为:
          audiorecord采集  ->filter  ->g.711编码  ->RTP送  ->RTP收  ->g.711解码  ->filter  ->回放
      其中    "filter->g.711编码->RTP送->RTP收->g.711解码->filter      框架为已有的MEDIASTREAM框架,C代码.      采集和回放均是用JAVA语言,ANDROID类实现.
    现在问题是:
                            1.采集和回放端如何与已有的MEDIASTREAM框架FILTER通信,音频数据如何从JAVA传送给C语言,可不可以直接调用?                                 
                            2.      audiorecord采集  ->filter                filter  ->回放      能不能用SOCKET建立通信?   


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
资源包主要包含以下内容: ASP项目源码:每个资源包中都包含完整的ASP项目源码,这些源码采用了经典的ASP技术开发,结构清晰、注释详细,帮助用户轻松理解整个项目的逻辑和实现方式。通过这些源码,用户可以学习到ASP的基本语法、服务器端脚本编写方法、数据库操作、用户权限管理等关键技术。 数据库设计文件:为了方便用户更好地理解系统的后台逻辑,每个项目中都附带了完整的数据库设计文件。这些文件通常包括数据库结构图、数据表设计文档,以及示例数据SQL脚本。用户可以通过这些文件快速搭建项目所需的数据库环境,并了解各个数据表之间的关系和作用。 详细的开发文档:每个资源包都附有详细的开发文档,文档内容包括项目背景介绍、功能模块说明、系统流程图、用户界面设计以及关键代码解析等。这些文档为用户提供了深入的学习材料,使得即便是从零开始的开发者也能逐步掌握项目开发的全过程。 项目演示与使用指南:为帮助用户更好地理解和使用这些ASP项目,每个资源包中都包含项目的演示文件和使用指南。演示文件通常以视频或图文形式展示项目的主要功能和操作流程,使用指南则详细说明了如何配置开发环境、部署项目以及常见问题的解决方法。 毕业设计参考:对于正在准备毕业设计的学生来说,这些资源包是绝佳的参考材料。每个项目不仅功能完善、结构清晰,还符合常见的毕业设计要求和标准。通过这些项目,学生可以学习到如何从零开始构建一个完整的Web系统,并积累丰富的项目经验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值