Android 10设备MC2200 PDA FTP下载APK失败问题求助
问题
通过FTP从文件服务器下载APK时,Android 13设备可正常下载,但在搭载Android 10(API 29)的MC2200安卓PDA上无法完成下载。FTP连接正常,能获取文件列表,但调用ftpDownloadFile()时进入catch块,问题出在FileOutputStream fos = new FileOutputStream(desFilePath);这一行。是否是Android版本差异导致?
原因分析
这确实是Android版本差异带来的问题,核心在于Android 10(API 29)对外部存储权限和文件访问机制的限制变更:
- 公共存储写入限制:Android 10开始默认限制对公共外部存储(如Downloads目录)的直接写入,即使申请
WRITE_EXTERNAL_STORAGE权限,也需要启用旧存储兼容模式才能生效。 - 废弃API兼容性:你使用的
Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS)在API 29已被标记为废弃,部分定制化PDA设备对该API的支持存在兼容性问题,导致无法正常创建文件输出流。 - 权限申请不完整:仅申请
READ_EXTERNAL_STORAGE权限不足以完成文件写入,需要同时申请WRITE_EXTERNAL_STORAGE。 - 权限申请时机错误:在子线程中调用
requestPermissions()会导致权限流程异常,该方法必须在主线程执行,否则后续写入会因权限不足失败。
解决方案
1. 补全权限并启用旧存储兼容
在AndroidManifest.xml中添加权限和兼容配置:
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application ... android:requestLegacyExternalStorage="true"> ... </application>
同时在代码中申请完整权限:
if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { String[] PERMISSIONS = { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS, PERMISSION_REQUEST); }
2. 替换为推荐的存储路径
放弃使用废弃API,改用应用专属外部存储路径(无需额外权限,Android 10及以上生效):
// 替换原有的downloadFolderPath String downloadFolderPath = getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS).getPath();
该路径属于应用专属目录,卸载应用时会自动清理,且读写无需申请外部存储权限。
3. 修正权限申请时机
将权限申请移到主线程,等待权限回调后再启动下载:
// 主线程处理权限 int PERMISSION_REQUEST = 1; if (ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE}, PERMISSION_REQUEST); } else { startFtpDownload(); } // 权限回调方法 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == PERMISSION_REQUEST) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { startFtpDownload(); } else { Toast.makeText(this, "需要存储权限才能下载文件", Toast.LENGTH_SHORT).show(); } } } // 封装下载逻辑 private void startFtpDownload() { new Thread(new Runnable() { @Override public void run() { // 原有的FTP连接、下载逻辑 } }).start(); }
4. 增强异常定位
在ftpDownloadFile()中打印详细异常信息,方便排查:
public boolean ftpDownloadFile(String serverPath, String desFilePath) { try { FileOutputStream fos = new FileOutputStream(desFilePath); // 原有下载逻辑 } catch (Exception e) { e.printStackTrace(); Log.e("FTP_DOWNLOAD", "下载失败: " + e.getMessage()); return false; } }
原问题代码
new Thread(new Runnable() { @Override public void run() { String downloadFolderPath = Environment.getExternalStoragePublicDirectory(DIRECTORY_DOWNLOADS).getPath(); File pdaFolder = new File(downloadFolderPath); File files[] = pdaFolder.listFiles(); for (int i = 0; i < files.length; i++) { System.out.println("file: " + files[i]); } boolean status = false; status = connectFTP.ftpConnect("192.168.5.2", "pda", "login1323", 21); if (status == true) { Log.d(TAG, "Connection Success"); } else { Log.d(TAG, "Connection failed"); } int PERMISSION_REQUEST = 1; if (ActivityCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { String[] PERMISSIONS = {Manifest.permission.READ_EXTERNAL_STORAGE}; ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS, PERMISSION_REQUEST); } cVersion = BuildConfig.VERSION_NAME; System.out.println(BuildConfig.VERSION_NAME); System.out.println(Build.VERSION.SDK_INT); for (String fileName : connectFTP.ftpGetFileList("/PDA")) { if (fileName.endsWith(".apk")) { serverApkVersion = fileName.replaceAll("^(File)\\s*", ""); String localFilePath = downloadFolderPath + "/" + serverApkVersion; if (!cVersion.equals(serverApkVersion.replace(".apk", ""))) { System.out.println(connectFTP.ftpGetFileList("/PDA").length); for(String a: connectFTP.ftpGetFileList("/PDA")) { System.out.println(a); } System.out.println(serverApkVersion); if (connectFTP.ftpDownloadFile("/PDA/" + serverApkVersion, localFilePath)) { } else { System.out.println("failed"); } } } } runOnUiThread(new Runnable() { @Override public void run() { customProgressDialog.dismiss(); Toast.makeText(MainActivity.this, downloadFolderPath, Toast.LENGTH_SHORT).show(); } }); } }).start();
内容的提问来源于stack exchange,提问作者ilovechicken
相关产品推荐
相关产品推荐

