实例详解Android中JNI的使用方法
Posted 人家风雪客
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了实例详解Android中JNI的使用方法相关的知识,希望对你有一定的参考价值。
目录
- 前言
- 1.导入C语言的类
- 2.接着导入android.mk文件
- 3.我们配置一下build.gradle文件
- 4.好了,此时可以编译一下项目了
- 5.此时我们可以找一下我们生成的so包了
- 6.将生成的so文件拷入src/main/jniLibs中
- 7.调用C语言方法的Activity如下
- 总结
前言
做Android开发的程序员应该都知道,Android的开发语言我们都是在使用JAVA(Kotlin和Flutter我们暂时不考虑)。但是,有时候我们也需要使用到C语言进行一些功能的开发。这个时候我们就需要用到JNI了。
1.导入C语言的类
首先我们需要把C语言写的功能类放入我们的项目中。这里我直接从资料中找了一个,毕竟我不会写。路径在src/main/jni中
find_name.cpp
1 #include <jni.h> 2 #include <string.h> 3 4 #include <stdio.h> 5 #include <stdlib.h> 6 #include <string.h> 7 #include <netdb.h> 8 #include <sys/stat.h> 9 #include <sys/types.h> 10 #include <sys/select.h> 11 #include <sys/socket.h> 12 #include <netinet/in.h> 13 #include <arpa/inet.h> 14 15 #define send_MAXSIZE 50 16 #define recv_MAXSIZE 1024 17 18 struct NETBiosNS { 19 unsigned short int tid; //unsigned short int 占2字节 20 unsigned short int flags; 21 unsigned short int questions; 22 unsigned short int answerRRS; 23 unsigned short int authorityRRS; 24 unsigned short int additionalRRS; 25 unsigned char name[34]; 26 unsigned short int type; 27 unsigned short int classe; 28 }; 29 30 char *getNameFromIp(const char *ip); 31 32 extern "C" 33 34 jstring Java_com_hao_cmake_MainActivity_cpuFromJNI(JNIEnv* env, jobject thiz, jstring ip) { 35 const char* str_ip; 36 str_ip = env->GetStringUTFChars(ip, 0); 37 return env->NewStringUTF(getNameFromIp(str_ip)); 38 } 39 40 char *getNameFromIp(const char *ip) { 41 char str_info[1024] = { 0 }; 42 struct sockaddr_in toAddr; //sendto中使用的对方地址 43 struct sockaddr_in fromAddr; //在recvfrom中使用的对方主机地址 44 char send_buff[send_MAXSIZE]; 45 char recv_buff[recv_MAXSIZE]; 46 memset(send_buff, 0, sizeof(send_buff)); 47 memset(recv_buff, 0, sizeof(recv_buff)); 48 int sockfd; //socket 49 unsigned int udp_port = 137; 50 int inetat; 51 if ((inetat = inet_aton(ip, &toAddr.sin_addr)) == 0) { 52 sprintf(str_info, "[%s] is not a valid IP address\\n", ip); 53 return str_info; 54 } 55 if ((sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) { 56 sprintf(str_info, "%s socket error sockfd=%d, inetat=%d\\n", ip, sockfd, inetat); 57 return str_info; 58 } 59 bzero((char*) &toAddr, sizeof(toAddr)); 60 toAddr.sin_family = AF_INET; 61 toAddr.sin_addr.s_addr = inet_addr(ip); 62 toAddr.sin_port = htons(udp_port); 63 64 //构造netbios结构包 65 struct NETBIOSNS nbns; 66 nbns.tid = 0x0000; 67 nbns.flags = 0x0000; 68 nbns.questions = 0x0100; 69 nbns.answerRRS = 0x0000; 70 nbns.authorityRRS = 0x0000; 71 nbns.additionalRRS = 0x0000; 72 nbns.name[0] = 0x20; 73 nbns.name[1] = 0x43; 74 nbns.name[2] = 0x4b; 75 int j = 0; 76 for (j = 3; j < 34; j++) { 77 nbns.name[j] = 0x41; 78 } 79 nbns.name[33] = 0x00; 80 nbns.type = 0x2100; 81 nbns.classe = 0x0100; 82 memcpy(send_buff, &nbns, sizeof(nbns)); 83 int send_num = 0; 84 send_num = sendto(sockfd, send_buff, sizeof(send_buff), 0, 85 (struct sockaddr *) &toAddr, sizeof(toAddr)); 86 if (send_num != sizeof(send_buff)) { 87 sprintf(str_info, 88 "%s sendto() error sockfd=%d, send_num=%d, sizeof(send_buff)=%d\\n", 89 ip, sockfd, send_num, sizeof(send_buff)); 90 shutdown(sockfd, 2); 91 return str_info; 92 } 93 int recv_num = recvfrom(sockfd, recv_buff, sizeof(recv_buff), 0, 94 (struct sockaddr *) NULL, (socklen_t*) NULL); 95 if (recv_num < 56) { 96 sprintf(str_info, "%s recvfrom() error sockfd=%d, recv_num=%d\\n", ip, 97 sockfd, recv_num); 98 shutdown(sockfd, 2); 99 return str_info; 100 } 101 //这里要初始化。因为发现linux和模拟器都没问题,真机上该变量若不初始化,其值就不可预知 102 unsigned short int NumberOfNames = 0; 103 memcpy(&NumberOfNames, recv_buff + 56, 1); 104 char str_name[1024] = { 0 }; 105 unsigned short int mac[6] = { 0 }; 106 int i = 0; 107 for (i = 0; i < NumberOfNames; i++) { 108 char NetbiosName[16]; 109 memcpy(NetbiosName, recv_buff + 57 + i * 18, 16); 110 //依次读取netbios name 111 if (i == 0) { 112 sprintf(str_name, "%s", NetbiosName); 113 } 114 } 115 sprintf(str_info, "%s|%s|", ip, str_name); 116 for (i = 0; i < 6; i++) { 117 memcpy(&mac[i], recv_buff + 57 + NumberOfNames * 18 + i, 1); 118 sprintf(str_info, "%s%02X", str_info, mac[i]); 119 if (i != 5) { 120 sprintf(str_info, "%s-", str_info); 121 } 122 } 123 return str_info; 124 }
这里要注意一点,jstring Java_com_hao_cmake_MainActivity_cpuFromJNI方法中,com_hao_cmake是我们的包名,MainActivity是调用JNI的Activity名称,cpuFromJNI是对应方法的名字。
2.接着导入Android.mk文件
这个文件也是放在jni文件夹中
1 LOCAL_PATH := $(call my-dir) 2 include $(CLEAR_VARS) 3 4 # 指定so库文件的名称 5 LOCAL_MODULE := jni_mix 6 # 指定需要编译的源文件列表 7 LOCAL_SRC_FILES := find_name.cpp 8 # 指定C++的编译标志 9 LOCAL_CPPFLAGS += -fexceptions 10 # 指定要加载的静态库 11 #LOCAL_WHOLE_STATIC_LIBRARIES += android_support 12 # 指定需要链接的库 13 LOCAL_LDLIBS := -llog 14 15 include $(BUILD_SHARED_LIBRARY) 16 $(call import-module, android/support)
3.我们配置一下build.gradle文件
android -> defaultConfig 下添加
1 externalNativeBuild{ 2 ndkBuild{ 3 abiFilters "arm64-v8a","armeabi-v7a" 4 } 5 }
android 下添加
1 externalNativeBuild { 2 ndkBuild { 3 path file(\'src/main/jni/Android.mk\') 4 } 5 } 6 packagingOptions{ 7 pickFirst \'lib/arm64-v8a/libjni_mix.so\' 8 pickFirst \'lib/armeabi-v7a/libjni_mix.so\' 9 }
4.好了,此时可以编译一下项目了
5.此时我们可以找一下我们生成的so包了
在build → intermediates → ndkBuild → debug → obj → local下,我们可以找到我们生成的相关配置平台的so文件
6.将生成的so文件拷入src/main/jniLibs中
这个样子的
7.调用C语言方法的Activity如下
1 public class MainActivity extends AppCompatActivity { 2 3 public native String cpuFromJNI(String ip); 4 5 static { 6 System.loadLibrary("jni_mix"); 7 } 8 9 @Override 10 protected void onCreate(Bundle savedInstanceState) { 11 super.onCreate(savedInstanceState); 12 setContentView(R.layout.activity_main); 13 String str = cpuFromJNI("192.168.0.163"); 14 Toast.makeText(this,str,Toast.LENGTH_SHORT).show(); 15 } 16 }
这样我们就完成了用C语言类生成so包,并使用JNI进行调用的全流程。
注意:在使用JNI进行调用的时候,我们的环境一定要有NDK,这个我这里就不说了,大家如果没有搭建需要上网找找搭建一下。
以上是关于实例详解Android中JNI的使用方法的主要内容,如果未能解决你的问题,请参考以下文章
Android项目中JNI技术生成并调用.so动态库实现详解