求助:Codename One的BrowserComponent安卓端下载链接点击无响应
解决方案
1. 拦截下载请求并手动处理
Codename One的BrowserComponent默认不会自动处理网页中的下载链接,需要通过BrowserNavigationCallback拦截请求,手动实现下载逻辑:
BrowserComponent bc = new BrowserComponent(); bc.setBrowserNavigationCallback(new BrowserNavigationCallback() { @Override public boolean shouldNavigate(String url, String where, boolean isBack) { return true; } @Override public void downloadRequested(String url, String userAgent, String contentDisposition, String mimeType, long contentLength) { // 提取文件名,优先从Content-Disposition头获取 String fileName = extractFileName(contentDisposition, url); // 请求存储权限后启动下载 Permission storagePerm = Permission.STORAGE; if (storagePerm.isGranted()) { startFileDownload(url, fileName); } else { storagePerm.request(() -> { if (storagePerm.isGranted()) { startFileDownload(url, fileName); } }); } } }); // 从请求头或URL提取文件名的辅助方法 private String extractFileName(String contentDisposition, String url) { if (contentDisposition != null) { String[] parts = contentDisposition.split(";"); for (String part : parts) { part = part.trim(); if (part.startsWith("filename=")) { return part.substring(9).replace("\"", ""); } } } // 从URL截取文件名 return url.substring(url.lastIndexOf('/') + 1); } // 执行下载的核心方法 private void startFileDownload(String url, String fileName) { try { String savePath = FileSystemStorage.getInstance().getAppHomePath() + fileName; ConnectionRequest cr = new ConnectionRequest() { @Override protected void readResponse(InputStream input) throws IOException { OutputStream os = FileSystemStorage.getInstance().openOutputStream(savePath); Util.copy(input, os); os.close(); // 下载完成后提示用户 Display.getInstance().callSerially(() -> { Dialog.show("下载完成", "文件已保存至: " + savePath, "确定", null); }); } }; cr.setUrl(url); cr.setPost(false); NetworkManager.getInstance().addToQueue(cr); } catch (IOException e) { e.printStackTrace(); Dialog.show("错误", "下载失败: " + e.getMessage(), "确定", null); } }
2. 添加并请求存储权限
安卓下载文件需要存储权限,需在项目配置中添加权限,并在运行时主动请求:
- 在
codenameone_settings.properties中添加安卓权限:
android.xpermissions=<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/><uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
- 针对Android 10及以上版本,若需保存到公共存储(如下载文件夹),额外添加:
android.xpermissions=<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
注意:MANAGE_EXTERNAL_STORAGE权限需要用户在系统设置中手动授权,无法通过常规弹窗请求。如果仅保存到应用私有目录(FileSystemStorage.getInstance().getAppHomePath()),Android 10+无需额外权限。
3. 适配Android 10+分区存储
如果需要将文件保存到公共下载文件夹,需改用MediaStore API适配分区存储,修改startFileDownload方法如下:
private void startFileDownload(String url, String fileName) { ConnectionRequest cr = new ConnectionRequest() { @Override protected void readResponse(InputStream input) throws IOException { if (Display.getInstance().getPlatformName().equals("Android")) { // Android 10+公共存储适配 ContentValues values = new ContentValues(); values.put(MediaStore.Downloads.DISPLAY_NAME, fileName); values.put(MediaStore.Downloads.MIME_TYPE, "application/octet-stream"); values.put(MediaStore.Downloads.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS); ContentResolver resolver = AndroidNativeUtil.getActivity().getContentResolver(); Uri uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, values); if (uri != null) { OutputStream os = resolver.openOutputStream(uri); Util.copy(input, os); os.close(); Display.getInstance().callSerially(() -> { Dialog.show("下载完成", "文件已保存到系统下载文件夹", "确定", null); }); } else { throw new IOException("无法创建下载文件"); } } else { // 其他平台保存到应用私有目录 String savePath = FileSystemStorage.getInstance().getAppHomePath() + fileName; OutputStream os = FileSystemStorage.getInstance().openOutputStream(savePath); Util.copy(input, os); os.close(); Display.getInstance().callSerially(() -> { Dialog.show("下载完成", "文件已保存至: " + savePath, "确定", null); }); } } }; cr.setUrl(url); cr.setPost(false); NetworkManager.getInstance().addToQueue(cr); }
内容的提问来源于stack exchange,提问作者platinum
相关产品推荐
相关产品推荐

