about BufferedReader.readline()

本文通过一个具体的编程案例,讲述了在使用Java进行HTTP请求处理时遇到的一个常见问题:在调试过程中监视表达式导致的数据流提前被消费,进而使得程序逻辑无法按预期执行。作者分享了解决这一问题的经验教训,并提醒开发者在调试涉及流读取的程序时要格外小心。

 

BufferedReader reader =

request.getReader();

 

 

while ( (line = reader.readLine()) != null) {

          jsonStrBuf.append(line);

}

 

昨天在调试该程序的时候, reader.readLine()设置为监视表达式,而输入流只有一行,在程序中,无论怎么样循环都进不去,郁闷啊郁闷。最终解决了这个问题,表达式已经事先读取了流,程序中就不会再读取到,低级的错误!!!

 

以后调试程序,表达式在流的读取时,会影响到程序的顺序执行的。请万分注意!!

//notation: js file can only use this kind of comments //since comments will cause error when use in webview.loadurl, //comments will be remove by java use regexp (function() { if (window.WebViewJavascriptBridge && window.WebViewJavascriptBridge.inited) { return; } var receiveMessageQueue = []; var messageHandlers = {}; var sendMessageQueue = []; var responseCallbacks = {}; var persistentCallbacks = {}; var uniqueId = 1; var lastCallTime = 0; var stoId = null; var FETCH_QUEUE_INTERVAL = 20; var messagingIframe; var CUSTOM_PROTOCOL_SCHEME = "yy"; var QUEUE_HAS_MESSAGE = "__QUEUE_MESSAGE__"; // 创建消息index队列iframe function _createQueueReadyIframe() { messagingIframe = document.createElement('iframe'); messagingIframe.style.display = 'none'; messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE; document.documentElement.appendChild(messagingIframe); } //创建消息体队列iframe function _createQueueReadyIframe4biz() { bizMessagingIframe = document.createElement('iframe'); bizMessagingIframe.style.display = 'none'; document.documentElement.appendChild(bizMessagingIframe); } //set default messageHandler 初始化默认的消息线程 function init(messageHandler) { console.log(222222222222222); if (WebViewJavascriptBridge._messageHandler) { throw new Error('WebViewJavascriptBridge.init called twice'); } _createQueueReadyIframe(); _createQueueReadyIframe4biz(); WebViewJavascriptBridge._messageHandler = messageHandler; var receivedMessages = receiveMessageQueue; receiveMessageQueue = null; for (var i = 0; i < receivedMessages.length; i++) { _dispatchMessageFromNative(receivedMessages[i]); } WebViewJavascriptBridge.inited = true; } // 发送 function send(data, responseCallback) { _doSend('send', data, responseCallback); } // 注册线程 往数组里面添加值 function registerHandler(handlerName, handler) { messageHandlers[handlerName] = handler; } function removeHandler(handlerName, handler) { delete messageHandlers[handlerName]; } // Register a persistent callback that won't be deleted after first use function registerPersistentCallback(callbackId, callback) { persistentCallbacks[callbackId] = callback; responseCallbacks[callbackId] = callback; } // Remove a persistent callback function removePersistentCallback(callbackId) { delete persistentCallbacks[callbackId]; delete responseCallbacks[callbackId]; } // 调用线程 function callHandler(handlerName, data, responseCallback, persistent) { // 如果方法不需要参数,只有回调函数,简化JS中的调用 console.log(11111111111); if (arguments.length == 2 && typeof data == 'function') { responseCallback = data; data = null; } _doSend(handlerName, data, responseCallback, persistent); } // Call handler with persistent callback that can be reused function callHandlerPersistent(handlerName, data, responseCallback) { if (arguments.length == 2 && typeof data == 'function') { responseCallback = data; data = null; } _doSend(handlerName, data, responseCallback, true); } //sendMessage add message, 触发native处理 sendMessage function _doSend(handlerName, message, responseCallback, persistent) { var callbackId; console.log(1); if(typeof responseCallback === 'string'){ console.log(2); callbackId = responseCallback; } else if (responseCallback) { console.log(3); callbackId = 'cb_' + (uniqueId++) + '_' + new Date().getTime(); responseCallbacks[callbackId] = responseCallback; if (persistent) { persistentCallbacks[callbackId] = responseCallback; } message.callbackId = callbackId; }else{ console.log(4); callbackId = ''; } try { console.log(5); var fn = eval('WebViewJavascriptBridge.' + handlerName); } catch(e) { console.log(e); } if (typeof fn === 'function'){ console.log(6); var responseData = fn.call(WebViewJavascriptBridge, JSON.stringify(message), callbackId); if(responseData){ console.log(7); responseCallback = responseCallbacks[callbackId]; if (!responseCallback) { console.log(8); return; } responseCallback(responseData); // Only delete if it's not a persistent callback if (!persistentCallbacks[callbackId]) { console.log(9); delete responseCallbacks[callbackId]; } } } sendMessageQueue.push(message); messagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://' + QUEUE_HAS_MESSAGE; } // 提供给native调用,该函数作用:获取sendMessageQueue返回给native,由于android不能直接获取返回的内容,所以使用url shouldOverrideUrlLoading 的方式返回内容 function _fetchQueue() { // 空数组直接返回 if (sendMessageQueue.length === 0) { return; } // _fetchQueue 的调用间隔过短,延迟调用 if (new Date().getTime() - lastCallTime < FETCH_QUEUE_INTERVAL) { if (!stoId) { stoId = setTimeout(_fetchQueue, FETCH_QUEUE_INTERVAL); } return; } lastCallTime = new Date().getTime(); stoId = null; var messageQueueString = JSON.stringify(sendMessageQueue); sendMessageQueue = []; //android can't read directly the return data, so we can reload iframe src to communicate with java bizMessagingIframe.src = CUSTOM_PROTOCOL_SCHEME + '://return/_fetchQueue/' + encodeURIComponent(messageQueueString); } //提供给native使用, function _dispatchMessageFromNative(messageJSON) { setTimeout(function() { var message = JSON.parse(messageJSON); var responseCallback; //java call finished, now need to call js callback function if (message.responseId) { responseCallback = responseCallbacks[message.responseId]; if (!responseCallback) { return; } responseCallback(message.responseData); // Only delete if it's not a persistent callback if (!persistentCallbacks[message.responseId]) { delete responseCallbacks[message.responseId]; } } else { //直接发送 if (message.callbackId) { var callbackResponseId = message.callbackId; responseCallback = function(responseData) { _doSend('response', responseData, callbackResponseId); }; } var handler = WebViewJavascriptBridge._messageHandler; if (message.handlerName) { handler = messageHandlers[message.handlerName]; } //查找指定handler try { handler(message.data, responseCallback); } catch (exception) { if (typeof console != 'undefined') { console.log("WebViewJavascriptBridge: WARNING: javascript handler threw.", message, exception); } } } }); } //提供给native调用,receiveMessageQueue 在会在页面加载完后赋值为null,所以 function _handleMessageFromNative(messageJSON) { if (receiveMessageQueue) { receiveMessageQueue.push(messageJSON); } _dispatchMessageFromNative(messageJSON); } WebViewJavascriptBridge.init = init; WebViewJavascriptBridge.doSend = send; WebViewJavascriptBridge.registerHandler = registerHandler; WebViewJavascriptBridge.removeHandler = removeHandler; WebViewJavascriptBridge.callHandler = callHandler; WebViewJavascriptBridge.callHandlerPersistent = callHandlerPersistent; WebViewJavascriptBridge.registerPersistentCallback = registerPersistentCallback; WebViewJavascriptBridge.removePersistentCallback = removePersistentCallback; WebViewJavascriptBridge._handleMessageFromNative = _handleMessageFromNative; WebViewJavascriptBridge._fetchQueue = _fetchQueue; var readyEvent = document.createEvent('Events'); var jobs = window.WVJBCallbacks || []; readyEvent.initEvent('WebViewJavascriptBridgeReady'); readyEvent.bridge = WebViewJavascriptBridge; window.WVJBCallbacks = []; jobs.forEach(function (job) { job(WebViewJavascriptBridge) }); document.dispatchEvent(readyEvent); })(); public static void webViewLoadLocalJs(WebView view, String path){ String jsContent = assetFile2Str(view.getContext(), path); view.loadUrl("javascript:" + jsContent); } public static String assetFile2Str(Context c, String urlStr){ InputStream in = null; try{ in = c.getAssets().open(urlStr); BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(in)); String line = null; StringBuilder sb = new StringBuilder(); do { line = bufferedReader.readLine(); if (line != null && !line.matches("^\\s*\\/\\/.*")) { sb.append(line); } } while (line != null); bufferedReader.close(); in.close(); return sb.toString(); } catch (Exception e) { e.printStackTrace(); } finally { if(in != null) { try { in.close(); } catch (IOException e) { } } } return null; }注入.js文件报 Uncaught ReferenceError: WebViewJavascriptBridge is not defined -- From line 1 of 发现错误: Uncaught ReferenceError: WebViewJavascriptBridge is not defined https://pv-mall.wuyang-honda.com/h5/#/ 1 6553 ReferenceError: WebViewJavascriptBridge is not defined -- From line 79 of https://pv-mall.wuyang-honda.com/h5/assets/index-EaCfmwlF.js Uncaught ReferenceError: WebViewJavascriptBridge is not defined -- From line 1 of https://pv-mall.wuyang-honda.com/h5/#/ [system] Location: https://pv-mall.wuyang-honda.com/h5/#/ -- From line 10 of https://pv-mall.wuyang-honda.com/h5/assets/vconsole.min-QVp8m_0m.js [system] Client: Android 12 -- From line 10 of https://pv-mall.wuyang-honda.com/h5/assets/vconsole.min-QVp8m_0m.js [system] UA: Mozilla/5.0 (Linux; Android 12; ALN-AL00 Build/HUAWEIALN-AL00; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/114.0.5735.196 Mobile Safari/537.36 Honda_ANDROIDAPP -- From line 10 of https://pv-mall.wuyang-honda.com/h5/assets/vconsole.min-QVp8m_0m.js
07-04
运行:import java.io.*; import java.net.*; import java.nio.file.Files; import java.util.StringTokenizer; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class WebServer { private static final int PORT = 6789; private static final String WEB_ROOT = System.getProperty("user.dir") + File.separator + "webroot"; private static final ExecutorService threadPool = Executors.newFixedThreadPool(10); public static void main(String[] args) { try (ServerSocket serverSocket = new ServerSocket(PORT)) { System.out.println("服务器已启动,监听端口:" + PORT); System.out.println("Web根目录:" + WEB_ROOT); while (true) { Socket clientSocket = serverSocket.accept(); threadPool.execute(new RequestHandler(clientSocket)); } } catch (IOException e) { System.err.println("服务器错误: " + e.getMessage()); } finally { threadPool.shutdown(); } } static class RequestHandler implements Runnable { private static final String CRLF = "\r\n"; private final Socket clientSocket; RequestHandler(Socket socket) { this.clientSocket = socket; } @Override public void run() { try (BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); DataOutputStream out = new DataOutputStream(clientSocket.getOutputStream())) { // 读取请求行 String requestLine = in.readLine(); System.out.println("收到请求:" + requestLine); if (requestLine == null) return; // 解析请求路径 StringTokenizer tokens = new StringTokenizer(requestLine); tokens.nextToken(); // 跳过方法 String fileName = tokens.nextToken(); fileName = WEB_ROOT + (fileName.equals("/") ? "/index.html" : fileName); // 安全路径检查 if (fileName.contains("..")) { sendErrorResponse(out, 403, "Forbidden"); return; } // 处理文件请求 File file = new File(fileName); if (file.exists() && !file.isDirectory()) { sendFileResponse(out, file); } else { sendErrorResponse(out, 404, "Not Found"); } } catch (IOException e) { System.err.println("处理请求错误: " + e.getMessage()); } finally { try { clientSocket.close(); } catch (IOException e) { System.err.println("关闭连接错误: " + e.getMessage()); } } } private void sendFileResponse(DataOutputStream out, File file) throws IOException { String contentType = Files.probeContentType(file.toPath()); byte[] fileData = Files.readAllBytes(file.toPath()); String responseHeader = "HTTP/1.1 200 OK" + CRLF + "Content-Type: " + contentType + CRLF + "Content-Length: " + fileData.length + CRLF + CRLF; out.writeBytes(responseHeader); out.write(fileData); out.flush(); } private void sendErrorResponse(DataOutputStream out, int statusCode, String statusText) throws IOException { String errorHtml = "<html><body><h1>" + statusCode + " " + statusText + "</h1></body></html>"; String response = "HTTP/1.1 " + statusCode + " " + statusText + CRLF + "Content-Type: text/html" + CRLF + "Content-Length: " + errorHtml.length() + CRLF + CRLF + errorHtml; out.writeBytes(response); out.flush(); } } } 如何显示出我的html文件
05-28
public class PureDiskLogAdapter implements LogAdapter { // 在cache中第一个文件默认名 private static final String CACHE_LOG_FIRST_NAME = "logs_0000.dat"; // 最大的log缓存量,支持设置 1 * 500 * 1024 ~ 9999 * 500 * 1024 // 超过则在init时删除 private static final long LOG_MAX_SIZE = 800 * 500 * 1024L; // 400Mb private static volatile PureDiskLogAdapter instance; private final FormatStrategy formatStrategy; private final String logFileFolderPath; private final byte[] encryptKey; private AESEncrypter encrypter; public static PureDiskLogAdapter getInstance(Context context, String appName, long limitCacheSize) { if (instance == null) { synchronized (PureDiskLogAdapter.class) { if (instance == null) { instance = new PureDiskLogAdapter(context, appName, limitCacheSize); } } } return instance; } public static boolean isLoggable() { return instance != null; } /** * 将日志解密到外置存储器中, * 每次将日志解密到外置存储器时都会先暂停日志的更新,并在解密保存成功后删除cache中的文件 * <p> * 注意:调用时需要检查文件读写权限 * * @param decrypt true为解密, false为不解密 * @return 保存的路径 */ public static String decryptLogger(Context context, boolean decrypt, String company, String appName) { if (instance == null) { return null; } File loggerFile = new File(instance.logFileFolderPath); if (!loggerFile.exists()) { return null; } File distDir = context.getExternalFilesDir(Environment.DIRECTORY_DOCUMENTS); if (distDir == null) { return null; } // 暂停所有log TLog.clean(); String dstPath = distDir.getAbsolutePath(); String dstFolder = dstPath + File.separatorChar + company.toUpperCase() + File.separatorChar + appName.toUpperCase(); // 用于导入日志的文件对象 File exportLogFile = instance.generateExternalFile(dstFolder, appName); if (loggerFile.isFile()) { instance.readEncryptFile(loggerFile, exportLogFile, decrypt); } else { List<File> fileList = FileUtils.getSortedFileList(loggerFile.listFiles()); for (File logFile : fileList) { instance.readEncryptFile(logFile, exportLogFile, decrypt); } } // 清除所有disk log FileUtils.delete(loggerFile); // 重新开启log reset(context, appName); return dstFolder; } private static void reset(Context context, String appName) { instance = null; TLog.reset(context, appName); } private PureDiskLogAdapter(Context context, String appName, long limitCacheSize) { logFileFolderPath = context.getApplicationContext().getCacheDir().getAbsolutePath() + File.separatorChar + "logger"; encryptKey = buildLogEncryptKey(); // 初始化时选检查logger目录下的日志是否超过预定值,超过则清除所有日志 cleanLoggerCache(logFileFolderPath); renameAllDiskLog(logFileFolderPath); formatStrategy = PureFormatStrategy.newBuilder(context).tag(appName.toUpperCase()).encryptKey(encryptKey).limitCache(limitCacheSize).build(); } @Override public boolean isLoggable(int priority, @Nullable String tag) { return true; } @Override public void log(int priority, @Nullable String tag, @NonNull String message) { formatStrategy.log(priority, tag, message); } private byte[] buildLogEncryptKey() { try { // 加密key return CodecUtils.getMD5Byte(PlainEncryptKeyDelegate.getEncryptKey().getBytes("utf-8")); } catch (Exception e) { return null; } } /** * 将cache中的log文件列表进行重命名,防止开启循环删除cache后,重新打开disk log文件没有按照最初的顺序排列 */ private void renameAllDiskLog(String folderPath) { File file = new File(folderPath); if (!file.exists() || file.isFile()) { return; } // 路径存在 List<File> fileList = FileUtils.getSortedFileList(file.listFiles()); if (fileList.isEmpty()) { return; } // 路径下文件列表不为空 if (CACHE_LOG_FIRST_NAME.equals(fileList.get(0).getName())) { return; } // 第一个文件名不匹配,需要把文件列表全部重名名 int newFileCount = 0; for (File tempFile : fileList) { File newFile = new File(folderPath, String.format(Locale.US, "logs_%04d.dat", newFileCount)); tempFile.renameTo(newFile); newFileCount++; } } /** * 清除保存在cache中的log,条件为超过 LOG_MAX_SIZE */ private void cleanLoggerCache(String folderPath) { File file = new File(folderPath); if (file.exists()) { long deleteRange = (long) (LOG_MAX_SIZE * 1.2f); if (FileUtils.getAllFileLength(file) > deleteRange) { FileUtils.delete(file); } } } /** * 生成文件名,按照 2017-08-16_16-38-22-678.dat 的格式保存 * * @param folderName 目录路径 */ private File generateExternalFile(String folderName, String appName) { File folder = new File(folderName); if (!folder.exists()) { folder.mkdirs(); } SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd_HH-mm-ss-SSS", Locale.US); return new File(folder, String.format("%s_%s.txt", appName, dateFormat.format(new Date(System.currentTimeMillis())))); } /** * 解码文件 * * @param file 源文件 * @param externalFile 目标文件 */ private void readEncryptFile(File file, File externalFile, boolean decrypt) { try (FileReader fileReader = new FileReader(file); FileWriter fileWriter = new FileWriter(externalFile, true); BufferedReader bufferedReader = new BufferedReader(fileReader)) { StringBuilder stringBuilder = new StringBuilder(); String readBuffer; while ((readBuffer = bufferedReader.readLine()) != null) { // 每次读一行,并确定是否解密 if (decrypt) { // 解密日志 stringBuilder.append(decryptLoggerString(readBuffer)); } else { stringBuilder.append(readBuffer); } } // 写入文件 fileWriter.write(stringBuilder.toString()); fileWriter.flush(); } catch (Exception e) { /* fail silently */ } } // AES解密,用getMD5Byte生成Key private String decryptLoggerString(String encrypted) throws Exception { byte[] enc = CodecUtils.hexStringToByteArray(encrypted); if (encrypter == null) { encrypter = new AESEncrypter(encryptKey, null, "AES"); } byte[] result = encrypter.decrypt(enc); // 将原来写入时替换换行符的替换回来 return new String(result).replaceAll(PureFormatStrategy.NEW_LINE_REPLACEMENT, PureFormatStrategy.NEW_LINE); } } Attempt to invoke direct method 'void vj0.a.j(java.io.File, java.io.File, boolean)' on a null object reference 空指针分析
最新发布
11-15
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值