复习

  • 1.什么是指针?
  • 2.指针的作用?
  • 3.指针相关的运算符? & *
  • 4.指针的数据类型和指针类型 char *p; int *q;
  • 5.指针变量保存带有类型地址
  • 6.指针的运算

指针和数组的关系

  • 指针变量保存数组名 int a[6]; int *p = a;

    a[i]	<=> 	*(a + i)	<=>	*(p + i)	<=> 	p[i]
    

字符串

  • c语言中使用字符数组保存字符串 char buf[5],字符串保存时需要多一个字节保存字符串结束标志’\0’
  • 用字符指针char *p找到字符串
  • 用string.h头文件提供字符串处理相关函数声明

结构体

  • 用结构体定义新数据类型 struct 姓名 学号 年龄 成绩 char name[20] int id; int age; float score;

函数指针

  • 函数的入口地址:二进制代码在代码段起始位置,函数名代表函数的入口地址。
  • 函数指针变量保存函数入口地址

    void fun1(int a);//函数声明
    void fun1(int a){ //函数的定义
    return ;
    }
    
    void *fun2(int a,int *b);//函数的声明
    
    void (*p)(int);//函数指针变量的定义
    
    p = fun1;
    
    void * (*pfun)(int, int *);//函数指针变量定义
    
    

typedef起别名

  • typedef 给存在的数据类型起别名

    int a;//a是一个标识符,是个变量名
    
    typedef int int32_t;//int32_t是一个标识符,int32_t类型别名,int32_t和int等价
    
    struct Student{
    //...
    }stu,*ptr;//stu是一个标识符,是个变量名
    
    typedef struct Student{
    //...
    }stu;//stu是一个标识符,stu是struct Student类型的别名
    
    typedef struct Student *pstu;//pstu是结构体指针类型
    

jni.h头文件


     typedef struct JNINativeInterface* JNIEnv; //JNIEnv * env;变量
    

利用env调用struct JNINativeInterface中的函数指针变量


    //env <=> JNIEnv * <=> struct JNINativeInterface** 二级结构体指针
    jclass      (*FindClass)(JNIEnv*, const char*);
    (**env).FindClass();
    (*env)->FindClass();
    

jni开发相关概念

  • 交叉编译:在一个平台上为另一个平台编译代码,不同的操作系统windows linux Mac OS X,不同处理器架构 X86 ARM Mips
  • 工具链:一套顺序调用的编译工具,形成一个链条
  • 函数库:实现某类功能的函数集合,函数已经编译二进制机器代码。.h头文件声明函数,.so/.a库文件

静态库和动态库

  • 静态库 .a :只用于程序链接,静态链接把库文件中函数实现拷贝到程序中。所以程序运行时候不需库
  • 动态库/共享库 .so : 用于程序链接,只是记录用到的函数,所以在程序运行之前需要先加载动态库

开发工具介绍

  • NDK:Native Development kit 本地开发工具集
  • CDT : C/C++ Development Tools : Eclipse插件
  • Cygwin:在windows下提供一个类unix linux模拟环境

NDK目录结构

  • build 目录下有 .mk文件,指导工程构建和编译
  • docs 开发文档
  • platforms android-18\arch-arm\usr\ include 头文件目录 lib库文件目录
  • prebuilt windows-x86_64\bin make.exe工程管理器
  • samples 开发实例
  • toolchains 工具链
  • ndk-build.cmd 谷歌包装过的make工具

jni开发流程

目标:使用NDK工具编译出本地.so动态库,把.so库文件装入应用程序。

  • 阶段1 编码
  • 新建Android工程,用native声明本地方法,调用本地方法

    /**
    * 用native声明的本地方法
    * @return c语言中提供字符串
    */
    public native String helloFromC();
    
    public void click(View v){
    Toast.makeText(this, helloFromC(), Toast.LENGTH_SHORT).show();
    }
    
  • 新建jni文件夹,在其中新建hello.c源文件,实现本地方法

    #include <jni.h>
    /**
    * jni规范 java中本地方法对应到c里面的函数名如下|
    * Java_包名(包名中的.需改为_)_类名_本地方法名
    * JNIEnv *env:代表jni环境,提供java和c之前数据转换方法
    * jobject obj : 是调用本地方法的类的对象
    */
    jstring Java_com_itheima_hellofromc_MainActivity_helloFromC(JNIEnv *env, jobject obj){
    char buf[] = "Hello From C!";// buf <=> &buf[0] : char *
    //需要转换c中char *字符串为java中的String
    //	jstring NewStringUTF(JNIEnv *env, const char *bytes);
    //	env : JNIEnv * <=> struct JNINativeInterface* *
    //	(**env).NewStringUTF();
    //	(*env)->NewStringUTF();
    return (*env)->NewStringUTF(env, buf);
    }
    
  • 阶段2 编译
  1. 在jni文件夹下新建Android.mk,指导编译
  2. 在path环境变量中指定ndk-build.cmd文件所在路径
  3. 在jni文件夹下执行ndk-build,生成并安装.so文件
  4. 在调用类中加载动态库

    LOCAL_PATH := $(call my-dir)				#指定源文件当前目录
     $(CLEAR_VARS)						#清除LOCAL_XXX编译变量,LOCAL_PATH除外
        := hello					#指定生成(模块名)动态库名hello,生成的动态库文件名libhello.so
    LOCAL_SRC_FILES := hello.c					#
     $(BUILD_SHARED_LIBRARY)				#指定编译目标类型为动态库.so

    static{
    System.loadLibrary("hello");//需要不带前后缀的库名
    }
    

jni开发中常见错误

  1. 找不到库Caused by: java.lang.UnsatisfiedLinkError: Couldn’t load calc-jn1: findLibrary returned null
    • 产生原因1:加载库名错误
    • 产生原因2:生成的库和运行平台不匹配,解决办法在jni文件夹下新建Application.mk文件其中指定 APP_ABI := all 生成所有支持平台的.so库
  2. 找不到方法 java.lang.UnsatisfiedLinkError: Native method not found: com.itheima.resultfromc.MainActivity.resultFromC:(II)I
    • 产生原因1:本地方法名不符合规范
    • 产生原因2:没有加载库

javah命令用法

  • jdk1.6 需在D:\jni02\code\02_ResultFromC\bin\classes 与com同层目录下执行javah
  • jdkl.7 需在D:\jni02\code\02_ResultFromC\src 与com同层目录下执行javah

    //D:\jni02\code\02_ResultFromC\src>javah com.itheima.resultfromc.MainActivity
    //生成com_itheima_resultfromc_MainActivity.h
    JNIEXPORT jint JNICALL Java_com_itheima_resultfromc_MainActivity_result_1from_1c
    (JNIEnv *, jobject, jint, jint);
    

ndk结合eclipse开发

  • 1.新建工程,声明本地方法,调用本地方法
  • 2.给eclipse指定ndk路径
  • 3.添加本地支持,自动生成jni文件及cpp源文件,Android.mk文件,改后缀名
  • 4.添加jni头文件路径
  • 5.javah命令生包含本地函数名头文件,把头文件拷贝jni文件夹,本地函数名拷贝到源文件,实现功能
  • 6.添加加载动态库语句
  • 7.运行程序,自动编译