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

使用Picasso库下载图片并保存到本地存储失败问题求助

Picasso onBitmapFailed Triggered When Trying to Download and Save Image to Local Storage

我帮你排查了代码和配置里的几个关键问题,这些大概率是导致onBitmapFailed触发的原因,咱们一步步来解决:

1. 明文HTTP请求被拦截(Android 9+)

你用的图片URL是http://开头的,而Android 9(API 28)及以上系统默认禁止明文网络请求,这会直接导致Picasso没法下载图片,触发失败回调。

解决办法:在AndroidManifest.xml的<application>标签里加上android:usesCleartextTraffic="true",允许明文请求:

<application 
    android:allowBackup="true" 
    android:icon="@mipmap/ic_launcher" 
    android:label="@string/app_name" 
    android:roundIcon="@mipmap/ic_launcher_round" 
    android:supportsRtl="true" 
    android:theme="@style/AppTheme"
    android:usesCleartextTraffic="true"> <!-- 新增这一行 -->
    <!-- 原有内容不变 -->
</application>

2. 存储路径拼接完全错误

你代码里的路径逻辑有问题:

final File myImageFile = new File(Environment.getExternalStorageDirectory().getPath() + "/" + path, "example");

这里的path是"/data/data/ops.com.imagefetcher/images",拼完之后会得到/storage/emulated/0//data/data/ops.com.imagefetcher/images/example这种不合法的路径——外部存储根目录下根本不存在/data/data这个文件夹,自然没法创建文件,甚至可能导致Picasso的回调异常。

调整路径的两种方案:

  • 要是想存到外部公共目录(比如相册):
// 先创建自定义文件夹,在Pictures目录下
File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyAppImages");
if (!storageDir.exists()) {
    storageDir.mkdirs(); // 递归创建目录
}
// 记得给文件名加后缀,比如.jpg
final File myImageFile = new File(storageDir, "example.jpg");
  • 要是想存到应用私有内部存储(只有你的APP能访问):
ContextWrapper cw = new ContextWrapper(mContext);
// 创建内部存储的私有images目录
File directory = cw.getDir("images", Context.MODE_PRIVATE);
final File myImageFile = new File(directory, "example.jpg");

3. 动态存储权限没申请(Android 6.0+)

如果是存到外部存储,Android 6.0(API 23)及以上只在Manifest里声明WRITE_EXTERNAL_STORAGE是没用的,必须动态申请权限。

在调用下载前先检查权限(比如在你的MyService或者MainActivity里):

// 检查权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
        != PackageManager.PERMISSION_GRANTED) {
    // 申请权限
    ActivityCompat.requestPermissions(this,
            new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
            1001); // 自定义请求码
} else {
    // 权限已允许,执行下载
    new DownloadImage().getImage(this);
}

// 处理权限申请结果
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    if (requestCode == 1001) {
        if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
            new DownloadImage().getImage(this);
        } else {
            Toast.makeText(this, "需要存储权限才能保存图片哦", Toast.LENGTH_SHORT).show();
        }
    }
}

4. 确保Target的强引用有效

你已经把Target作为成员变量,但可以优化一下,避免重复创建导致引用丢失:

public class DownloadImage {
    Context mContext;
    // 保持强引用的Target成员
    private Target mSaveTarget;

    public void getImage(Context context) {
        mContext = context;
        // 初始化Target,确保只创建一次
        if (mSaveTarget == null) {
            mSaveTarget = buildSaveTarget();
        }
        Picasso.with(context)
                .load("http://blog.concretesolutions.com.br/wp-content/uploads/2015/04/Android1.png")
                .into(mSaveTarget);
        Log.e("Download Image: ", "inside getImage()");
    }

    private Target buildSaveTarget() {
        return new Target() {
            @Override
            public void onBitmapLoaded(final Bitmap bitmap, Picasso.LoadedFrom from) {
                new Thread(new Runnable() {
                    @Override
                    public void run() {
                        Log.e("Download Image: ", "inside run");
                        // 这里用上面调整后的正确路径
                        File storageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "MyAppImages");
                        if (!storageDir.exists()) {
                            storageDir.mkdirs();
                        }
                        final File myImageFile = new File(storageDir, "example.jpg");
                        FileOutputStream ostream = null;
                        try {
                            Log.e("Download Image: ", "inside try");
                            myImageFile.createNewFile();
                            ostream = new FileOutputStream(myImageFile);
                            bitmap.compress(Bitmap.CompressFormat.JPEG, 80, ostream);
                            ostream.flush();
                            Log.e("Download Image: ", "image saved to >>>" + myImageFile.getAbsolutePath());
                        } catch (Exception e) {
                            e.printStackTrace();
                            Log.e("Download Image: ", "save error: " + e.getMessage());
                        } finally {
                            try {
                                if (ostream != null) {
                                    ostream.close();
                                    Log.e("Download Image: ", "inside finally");
                                }
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }).start();
            }

            @Override
            public void onBitmapFailed(Drawable errorDrawable) {
                Log.e("Download Image: ", "error!");
                // 打开Picasso日志,看具体失败原因
                Picasso.with(mContext).setLoggingEnabled(true);
            }

            @Override
            public void onPrepareLoad(Drawable placeHolderDrawable) {
            }
        };
    }
}

5. 开启Picasso日志排查细节

在onBitmapFailed里开启Picasso的日志,能看到更详细的下载失败原因(比如网络超时、图片不存在等):

@Override
public void onBitmapFailed(Drawable errorDrawable) {
    Log.e("Download Image: ", "error!");
    Picasso.with(mContext).setLoggingEnabled(true);
}

总结

先优先解决明文HTTP限制和路径错误这两个问题,这是最可能导致失败的核心原因。然后再确认权限和Target引用的问题,应该就能正常下载保存图片了。

内容的提问来源于stack exchange,提问作者sak

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 07:51:42