1.记一次源码追踪分析,从Java到JNI,再到JVM的C++:fileChannel.map()为什么快;源码分析map方法,put方法
2.java是如何调用native方法?hotspot源码分析必会技能
3.jnihookåç
4.å®åjniå¼åï¼
记一次源码追踪分析,从Java到JNI,再到JVM的布林交易源码C++:fileChannel.map()为什么快;源码分析map方法,put方法
前言
在系统IO相关的系统调用有read/write,mmap,sendfile等这些。
其中read/write是普通的读写,每次都需要将buffer从用户空间拷贝到内核空间;
而mmap使用的是内存映射,会将磁盘文件对应的页映射(拷贝)到内核空间的page cache,并记录到用户进程的韦根源码页表中,使得用户空间也可以像操作用户空间一样操作该文件的映射,最后再由操作系统来讲该映射(脏页)回写到磁盘;
sendfile则使用的是零拷贝技术,在mmap的基础上,当发送数据的时候只拷贝fd和offset等元数据信息,而将数据主体直接拷贝至protocol buffer,实现了内核数据零冗余的零拷贝技术
本文地址:/post//
问题/目的问题1Java中哪些API使用到了mmap问题2怎么知道该API使用到了mmap,如何追踪程序的系统调用目的1源码中分析验证,从Java到JNI,再到C++:fileChannel.map()使用的是系统调用mmap目的2源码验证分析:调用mmapedByteBuffer.put(Byte[])时JVM在搞些什么?mmap比普通的read/write快在哪?揭晓答案1mmap在Java NIO中的体现/使用看一个例子
// 1GBpublic static final int _GB = 1**;File file = new File("filename");FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();MappedByteBuffer mmapedByteBuffer = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, _GB);for (int i = 0; i < _GB; i++) { count++;mmapedByteBuffer.put((byte)0);}其中fileChannel.map()底层使用的就是系统调用mmap,函数签名为: public abstract MappedByteBuffer map(MapMode mode,long position, long size)throws IOException
答案2程序执行的系统调用追踪/** * @author Tptogiar * @description * @date /5/ - : */public class TestMappedByteBuffer{ public static final int _4kb = 4*;public static final int _GB= 1**;public static void main(String[] args) throws IOException, InterruptedException { // 为了方便在日志中找到本段代码的开始位置和结束位置,这里利用文件io来打开始标记FileInputStream startInput = null;try { startInput = new FileInputStream("start1.txt");startInput.read();} catch (IOException e) { e.printStackTrace();}File file = new File("filename");FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, _GB); //我们想分析的语句问题2for (int i = 0; i < _GB; i++) { map.put((byte)0); // 下文中需要分析的语句目的2}// 打结束标记FileInputStream endInput = null;try { endInput = new FileInputStream("end.txt");endInput.read();} catch (IOException e) { e.printStackTrace();}}}把上面这段代码编译后把“.class”文件拉到linux执行,并用linux上的abberation 策略源码strace工具记录其系统调用日志,拿到日志文件我们可以在日志中看到以下信息(关于怎么拿到日志可以参照我的博文:无(代写)):
注:日志有多行,这里只选取我们关注的
// ...// 看到了我们打的开始标志openat(AT_FDCWD, "start1.txt", O_RDONLY) = -1 ENOENT (No such file or directory)// ... // 打开文件,文件描述符fd为6openat(AT_FDCWD, "filename", O_RDWR|O_CREAT, ) = 6// 判断文件状态fstat(6, { st_mode=S_IFREG|, st_size=, ...}) = 0// ... // 判断文件状态fstat(6, { st_mode=S_IFREG|, st_size=, ...}) = 0// 进行内存映射mmap(NULL, , PROT_READ|PROT_WRITE, MAP_SHARED, 6, 0) = 0x7f2fd6cd// ...// 程序退出exit(0)// 看到了我们打的结束标志openat(AT_FDCWD, "end.txt", O_RDONLY) = -1 ENOENT (No such file or directory)在上面程序的系统调用日志中我们确实看到了我们打的开始标志,结束标志。在开始标志和结束标志之间我们看到了我们的文件"filename"确实被打开了,文件描述符fd = 6;在打开文件后紧接着又执行了系统调用mmap,这一点我们Java代码一致,这样,我们就验证了我们答案1中的结论,可以开始我们的下文了
源码追踪分析,从Java到JNI,再到JVM的nginx源码优化C++目的1寻源之旅:fileChannel.map()我们知道我们执行Java代码fileChannel.map()确实会在底层调用系统调用,那怎么在源码中得到验证呢?怎么落脚于源码进行分析呢?下面开始我们的寻源之旅
FileChannelImpl.map() 注:由于代码较长,这里代码中略去了一些我们不关注的,比如异常捕获等
public MappedByteBuffer map(MapMode mode, long position, long size)throws IOException{ // ...try { // ...synchronized (positionLock) { // ...long mapPosition = position - pagePosition;mapSize = size + pagePosition;try { // !我们要找的语句就在这!addr = map0(imode, mapPosition, mapSize);} catch (OutOfMemoryError x) { // 如果内存不足,先尝试进行GCSystem.gc();try { Thread.sleep();} catch (InterruptedException y) { Thread.currentThread().interrupt();}try { // 再次试着mmapaddr = map0(imode, mapPosition, mapSize);} catch (OutOfMemoryError y) { // After a second OOME, failthrow new IOException("Map failed", y);}}} // ...} finally { // ...}}上面函数源码中真正执行mmap的语句是在addr = map0(imode, mapPosition, mapSize),于是我们寻着这里继续追踪
FileChannelImpl.map0()
// Creates a new mappingprivate native long map0(int prot, long position, long length)throws IOException;可以看到,该方法是一个native方法,所以后面的源码我们需要到这个FileChannelImpl.class对应的fileChannelImpl.c中去看,所以我们需要去找到JDK的源码
在JDK源码中我们找到fileChannelImpl.c文件
fileChannelImpl.c 根据JNI的对应规则,我们找到该文件内对应的Java_sun_nio_ch_FileChannelImpl_map0方法,其源码如下:
JNIEXPORT jlong JNICALLJava_sun_nio_ch_FileChannelImpl_map0(JNIEnv *env, jobject this, jint prot, jlong off, jlong len){ void *mapAddress = 0;jobject fdo = (*env)->GetObjectField(env, this, chan_fd);jint fd = fdval(env, fdo);int protections = 0;int flags = 0;if (prot == sun_nio_ch_FileChannelImpl_MAP_RO) { protections = PROT_READ;flags = MAP_SHARED;} else if (prot == sun_nio_ch_FileChannelImpl_MAP_RW) { protections = PROT_WRITE | PROT_READ;flags = MAP_SHARED;} else if (prot == sun_nio_ch_FileChannelImpl_MAP_PV) { protections =PROT_WRITE | PROT_READ;flags = MAP_PRIVATE;}// !我们要找的语句就在这里!mapAddress = mmap(0,shell 源码 博客/* Let OS decide location */len,/* Number of bytes to map */protections,/* File permissions */flags,/* Changes are shared */fd, /* File descriptor of mapped file */off); /* Offset into file */if (mapAddress == MAP_FAILED) { if (errno == ENOMEM) { JNU_ThrowOutOfMemoryError(env, "Map failed");return IOS_THROWN;}return handle(env, -1, "Map failed");}return ((jlong) (unsigned long) mapAddress);}我们要找的语句就上面代码中的mapAddress = mmap(0,len,protections,flags,fd,off),至于为什么不是直接的mmap,而是mmap,是因为这里的mmap是一个宏,在文件上方有其定义,如下:
#define mmap mmap至此,我们就在源码中得到验证了我们问题2中的结论:fileChannelImpl.map()底层使用的是mmap系统调用
目的2寻源之旅:mmapedByteBuffer.put(Byte[ ])接着我们来看看当我们调用mmapedByteBuffer.put(Byte[])JVM底层在搞些什么动作
MappedByteBuffer ?首先我们得知道,当我们执行MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, _GB)时,实际返回的对象是DirectByteBuffer类的实例,因为MappedByteBuffer为抽象类,且只有DirectByteBuffer继承了它,看下面两图就明白了
DirectByteBuffer 于是我们找到DirectByteBuffer内的put(Byte[ ])方法
public ByteBuffer put(byte x) { unsafe.putByte(ix(nextPutIndex()), ((x)));return this;}可以看到该方法内实际是调用Unsafe类内的putByte方法来实现功能的,所以我们还得去看Unsafe类
Unsafe.class
public native voidputByte(long address, byte x);该方法在Unsafe内是一个native方法,所以所以我们还得去看unsafe.cpp文件内对应的实现
unsafe.cpp
在JDK源码中,我们找到unsafe.cpp
在这份源码内,没有使用JNI内普通加前缀的方法来形成对应关系
不过我们还是能顺着源码的蛛丝轨迹找到我们要找的方法
注意到源码中有这样的注册机制,所以我们可以知道我们要找的代码就是上图中标注的代码
顺藤摸瓜,我们就找到了该方法的定义
UNSAFE_ENTRY(void, Unsafe_SetNative##Type(JNIEnv *env, jobject unsafe, jlong addr, java_type x)) \UnsafeWrapper("Unsafe_SetNative"#Type); \JavaThread* t = JavaThread::current(); \t->set_doing_unsafe_access(true); \void* p = addr_from_java(addr); \*(volatile native_type*)p = x; \t->set_doing_unsafe_access(false); \UNSAFE_END \该方法内主要的逻辑语句就是以下两句:
/** * @author Tptogiar * @description * @date /5/ - : */public class TestMappedByteBuffer{ public static final int _4kb = 4*;public static final int _GB= 1**;public static void main(String[] args) throws IOException, InterruptedException { // 为了方便在日志中找到本段代码的开始位置和结束位置,这里利用文件io来打开始标记FileInputStream startInput = null;try { startInput = new FileInputStream("start1.txt");startInput.read();} catch (IOException e) { e.printStackTrace();}File file = new File("filename");FileChannel fileChannel = new RandomAccessFile(file, "rw").getChannel();MappedByteBuffer map = fileChannel.map(FileChannel.MapMode.READ_WRITE, 0, _GB); //我们想分析的语句问题2for (int i = 0; i < _GB; i++) { map.put((byte)0); // 下文中需要分析的语句目的2}// 打结束标记FileInputStream endInput = null;try { endInput = new FileInputStream("end.txt");endInput.read();} catch (IOException e) { e.printStackTrace();}}}0至此,我们就知道:其实我们调用mmapedByteBuffer.put(Byte[ ])时,JVM底层并不需要涉及到系统调用(这里也可以用strace工具追踪从而得到验证)。也就是说通过mmap映射的空间在内核空间和用户空间是共享的,我们在用户空间只需要像平时使用用户空间那样就行了————获取地址,设置值,而不涉及用户态,内核态的切换
总结fileChannelImpl.map()底层用调用系统函数mmap
fileChannelImpl.map()返回的其实不是MappedByteBuffer类对象,而是DirectByteBuffer类对象
在linux上可以通过strace来追踪系统调用
JNI中“.class”文件内方法与“.cpp”文件内函数的对应关系不止是前缀对应的方法,还可以是注册的方式,这一点的追寻代码的时候有很大帮助
directByteBuffer.put()方法底层并没有涉及系统调用,也就不需要涉及切态的性能开销(其底层知识执行获取地址,设置值的操作),所以mmap的性能就比普通读写read/write好
...
原文:/post/java是如何调用native方法?hotspot源码分析必会技能
在深入研究JDK源码,如并发包和Thread相关部分时,往往会遇到native修饰的方法,它们隐藏在层层方法的底层。native方法的存在并非偶然,它是解决Java语言与操作系统直接交互的关键。Java作为高层语言,需要JVM作为桥梁,将Java指令转换为可以直接操作系统的C或C++代码,这就是native方法的用武之地。
JDK、JRE和JVM的关系是这样的:JDK包含JRE,其中的JVM负责执行Java代码并进行操作系统间的转换。在OpenJDK源码中,特别是hotspot实现的JVM中,能找到native方法的具体实现。JNI(Java Native Interface)技术用于模拟Java调用C或C++编写的native方法,确保跨平台的兼容性。
让我们通过实践来理解这个过程。首先,创建一个简单的Java类,通过javac编译,生成JavaCallC.class文件。然后使用javah命令生成JavaCallC.h头文件,这是C语言调用Java的关键部分,需要与Java代码中的native方法签名匹配。接着,编写C代码(Cclass.c),编译成动态链接库libJavaCallC.so,并将库文件路径添加到LD_LIBRARY_PATH环境变量中。
最后,执行JavaCallC命令,如果一切顺利,会看到"Java_JavaCallC_cMethod call succ"的输出,表明Java成功调用了native方法。在尝试过程中可能会遇到各种问题,但通过一步步的调试和学习,我们可以逐步掌握这个过程。
jnihookåç
jnihookæ¯ä¸ç§éè¿ä¿®æ¹Java Native Interface (JNI)æ¥å®ç°å¯¹Androidåºç¨ç¨åºçå¨æä¿®æ¹åæ§å¶çææ¯ãJNIæ¯Javaåæ¬å°ä»£ç ï¼é常æ¯C/C++ï¼ä¹é´çæ¡¥æ¢ï¼å®å 许å¨Java代ç ä¸è°ç¨æ¬å°ä»£ç ï¼å¹¶å®ç°Javaä¸åºå±æä½ç³»ç»æ硬件ç交äºã
jnihookçåçæ¯éè¿ä¿®æ¹JNIå½æ°è¡¨ï¼å称为å½æ°æé表ï¼ä¸çå½æ°æéæ¥å®ç°å¯¹ç®æ å½æ°çæ¿æ¢ãå½æ°è¡¨æ¯ä¸ä¸ªåå¨JNIå½æ°æéçæ°æ®ç»æï¼å®å°Javaæ¹æ³ååºå±å®ç°çæ¬å°æ¹æ³å¯¹åºèµ·æ¥ãéè¿ä¿®æ¹å½æ°è¡¨ä¸çå½æ°æéï¼å¯ä»¥å°ç®æ å½æ°çè°ç¨éå®åå°èªå®ä¹çå½æ°æè Hookå½æ°ï¼ä»èå®ç°å¯¹ç®æ å½æ°çä¿®æ¹ææ§å¶ã
jnihookçåå æ¯ä¸ºäºå®ç°ä¸äºç¹å®çç®çï¼æ¯å¦å®ç°åºç¨ç¨åºçéåå·¥ç¨ãç ´è§£ãè°è¯ãHookæ¡æ¶çãå®å¯ä»¥ç¨æ¥ä¿®æ¹åºç¨ç¨åºçè¡ä¸ºï¼çªç ´åæçéå¶ï¼å®ç°èªå®ä¹çåè½æè ç»è¿æäºå®å ¨æ£æµãåæ¶ï¼jnihookä¹ä¸ºå¼å人åæä¾äºä¸ç§è°è¯ååæåºç¨ç¨åºçæ段ï¼å¸®å©ä»ä»¬ç解åºç¨ç¨åºçå·¥ä½åçåå é¨æºå¶ã
ç¶èï¼éè¦æ³¨æçæ¯ï¼jnihookææ¯å¨ä¸äºæ åµä¸å¯è½ä¼è¢«æ»¥ç¨ï¼æ¯å¦ç¨äºéæ³éåå·¥ç¨ãç ´è§£çççæ´»å¨ï¼ä»èä¾µç¯äºè½¯ä»¶çç¥è¯äº§æååæ³æçãå æ¤ï¼å¨ä½¿ç¨jnihookææ¯æ¶ï¼éè¦éµå®æ³å¾æ³è§ï¼å¹¶å°é软件å¼åè çæçãåæ¶ï¼å¼åè ä¹åºè¯¥å¢å¼ºåºç¨ç¨åºçå®å ¨æ§ï¼é²æ¢è¢«æ¶æçjnihookæ»å»ã
å®åjniå¼åï¼
å¦ä½ç¼åå®å软件
1ãè§ææ2é1å®åçæ¬ï¼åå¼å§ä¹ä¸ç¥ééé¢æäºä»ä¹ææ¯é¾åº¦ï¼ä½æ¯è¦åçç®æ å·²ç»æç¡®äºï¼èä¸ä¹æ²¡æç°æçï¼ç¢°å°é®é¢å°±æ¥èµæï¼æ ¢æ ¢å°è§£å³ï¼è¿æ ·æçæ¾ç¢ï¼å¦ä¹ çææé常好ã
2ãä¸é¢æå°±å¼å§ä»ç»æ们å¼åéè¦ç¨ç软件ï¼Xcode(软件ä¸è½½å°åï¼developer.apple/xcode/)ï¼IOS模æå¨ã
3ãéæ±åæãç¡®å®è¦å¼åæä¸æ¬¾è½¯ä»¶çåæ¶è¦å¯¹éæ±è¿è¡åæï¼å¼åçappæå®é å®ç¨æä¹æå¯ä»¥ãå¼åè éè¦å¯¹è½¯ä»¶ç³»ç»è¿è¡æ¦è¦è®¾è®¡ï¼å³ç³»ç»è®¾è®¡ã
4ãè°·ææ¨åºçAppInventorAndroidAppå¼åå·¥å ·å¯ä»¥è®©ä½ ä» éè¿ææå¼çç®åæä½å°±å¯ä»¥å建èªå·±çAndroidAppã对äºé£äºä¸ºäºç¹å®ç®çæ³è¦å¨æå°è¯å¼åä¸ä¸ªç®ååºç¨çç¨æ·ã
5ãEclipseADTEclipseADTæ¯Eclipseå¹³å°ä¸ç¨æ¥å¼åAndroidåºç¨ç¨åºçæ件TheSDKandAVDManagerè¯¥å·¥å ·å å«å¾å¤éè¦çåè½ï¼å æ¬ç®¡çä¸åçAndroidSDKçæ¬ï¼æ建ç®æ ï¼ï¼Androidççæ¬ä¼å¤ï¼APIä¸æäºå ¼å®¹æ§é®é¢ã
6ãä¸é¢ï¼ä¸ç§è±æ就为大家æ®åä¸ä¸å®å软件å¼åå ¥é¨ç¥è¯ãå级é¶æ®µè¦å¦ä¹ çå 容é ç½®ç¯å¢ï¼å ¨é¨éç¨åæ°çæ¬SDKãADTãECLIPSEãJDKãç¼åæè è¿è¡æå¦ç¤ºä¾è¿æ¶é´ä¸»è¦è¿è¡ç示ä¾å¦HELLOWORLDï¼è¿æSDK带çä¾åã
Androidä¸æä¹éè¿JNIç¼ç¨å»åéadbæ令æ§å¶ææºç¼åä¸ä¸ªCç¨åºï¼ä½¿ç¨system(âcmdline)ï¼ç´æ¥è°ç¨å½ä»¤è¡ç¨åºå³å¯ã
(constchar*)(*env)-GetStringUTFChars(envï¼inputStrï¼JNI_FALSE)ï¼LOGI(dufresne---%sï¼(constchar*)str)ï¼//éç¥èææºæ¬å°ä»£ç ä¸åéè¦éè¿str访é®Javaå符串ã
$cd~/project/Android/JNITest/bin$cd~/project/Android/JNITest/binæ们ç¨lså½ä»¤æ¥çï¼å¯ä»¥çå°binç®å½ä¸æ个classesç®å½ï¼å ¶ç®å½ç»æ为classes/org/tonny/jniï¼å³classesçåç®å½ç»ææ¯androidå·¥ç¨çå åorg.tonny.jniã
å¦ä½ç¨javaè¿è¡å®ååºç¨çå¼åç´æ¥ç¾åº¦æç´¢å®åå¼åæç¨ï¼èµæºé常å¤ãå å»æJavaåCå¦å¥½å说ã
æ¯çï¼Javaå¯ä»¥ç¨äºå¼åå®ååºç¨ãå®ååºç¨çå¼åå¯ä»¥ä½¿ç¨Javaç¼ç¨è¯è¨åAndroidSDKï¼è½¯ä»¶å¼åå·¥å ·å ï¼ãè³äºâå®åæ¯å¦ä¼ä¸è½½å± æ°âï¼æä¸å¤ªæç½æ¨çææã
第ä¸æ¥ï¼å®è£ jdkï¼é ç½®jdkç¯å¢ãç¾åº¦æjdké 置第äºæ¥ï¼å®è£ AndroidStudio+SDKãæè Eclipse+ADT+AndroidSDKã第ä¸æ¥ï¼éè¦Androidæºåæè å®è£ èææºãå¦æçé®ï¼è¯·æåºã
ä¸ï¼JavaSEç¼ç¨Javaæ¯ä¸ç§é¢å对象çå¼åè¯è¨ï¼Androidæä½ç³»ç»çåºç¨å±ä½¿ç¨Javaè¯è¨æ¥å¼åï¼æ以è¦æ³è¿è¡Androidå¼åå¿ é¡»æè¯å¥½çJavaåºç¡ã
Androidåºç¨ç¨åºå¼åæ¯ä»¥Javaè¯è¨ä¸ºåºç¡çï¼æ以éè¦ææå®çJavaåºç¡ç¥è¯ãé¦å çæjavaåºæ¬è¯æ³ï¼ç¶åçæ设计模å¼çã
ç¨jniåçå®ååºç¨è½è¢«åç¼è¯å
å°ç¨åºè½¬æ¢ææ¬å°ä»£ç ä¹æ¯ä¸ç§é²æ¢åç¼è¯çæææ¹æ³ãå 为æ¬å°ä»£ç å¾å¾é¾ä»¥è¢«åç¼è¯ãå¼å人åå¯ä»¥éæ©å°æ´ä¸ªåºç¨ç¨åºè½¬æ¢ææ¬å°ä»£ç ï¼ä¹å¯ä»¥éæ©å ³é®æ¨¡å转æ¢ã
æå ç§æ¹å¼æ¥æé«è¢«åç¼è¯å代ç çé¾åº¦ï¼1å ³é®ä»£ç 使ç¨jniè°ç¨æ¬å°ä»£ç ï¼ç¨cæè c++ç¼åï¼å æ¤ç¸å¯¹æ¯è¾é¾äºåç¼è¯2æ··æ·java代ç ãæ··æ·æ¯ä¸æ¹å代ç é»è¾çæ åµä¸ï¼å¢å æ ç¨ä»£ç ï¼æè éå½åï¼ä½¿åç¼è¯åçæºä»£ç é¾äºçæã
ç±äºapkæ¯Androidèææºå è½½çï¼å®æä¸å®çè§èï¼å å¯apkåDalvikæ æ³è¯å«apkäºãå®å ¨é¿å æ¯ä¸å¯è½çï¼æ»æ人è½å¤ç ´è§£åç代ç ãä½æ¯æå ç§æ¹å¼æ¥æé«è¢«åç¼è¯å代ç çé¾åº¦ã
å¦ä½JAVA代ç è°ç¨ï¼jniå§ï¼å®åçSOæ件æ¯linuxä¸çæ件ï¼ç¨cæè c++åçã
ä¸é¢è¯´äºï¼è¿ç§æ¹å¼å ¶å®å¹¶ä¸æ¯çæ£å å¯ä»£ç ï¼å ¶å®ä»£ç è¿æ¯è½å¤è¢«äººåç¼è¯(æ人å¯è½è¯´ï¼ä½¿ç¨proguardä¸çoptimizeé项ï¼å¯ä»¥ä»åèæµå±é¢æ´æ¹ä»£ç ï¼çè³å¯ä»¥è®©JDè¿äºåç¼è¯è½¯ä»¶å¯ä»¥æ æ³å¾å°å 容ã
Androidä¸JNIæ¯ç¼è¯soåºçæºä»£ç ï¼ç¼è¯æååä¼çæSOåºï¼androidä¸æç»æ¯ä½¿ç¨SOåºçã
å®åå¼åè°ç¨åºå±ç¡¬ä»¶æä½æ¹æ³å¦ä¸ï¼ç¼è¯åå®è£ 该çæçapkå³å¯ä½¿ç¨è¯¥ç¨åºè°ç¨åºå±ç¡¬ä»¶é©±å¨ã大æ¦æ´ä¸ªè¿ç¨å°±è¿æ ·ï¼ä¸å±appè°ç¨æ¡æ¶å±çjavaæ¥å£ï¼javaæ¥å£éè¿jniè°ç¨ç¡¬ä»¶æ½è±¡å±å³å¯ã
ï¼Androidåºç¨ç¨åºå±ï¼2ï¼åºç¨ç¨åºæ¡æ¶å±ï¼3ï¼ç³»ç»è¿è¡åºå±ï¼4ï¼Linuxæ ¸å¿å±ã
å¯ä»¥åèä¸ä¸/luoshengyang/article/details/ãéé¢çåæä»ä¸å°å ï¼å®æ´çæè¿°çåºå±å°åºç¨å±çè°ç¨ï¼å¯¹äºç解HALæå¾å¤§ç帮å©ã
Androidåºå±é©±å¨ï¼ä»¥åæä½ç³»ç»å ¨é¨å æ ¸é½æ¯ä½¿ç¨Cè¯è¨åæ±ç¼è¯è¨ç¼ç¨å¼åãä½æ¯androidæä½ç³»ç»æ¬èº«æ¯ä½¿ç¨Javaå¼åï¼ä½¿ç¨JNIè°ç¨æ¬å°çCè¯è¨åºï¼ç±æä½ç³»ç»å®ç°å¯¹åºå±è°ç¨ãiOSç³»ç»æ¯è¹æèªå·±å¼åçï¼æ ¸å¿åéCè¯è¨ã
第åå±ï¼åºäºCçNDK(NativeDevelopmentKit)ï¼å¾å¤å¤§å游ææ¯ç¨è¿ç§æ¹æ³å¼åçï¼ç¸å¯¹äºJDKï¼è¿ç§æ¹æ³çé¨æ§å°±æ´é«äºï¼ç®å使ç¨çæ¯è¾å°ï¼æ¯ç«ç°å¨æ¯æºè½ææºçæ¶ä»£ï¼ç¡¬ä»¶åç½éé½å¤§å¹ æåï¼æ²¡å¿ è¦å¨ä¸å¨å°±Touchåºå±ã
AndroidNDKå¼åç®ä»NDKåSDK以åJNIæä»ä¹å ³ç³»_å®åndkæ¯å¹²åç_ç¾åº¦...对äºandroidç³»ç»çä¸äºå¼åçæ¬ç管ç以å模æå¨ç®¡çãèndkï¼NativeDevelopmentKitï¼è·sdkå·®ä¸å¤çæ¯ä»ä¹æ¯ä¸ä¸ªå¼åå·¥å ·å ãç¨ä»å¼åc/c++æ¯å¾æ¹ä¾¿çãä»æä¸ä¸ªå¼ºå¤§çç¼è¯éåãå ¶å®åè¯ç解éæä¹ä¸æ³å¤è¯´ã
NDKæ¯ç¨æ¥ç»å®åææºå¼å软件ç¨çï¼ä½æ¯åSDKä¸åçæ¯å®ç¨çæ¯Cè¯è¨ï¼èSDKç¨çæ¯Javaè¯è¨ãNDKæ¯ä¸ç³»åå·¥å ·çéåï¼NDKæä¾äºä¸ç³»åçå·¥å ·ï¼å¸®å©å¼åè å¿«éå¼åCï¼æC++ï¼çå¨æåºï¼å¹¶è½èªå¨å°soåjavaåºç¨ä¸èµ·æå æapkã
NDKæ¯ç¨æ¥ç»å®åææºå¼å软件ç¨çï¼ä½æ¯åSDKä¸åçæ¯å®ç¨çæ¯Cè¯è¨ï¼èSDKç¨çæ¯Javaè¯è¨ã
JNI表示JavaNativeIntefaceï¼æ¯Javaä¸å®ä¹çä¸ç§ç¨äºè¿æ¥JavaåC/C++æ¥å£çä¸ç§å®ç°æ¹å¼ãNDK表示NativeDevelopmentKitï¼æ¯Googleå¨Androidå¼åä¸æä¾çä¸å¥ç¨äºå¿«éå建nativeå·¥ç¨çä¸ä¸ªå·¥å ·ã
2024-12-29 15:57550人浏览
2024-12-29 15:142243人浏览
2024-12-29 14:54522人浏览
2024-12-29 14:512959人浏览
2024-12-29 14:512940人浏览
2024-12-29 14:18930人浏览
台南一名騎士,日前行經崇德路口時,看到有2名女子推著輪椅站在斑馬線上,東張西望想過馬路。騎士當下停下來禮讓,但其他汽機車仍繼續行駛,呼嘯而過,讓行人根本不敢動。由於該路口沒有號誌燈,等了約20多秒後沒
1.Vue3 源码中创建应用实例(createApp)流程2.Linux中源码编译安装程序包括哪些基本步骤?3.源码编辑器怎么创建函数4.十二、flink源码解析-创建和启动TaskManager二5