本篇重点学习一下NDK的核心开发库Bionic API库,通过Bionic API我们可以开发各种各样的原生功能,真正发挥原生开发的强大功能。
Bionic是Android平台为了使用C/C++进行原生应用程序开发所有提供的POSIX标准C库。它是Google为Android操作系统提供的BSD标准C库的衍生库。同时Bionic是专门为移动计算而精心设计的,针对移动设备上有限的CPU周期和可用内存进行了裁剪以提高工作效率。
Bionic尽管是C标准库,但是它不以任何方式与其它C库二进制兼容。也就是说Bionic和其它C库不兼容,无法进行交叉编译和相互引用。
Bionic提供了C标准库,类型定义,函数和少数Android特有的特性。
主要功能可以概括如下:
1. 内存管理
2. 文件输入输出
3. 字符串操作
4. 数学函数
5. 日期和时间
6. 进程控制
7. 信号处理
8. 网络套接字
9. 多线程
10. 用户和组
11. 系统配置
12. 命名服务切换
Bionic是专门为移动计算而精心设计的,所以Bionic不会支持所有C标准库函数。也就是说它是C标准库的一个子集。
如图
适用于在代码中定义的静态变量和全局变量,静态分配在应用程序启动时自动发生。
适用于函数参数和函数内的局部变量,自动内存分配在函数调用时和函数内部有效,在函数使用完会自动释放。
动态分配是在程序运行时进行的,取决于当时的内存环境,如果需要的内存较大,可能会出现分配不成功的情况。
C语言不提供对内置动态内存管理的支持,不像C++,可以使用new关键字来动态生成一个对象。C语言必须使用C库提供的函数来使用动态内存。
C语言使用内存管理函数,需要包括stdlib.h头文件
C语言使用malloc函数来动态分配内存,malloc函数原型如下:
void* malloc(size_tbyte_count);
示例如下:
const char *str2="hello world"; char *str=(char*)malloc(strlen(str2)); strcpy(str,str2);
C语言使用realloc函数来调整内存大小,realloc函数原型如下:
void* realloc(void* p, size_t byte_count)
示例如下:
const char *str2="hello world"; int len=strlen(str2); char *str=(char*)malloc(strlen(str2)); strcpy(str,str2); str=(char*)realloc(str,len+4); str=(char*)realloc(str,len-4);
C语言使用free函数来释放用malloc,realloc函数分配的内存,不能使用free函数释放C++通过new关键字分配的内存。
free函数原型如下:
void free(void* p);
示例如下:
const char *str2="hello world"; int len=strlen(str2); char *str=(char*)malloc(strlen(str2)); strcpy(str,str2); free(str);
C++提供了对动态内存管理的内置支持,可以通过new和delete关键字来管理动态内存。
在C++中,强烈建议使用new和delete关键字来管理动态内存,因为new关键字会调用C++类的构造函数,delete关键字同样会调用C++类的析构函数。而C语言的内存分配函数则无法实现类似的功能。
std::string *str3=new std::string("hello world"); printf(str3->c_str());
std::string *str3=new std::string("hello world"); printf(str3->c_str()); delete str3; int *array=new int32_t[10]; for(int i=0;i<10;i++) { array[i]=i; } delete[] array;
需要引入stdio.h头文件
fopen |
打开文件流 |
fread |
从文件流里面读取数据 |
fwrite |
往文件流里面写入数据 |
fprintf |
把格式化的内容写入文件流 |
fscanf |
从文件流里面读取格式化的内容 |
fgets |
从文件流里面读取一行文本 |
fputs |
把以换行符结尾的文本写入文件流 |
fgetc |
从文件流中读取一个字符 |
fputc |
把一个字符写入到文件流 |
fseek |
移动文件指针 |
ftell |
返回当前文件指针的位置 |
feof |
判断文件指针是否已经到达文件结尾 |
ferror |
判断文件流是否有错误 |
fflush |
把文件流中的数据刷新到文件中 |
fclose |
关闭文件流 |
void readFile(const char * filePath){ if(filePath==NULL) return; FILE *file=fopen(filePath,"r"); fseek(file,SEEK_END); long fileSize=ftell(file); fseek(file,SEEK_SET); char *data=new char[fileSize]; fread(data, sizeof(char),fileSize/sizeof(char),file); fclose(file); }
void writeFile(const char * filePath){ if(filePath==NULL) return; FILE *file=fopen(filePath,"w"); const char *data="hello world"; int len=strlen(data); fwrite(data, sizeof(char),len,file); fflush(file); fclose(file); }
Bionic允许原生代码启动并与其它进行交互。原生代码可以执行shell命令,也可以在后台启动一个进程并与之通信。
需要引入stdlib.h头文件
用system函数向shell传递命令
int result=system("mkdir /data/data/com.kgdwbb.jnidemo/files/file1"); if(result==-1 || result==127){ //shell 命令执行失败 }
system函数不提供向进程发送和接收命令的通道,我们需要使用popen函数,popen函数的原型如下:
FILE *popen(const char * cmd, const char * type);
第一个参数是要执行的命令,比如向shell传递”ls”命令
第二个参数是打开通道的方式,比如“r”读取,“w”写入
示例如下:
FILE *file=popen("ls","r"); if(file==NULL) return; char *line=new char[1024]; while (fgets(line,1024,file)!=NULL){ //do something } fclose(file);
通过Bionic提供的API我们可以很方便的访问Android系统属性。
需要引入的头文件是:#include
访问系统属性的函数表
int __system_property_get(const char *name, char *value); |
获取系统属性 |
int __system_property_set(const char *key, const char *value); |
改变系统属性 |
int __system_property_read(const prop_info *pi, char *name, char *value); |
读取系统属性 |
const prop_info *__system_property_find(const char *name); |
查找系统属性 |
示例如下:
const char *propName="ro.product.model"; char *value=new char; __system_property_get(propName,value); __system_property_set(propName,"newModel"); const prop_info *info= __system_property_find(propName); char *name=new char; char *value2=new char; __system_property_read(info,name,value2);
需要引入的头文件是:#include
getuid函数用来获取用户ID
getgid函数用来获取用户的组ID
示例如下:
uid_t uid=getuid(); gid_t gid=getgid();
getlogin函数用来获取安装应用程序的用户名
示例如下:
char *userName=getlogin();
本篇重点介绍了Bionic库相关的基础知识,相信大家学完这一篇,对Bionic库应该有比较深刻的认识。当然还有一些Bionic库的特性没有介绍,后面几篇文章会重点介绍。