解决ViewPager嵌套滑动冲突:android-auto-scroll-view-pager最佳实践
代码背景博主想实现一个跨平台通信机制来连接LSP服务端想通过unix domian socket来实现这个。
在java实现客户端连接时出现了问题JDK 原生搞不定 Windows 命名管道Java 的网络库只认 Socket 家族不认“文件”家族。
所以想直接访问这个通道就必须用 Kernel32 。
问题分析描述 在 Windows 上如果用一个线程 ReadFile 阻塞等数据主线程想 WriteFile 发消息时会直接卡死。
博主在测试过程中发现能收到服务端的通知但是在发送消息后服务端无法收到。
经过判断是在发送时卡住然后研究出具体原因。
由此记录一下过程原因 同步模式下读和写共用同一个句柄锁。
读不完锁不放写不进。
避坑指南 在 Windows 环境下 必须先“轮询”再“读取” 。
使用 PeekNamedPipe 检查是否有数据。
没数据就 Thread.sleep(
让出句柄锁。
选择同步的理由代码简洁度 重叠 I/O 在 Java/JNA 里的实现非常臃肿容易引入内存泄漏或指针错误。
兼容性 LSP 服务端通常一次只允许一个连接双句柄方案可能会导致服务端报错“管道忙”。
代码实现packageorg.example.lspClient;importcom.sun.jna.platform.win
Kernel32;importcom.sun.jna.platform.win
WinBase;importcom.sun.jna.platform.win
WinNT;importcom.sun.jna.ptr.IntByReference;importorg.tinylog.Logger;importjava.io.*;importjava.net.StandardProtocolFamily;importjava.net.UnixDomainSocketAddress;importjava.nio.ByteBuffer;importjava.nio.channels.SocketChannel;importjava.nio.charset.StandardCharsets;importjava.nio.file.Path;importjava.util.Arrays;importjava.util.function.Consumer;/** * 跨平台 UDS 传输层具有 LSP 协议感知能力 */publicclassUdsTransportimplementsCloseable{privatefinalbooleanisWindows;privateWinNT.HANDLE hPipe;privateSocketChannelchannel;privatefinalByteArrayOutputStreambuffernewByteArrayOutputStream();privateConsumerStringmessageListener;privatevolatilebooleanrunningtrue;publicUdsTransport(StringsocketPath)throwsIOException{StringosSystem.getProperty(os.name).toLowerCase();this.isWindowsos.contains(win);if(isWindows){connectWindowsPipe(socketPath);}else{connectUnixSocket(socketPath);}startReadingThread();}publicvoidsetOnMessage(ConsumerStringlistener){this.messageListenerlistener;}privatevoidconnectWindowsPipe(StringpipeName)throwsIOException{this.hPipeKernel
INSTANCE.CreateFile(pipeName,WinNT.GENERIC_READ|WinNT.GENERIC_WRITE,0,null,WinNT.OPEN_EXISTING,0,null);if(WinBase.INVALID_HANDLE_VALUE.equals(hPipe)){interrorKernel
INSTANCE.GetLastError();thrownewIOException(Failed to connect to Named Pipe: pipeName (Error: error));}}privatevoidconnectUnixSocket(StringsocketPath)throwsIOException{this.channelSocketChannel.open(StandardProtocolFamily.UNIX);this.channel.connect(UnixDomainSocketAddress.of(Path.of(socketPath)));}publicvoidsendMessage(StringjsonContent)throwsIOException{byte[]bodyjsonContent.getBytes(StandardCharsets.UTF_
;StringheaderContent-Length: body.length\r\n\r\n;writeRaw(header.getBytes(StandardCharsets.US_ASCII));writeRaw(body);}privatevoidwriteRaw(byte[]bytes)throwsIOException{if(isWindows){IntByReferencewrittennewIntByReference();booleansuccessKernel
INSTANCE.WriteFile(hPipe,bytes,bytes.length,written,null);if(!success){interrKernel
INSTANCE.GetLastError();thrownewIOException(WriteFile failed with error: err);}if(written.getValue()bytes.length){Logger.warn(Partial write detected: written.getValue()/bytes.length);return;}}else{ByteBuffernioBufferByteBuffer.wrap(bytes);while(nioBuffer.hasRemaining()){intwrittenchannel.write(nioBuffer);if(written
{thrownewIOException(Channel write stalled);}}}Logger.info(Wrote bytes to: Arrays.toString(bytes));}privatevoidstartReadingThread(){ThreadthreadnewThread(()-{byte[]readBuffernewbyte[8192];try{while(running){intnreadToBuffer(readBuffer);if(n
{synchronized(buffer){buffer.write(readBuffer,0,n);}processBuffer();}else{// 给WriteFile 竞争句柄锁的机会Thread.sleep(
;}}}catch(Exceptione){if(running)System.err.println(Transport read error: e.getMessage());}},UdsTransport-Reader);thread.setDaemon(true);thread.start();}privateintreadToBuffer(byte[]target)throwsIOException{if(isWindows){IntByReferenceavailnewIntByReference();// 轮询是否有数据booleanpeekOkKernel
INSTANCE.PeekNamedPipe(hPipe,null,0,null,avail,null);if(!peekOk){interrKernel
INSTANCE.GetLastError();if(err
thrownewEOFException(Pipe closed);return0;}if(avail.getValue()
{return0;// 当前没数据返回 0 让出 CPU 和句柄锁}IntByReferencereadnewIntByReference();// 此时 ReadFile 不会阻塞因为它已经知道有数据了booleansuccessKernel
INSTANCE.ReadFile(hPipe,target,target.length,read,null);if(!success){interrKernel
INSTANCE.GetLastError();if(err
thrownewEOFException(Pipe closed);if(err
returntarget.length;// ERROR_MORE_DATAthrownewIOException(ReadFile failed: err);}returnread.getValue();}else{ByteBuffernioBufByteBuffer.wrap(target);intnchannel.read(nioBuf);if(n-
thrownewEOFException(Socket closed);returnn;}}privatevoidprocessBuffer(){synchronized(buffer){while(true){byte[]databuffer.toByteArray();if(data.length
break;StringtempStrnewString(data,StandardCharsets.US_ASCII);intheaderEndtempStr.indexOf(\r\n\r\n);if(headerEnd-
break;intcontentLength-1;String[]linestempStr.substring(0,headerEnd).split(\r\n);for(Stringline:lines){if(line.toLowerCase().startsWith(content-length:)){contentLengthInteger.parseInt(line.substring(
.trim());}}if(contentLength-
break;inttotalSizeheaderEnd4contentLength;if(data.lengthtotalSize)break;byte[]bodyBytesnewbyte[contentLength];System.arraycopy(data,headerEnd4,bodyBytes,0,contentLength);StringmessagenewString(bodyBytes,StandardCharsets.UTF_
;// 异步回调if(messageListener!null){messageListener.accept(message);}byte[]remainingnewbyte[data.length-totalSize];System.arraycopy(data,totalSize,remaining,0,remaining.length);buffer.reset();try{buffer.write(remaining);}catch(IOExceptionignored){}}}}Overridepublicvoidclose()throwsIOException{runningfalse;if(isWindows){if(hPipe!null)Kernel
INSTANCE.CloseHandle(hPipe);}else{if(channel!null)channel.close();}}}
鉴黄师在线下载-鉴黄师在线下载应用