介绍
JNI含义:java native interface
JNI好处:
1、JNI可以扩展java虚拟机的能力,让java代码可以调用驱动
2、C/C++的效率要高,通过jni把耗时操作方法C/C++可以提高java运行效率
3、java代码编译成的.class
文件安全性较差, 可以通过jni把重要的业务逻辑放到c/c++去实现,c/c++反编译比较困难,安全性较高.
C基本语法
1、C的基本数据类型
java基本数据类型:boolean(1个字节),byte(1),char(2),short(2),int(4),long(8),float(4),double(8)
C的基本数据类型:char(1个字节), int(4), float(4), double(8), long(4), short(2), signed, unsigned, void
signed:有符号数,可以表示负数;unsigned:无符号数,不可以表示负数。signed和unsigned只能用来修饰整形变量char、short、int、long。
C没有boolean和byte,C用0和非0表示false和true。
2、占位符和字符串
占位符:
%d - int
%ld – long int
%lld - long long
%hd – short
%c - char
%f - float
%lf – double
%u – 无符号数
%x – 十六进制输出 int 或者long int 或者short int
%o - 八进制输出
%s – 字符串
占位符不要乱用,要选择正确的对应类型,否则可能会损失精度。
C没有String类型,C的字符串实际就是字符数组。C字符串两种定义方式:
//注意'\0'字符串结束符, []只能再变量名之后
char str[] = {'q','w','e','r','t','\0'};
//这种定义方式不用写结束符,可以表示汉字
char str[] = "你好";
C字符串不检查下标越界,使用时要注意。
C的控制台输入
scanf("占位符", &地址);//& 取地址符
3、内存地址的概念
声明一个变量,就会立即为这个变量申请内存,一定会有一个对应的内存地址;没有地址的内存是无法使用的;内存的每一个字节都有一个对应的地址;内存地址用一个16进制数来表示;32位操作系统最大可以支持4G内存。
4、指针
int i = 123;
//一般计算机中用16进制数来表示一个内存地址
printf("%#x\n",&i);
//int* int类型的指针变量 pointer指针 指针变量只能用来保存内存地址
//用取地址符&i 把变量i的地址取出来 用指针变量pointer 保存了起来
//此时我们可以说 指针pointer指向了 i的地址
int* pointer = &i;
printf("pointer的值 = %#x\n",pointer);
printf("*pointer的值%d\n",*pointer);
*pointer = 456;
printf("i的值是%d\n",i);
system("pause");
未赋值的指针称为野指针。
5、指针交换两个数的值
所有传递其实本质都是值传递,引用传递本质是把地址传递过去,引用传递其实也是传递一个值,但是这个值是一个内存地址。
void swap(int* p, int* p2){
int temp = *p;
*p = *p2;
*p2 = temp;
}
6、数组和指针的关系
数组占用的内存空间是连续的
数组变量保存的地址,是第0个元素地址,也就是首地址。
&array
和&array[0]
值一样。
*(p + 1)
:指针位移一个单位,一个单位是多少个字节,取决于指针的类型。
7、指针的长度
不管变量的类型是什么,它的内存地址的长度一定是相同的;类型不同只决定变量占用的内存空间不同;32位环境下,内存地址长度都是4个字节,64位环境下,内存地址长度都是8个字节。
8、多级指针
二级指针变量只能保存一级指针变量的地址,有几个*
就是几级指针
int i = 123;
//int类型一级指针
int* p = &i;
//int 类型 二级指针 二级指针只能保存一级指针的地址
int** p2 = &p;
//int 类型 三级指针 三级指针只能保存二级指针的地址
int*** p3 = &p2;
//通过p3 取出 i的值
printf("***p3 = %d\n", ***p3);
9、堆、栈、静态内存、动态内存
栈内存:系统自动分配,系统自动销毁,连续的内存区域,向低地址扩展,大小固定,栈上分配的内存称为静态内存。
静态内存分配:子函数执行完,子函数中的所有局部变量都会被销毁,内存释放,但内存地址不可能被销毁,只是地址上的值没了。
堆内存:程序员手动分配(java:new,c:malloc),空间不连续,大小取决于系统的虚拟内存,C程序员手动回收free,java自动回收,堆上分配的内存称为动态内存。
#include<stdio.h>
#include<stdlib.h>
main(){
printf("请输入班级的人数:");
int count;
scanf("%d",&count);
//申请一块堆内存
int* pointer = malloc(sizeof(int)*count);
int i;
for(i = 0;i<count;i++){
printf("请输入第%d个学生的学号:",i+1);
scanf("%d", pointer+i);
}
for(i = 0;i<count;i++){
printf("第%d个学生的学号是:%d\n",i+1,*(pointer+i));
}
printf("请输入插班生的人数:");
//声明一个变量increment用来保存 插班生的人数
int increment;
//接受用户的输入
scanf("%d",&increment);
//重新申请一块足够大的内存
//如果 malloc申请到的内存后面还有足够的空间 realloc会在malloc申请的内存空间后继续申请足够大的内存空间
//如果 malloc申请到的内存后面没有足够的空间 realloc会找到一块足够大的堆内存 并且把 malloc申请到的内存中的值复制过来
pointer = realloc(pointer,sizeof(int)*(count+increment));
for(i = count;i<count+increment;i++){
printf("请输入第%d个学生的学号:",i+1);
scanf("%d", pointer+i);
}
for(i = count;i<count+increment;i++){
printf("第%d个学生的学号是:%d\n",i+1,*(pointer+i));
}
system("pause");
}
10、结构体
结构体的大小大于等于结构体中每一变量的占字节数的和;结构体的大小是最大的那个变量所占字节数的整数倍。
c结构体类似java的class。struct来声明c的结构体。C结构体中不能定义函数,可以定义函数指针 。
#include<stdio.h>
#include<stdlib.h>
void study(){
printf("good good study!\n");
}
typedef struct Student{
int age; //8
int score; // 4
char sex; //1
void(*studypointer)();
} stud;
main(){
stud stu = {18,100,'f'};
stu.studypointer = &study;
stu.studypointer();
struct Student* stuPointer = &stu;
printf("*stuPointer.age = %d\n",(*stuPointer).age);
(*stuPointer).sex ='m';
printf("stu.sex = %c\n",stu.sex);
printf("stuPointer->age = %d",stuPointer->age);
//printf("stu.age = %hd\n",stu.age);
//printf("stu.score = %d\n",stu.score);
//printf("stu.sex = %c\n",stu.sex);
// printf("结构体student占%d个字节\n",sizeof(stu));
system("pause");
}
11、联合体
union
长度等于联合体中定义的变量当中最长的那个,联合体只能保存一个变量的值,联合体共用同一块内存
c++
c++开发jni代码时 env不再是结构体Jninativeinterface的二级指针。_JNIEnv
是C++的结构体,C++的结构体可以定义函数。env
是JNIEnv
的一级指针,也就是结构体_JNIEnv
的一级指针。env->
来调用结构体里的函数。
am命令
am命令:在adb shell里可以通过am命令进行一些操作如启动activity Service 启动浏览器等等。
#include <jni.h>
#include <stdlib.h>
#include <stdio.h>
#include <android/log.h>
#define LOG_TAG "System.out"
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
int ppid;
JNIEXPORT void JNICALL Java_com__cfork_demo_MainActivity_cfork
(JNIEnv * env, jobject obj){
int pid = fork();
//fork成功的分叉出一个子进程 会返回当前进程的id 但是只能在主进程中fork成功
//在子进程中运行fork 会返回0 但是不能再分叉出新的进程
//fork的返回值可能三种 >0 == 0 <0
FILE* file;
if(pid>0){
LOGD("pid = %d",pid);
}else if(pid == 0){
//拿到父进程的进程编号
LOGD("pid == 0");
while(1){
ppid = getppid();
//如果父进程的进程编号为1 说明父进程被杀死了
if(ppid == 1){
LOGD("ppid =%d",ppid);
file = fopen("/data/data/com.cfork.demo","r");
if(file == NULL){
//打开网页 调用am命令
execlp("am", "am", "start", "--user","0","-a", "android.intent.action.VIEW", "-d", "http://www.baidu.com", (char *) NULL);
}else{
execlp("am", "am", "start", "--user","0", "-n" , "com.cfork.demo/com.cfork.demo.MainActivity",(char *) NULL);
}
break;
}
LOGD("sub process is running");
sleep(2);
}
}else{
LOGD("pid<0 ");
}
}
JNI
c本地函数命名规则:Java_包名_类名_本地方法名
。
参数jobject thiz
:调用本地函数的java对象,在这个例子中,就是MainActivity的实例。
参数JNIEnv* env
:是结构体JNINativeInterface的二级指针。JNIEnv是结构体JNINativeInterface的一级指针,操作一级指针用(*env)->
。JNINativeInterface结构体中定义了大量的函数指针,这些函数指针在jni开发中很常用。
C代码
#include <stdlib.h>
#include <stdio.h>
#include <jni.h>
jstring Java_com_vcredit_cdemo_MainActivity_stringFromJNI(JNIEnv* env,jobject thiz){
char* cstr = "hello from c!";
return (*env)->NewStringUTF(env,cstr);
}
C++代码
#include <jni.h>
#include <string>
extern "C" JNIEXPORT jstring JNICALL
Java_com_vcredit_cdemo_MainActivity_stringFromJNI(
JNIEnv *env,
jobject /* this */) {
std::string hello = "Hello from C++";
return env->NewStringUTF(hello.c_str());
}
java传参数给c
Java给C传递int
,String
和int[]
,并且返回:
#include <jni.h>
#include <stdlib.h>
#include <string.h>
//传递数字,并返回数字
JNIEXPORT jint JNICALL
Java_com_vcredit_cdemo_MainActivity_intFromJNI(JNIEnv *env, jobject thiz, jint a, jint b) {
return a + b;
}
char *_JString2CStr(JNIEnv *env, jstring jstr) {
char *rtn = NULL;
jclass clsstring = (*env)->FindClass(env, "java/lang/String");
jstring strencode = (*env)->NewStringUTF(env, "GB2312");
jmethodID mid = (*env)->GetMethodID(env, clsstring, "getBytes", "(Ljava/lang/String;)[B");
jbyteArray barr = (jbyteArray) (*env)->CallObjectMethod(env, jstr, mid,
strencode); // String .getByte("GB2312");
jsize alen = (*env)->GetArrayLength(env, barr);
jbyte *ba = (*env)->GetByteArrayElements(env, barr, JNI_FALSE);
if (alen > 0) {
rtn = (char *) malloc(alen + 1); //"\0"
memcpy(rtn, ba, alen);
rtn[alen] = 0;
}
(*env)->ReleaseByteArrayElements(env, barr, ba, 0);
return rtn;
}
//传递字符串,并返回字符串
JNIEXPORT jstring JNICALL
Java_com_vcredit_cdemo_MainActivity_stringChange(JNIEnv *env, jobject thiz, jstring string) {
char *cstr = _JString2CStr(env, string);
int length = strlen(cstr);
int i;
for (i = 0; i < length; i++) {
*(cstr + i) += 1;
}
return (*env)->NewStringUTF(env, cstr);
}
//传递int数组,并返回数组
JNIEXPORT jintArray JNICALL
Java_com_vcredit_cdemo_MainActivity_arrIncrease(JNIEnv *env, jobject thiz, jintArray jArray) {
jsize length = (*env)->GetArrayLength(env, jArray);
jint *arrayPointer = (*env)->GetIntArrayElements(env, jArray, NULL);
int i;
for (i = 0; i < length; i++) {
*(arrayPointer + i) += 10;
}
(*env)->SetIntArrayRegion(env, jArray, 0, length, arrayPointer);
return jArray;
}
在C中打印log
#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__)
//使用
LOGD("length = %d",length);
c调用Java
C调用Java空参方法
JNIEXPORT void JNICALL
Java_com_vcredit_cdemo_MainActivity_callbackvoidmethod(JNIEnv* env, jobject thiz) {
jclass claz = (*env)->FindClass(env, "com/vcredit/cdemo/MainActivity");
jmethodID methodID = (*env)->GetMethodID(env, claz, "helloFromJava", "()V");
(*env)->CallVoidMethod(env, thiz, methodID);
}
public void helloFromJava() {
Log.d("-----rrrrr", "hello from java");
}
注意FindClass路径用
/
。
C调用Java有Int参数方法并返回
JNIEXPORT void JNICALL
Java_com_vcredit_cdemo_MainActivity_callbackintmethod(JNIEnv *env, jobject thiz) {
jclass claz = (*env)->FindClass(env, "com/vcredit/cdemo/MainActivity");
jmethodID methodID = (*env)->GetMethodID(env, claz, "add", "(II)I");
int result = (*env)->CallIntMethod(env, thiz, methodID, 6, 6);
LOGD("result = %d", result);
}
public int add(int x, int y) {
return x + y;
}
C调用Java有String参数方法
JNIEXPORT void JNICALL
Java_com_vcredit_cdemo_MainActivity_callbackStringmethod(JNIEnv *env, jobject thiz) {
jclass claz = (*env)->FindClass(env, "com/vcredit/cdemo/MainActivity");
jmethodID methodID = (*env)->GetMethodID(env, claz, "printString", "(Ljava/lang/String;)V");
jstring result = (*env)->NewStringUTF(env, "hello from c");
(*env)->CallVoidMethod(env, thiz, methodID, result);
}
public void printString(String s) {
Log.d("-----rrrrr", s);
}