You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.13 08:36:12