复习
- 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 编译
- 在jni文件夹下新建Android.mk,指导编译
- 在path环境变量中指定ndk-build.cmd文件所在路径
- 在jni文件夹下执行ndk-build,生成并安装.so文件
- 在调用类中加载动态库
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开发中常见错误
- 找不到库Caused by: java.lang.UnsatisfiedLinkError: Couldn’t load calc-jn1: findLibrary returned null
- 产生原因1:加载库名错误
- 产生原因2:生成的库和运行平台不匹配,解决办法在jni文件夹下新建Application.mk文件其中指定 APP_ABI := all 生成所有支持平台的.so库
- 找不到方法 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.运行程序,自动编译