【问题标题】:Retrofit API to retrieve a png image改造 API 以检索 png 图像
【发布时间】:2014-10-17 05:21:15
【问题描述】:

您好,我是 Android 的 Retrofit 框架的新手。我可以使用它从 REST 服务获得 JSON 响应,但我不知道如何使用改造下载 png。我正在尝试从此网址下载 png: http://wwwns.akamai.com/media_resources/globe_emea.png。 应该在 Callback 中指定什么响应对象来实现这一点。

【问题讨论】:

  • 为什么需要 Retrofit 来下载 PNG。您可以简单地在 Android 中使用普通的 I/O 类。

标签: android retrofit


【解决方案1】:

当然我们通常使用 Picasso 来加载图片,但有时我们确实需要使用 Retrofit 来加载特殊图片(例如获取验证码图片),您需要添加一些请求头,从响应头中获取一些值(当然你也可以使用Picasso + OkHttp,但是在一个项目中你已经使用Retrofit来处理大部分网络请求),所以这里介绍如何通过Retrofit 2.0实现。 0(我已经在我的项目中实现了)。

关键是你需要使用okhttp3.ResponseBody来接收响应,否则Retrofit会将响应数据解析为JSON,而不是二进制数据。

代码:

public interface Api {
    // don't need add 'Content-Type' header, it's useless
    // @Headers({"Content-Type: image/png"})
    @GET
    Call<ResponseBody> fetchCaptcha(@Url String url);
}

Call<ResponseBody> call = api.fetchCaptcha(url);
call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
            if (response.isSuccessful()) {
                if (response.body() != null) {
                    // display the image data in a ImageView or save it
                    Bitmap bmp = BitmapFactory.decodeStream(response.body().byteStream());
                    imageView.setImageBitmap(bmp);
                } else {
                    // TODO
                }
            } else {
                // TODO
            }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
            // TODO
        }
    });

【讨论】:

  • 呼叫来自 okhhtp3 还是 retrofit2?
  • @user2362956 ,来自retrofit2的调用,但是retrofit2在底层依赖或者使用了okhttp3。
  • 我找了这个答案2小时,我欠你一杯啤酒! ;)
  • 我得到这个异常 D/skia: --- SkAndroidCodec::NewFromStream 返回 null
【解决方案2】:

如前所述,您不应该使用 Retrofit 来实际下载图像本身。如果您的目标是简单地下载内容而不显示它,那么您可以简单地使用像 OkHttp 这样的 Http 客户端,这是 Square 的另一个库。

这里有几行代码可以让你下载这张图片。然后您可以从 InputStream 中读取数据。

    OkHttpClient client = new OkHttpClient();

    Request request = new Request.Builder()
            .url("http://wwwns.akamai.com/media_resources/globe_emea.png")
            .build();

    client.newCall(request).enqueue(new Callback() {
        @Override
        public void onFailure(Request request, IOException e) {
            System.out.println("request failed: " + e.getMessage());
        }

        @Override
        public void onResponse(Response response) throws IOException {
            response.body().byteStream(); // Read the data from the stream
        }
    });

尽管 Retrofit 不是回答您问题的工作人员,但您的接口定义的签名应该是这样的。但同样不要这样做。

public interface Api {
    @GET("/media_resources/{imageName}")
    void getImage(@Path("imageName") String imageName, Callback<Response> callback);
}

【讨论】:

  • 这是一种使用 byteStream 的更复杂的方法,不如 Picasso 灵活(缓存、调整大小、裁剪等)。
  • 该帖子提到,仅当您只是下载内容但不显示内容时,这才是一种替代方法。 Pradeem 的用例没有具体说明他要显示内容。
  • 顺便说一句为什么我不应该这样做?
【解决方案3】:

Retrofit 是一个 REST 库,您只能使用 Retrofit 来获取图像 URL,但要显示图像,您应该使用 Picasso:http://square.github.io/picasso/

【讨论】:

  • 非常感谢Picasso的推荐,这个库非常好用。
  • 但是如何在从 picasso 调用 url 时添加标题?
【解决方案4】:

例如声明它返回调用:

@GET("/api/{api}/bla/image.png")
Call<ResponseBody> retrieveImageData();

然后自己转换成Bitmap:

ResponseBody body = retrofitService.retrieveImageData().execute().body();
        byte[] bytes = body.bytes();
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

【讨论】:

  • 终于......有人回答了这个问题。虽然使用 bytestream 进行改造来显示图像不起作用但是当我使用 response.body().bytes() 它工作得非常好......谢谢@Alecio。我欠你很多……
【解决方案5】:

详情

  • Android 工作室 3.1.4
  • Kotlin 1.2.60
  • 改造 2.4.0
  • 已签入 minSdkVersion 19

解决方案

对象 RetrofitImage

object RetrofitImage {

    private fun provideRetrofit(): Retrofit {
        return Retrofit.Builder().baseUrl("https://google.com").build()
    }

    private interface API {
        @GET
        fun getImageData(@Url url: String): Call<ResponseBody>
    }

    private val api : API by lazy  { provideRetrofit().create(API::class.java) }

    fun getBitmapFrom(url: String, onComplete: (Bitmap?) -> Unit) {

        api.getImageData(url).enqueue(object : retrofit2.Callback<ResponseBody> {

            override fun onFailure(call: Call<ResponseBody>?, t: Throwable?) {
                onComplete(null)
            }

            override fun onResponse(call: Call<ResponseBody>?, response: Response<ResponseBody>?) {
                if (response == null || !response.isSuccessful || response.body() == null || response.errorBody() != null) {
                    onComplete(null)
                    return
                }
                val bytes = response.body()!!.bytes()
                onComplete(BitmapFactory.decodeByteArray(bytes, 0, bytes.size))
            }
        })
    }
}

用法1

RetrofitImage.getBitmapFrom(ANY_URL_STRING) {
   // "it" - your bitmap
   print("$it")
}

用法2

ImageView 的扩展

fun ImageView.setBitmapFrom(url: String) {
    val imageView = this
    RetrofitImage.getBitmapFrom(url) {
        val bitmap: Bitmap?
        bitmap = if (it != null) it else {
            // create empty bitmap
            val w = 1
            val h = 1
            val conf = Bitmap.Config.ARGB_8888
            Bitmap.createBitmap(w, h, conf)
        }

        Looper.getMainLooper().run {
            imageView.setImageBitmap(bitmap!!)
        }
    }
}

扩展程序的使用

imageView?.setBitmapFrom(ANY_URL_STRING)

【讨论】:

  • @VarunRaj 你是如何检测到滞后的?您是否在回收视图中使用此代码并且滚动它时它是滞后的?
  • 是的,我在回收站视图中使用它。我的回收器视图位于嵌套滚动视图中,一旦图像进入屏幕,它就会开始滞后
  • 嗯...这可能是个问题。您必须为此操作(加载和渲染图像)实现单独的任务队列。所以,如果没有对主队列的控制,你就无法享受它。
【解决方案6】:

您也可以使用 Retrofit 来执行 @GET 并返回 Response。然后在代码中,您可以执行isr = new BufferedInputStream(response.getBody().in()) 来获取图像的输入流并将其写入位图,例如通过执行BitmapFactory.decodeStream(isr)

【讨论】:

  • 嗨!我知道这是一个很老的答案。我只是想补充一点,在改造 2.0 中你必须返回 okhttp3.ResponseBody
  • 为什么要使用新的 BufferedInputStream 而不是只调用BitmapFactory.decodeStream(response.getBody().in())
  • java.io.IOException: Closed am getting this error with "SkAndroidCodec::NewFromStream returned null"
【解决方案7】:

希望下面的代码对你有帮助:

MainActivity.java中包含以下函数:

void getRetrofitImage() {
    Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(url)
            .addConverterFactory(GsonConverterFactory.create())
            .build();

    RetrofitImageAPI service = retrofit.create(RetrofitImageAPI.class);

    Call<ResponseBody> call = service.getImageDetails();

    call.enqueue(new Callback<ResponseBody>() {
        @Override
        public void onResponse(Response<ResponseBody> response, Retrofit retrofit) {

            try {

                Log.d("onResponse", "Response came from server");

                boolean FileDownloaded = DownloadImage(response.body());

                Log.d("onResponse", "Image is downloaded and saved ? " + FileDownloaded);

            } catch (Exception e) {
                Log.d("onResponse", "There is an error");
                e.printStackTrace();
            }

        }

        @Override
        public void onFailure(Throwable t) {
            Log.d("onFailure", t.toString());
        }
    });
}

以下是图片的文件处理代码:

private boolean DownloadImage(ResponseBody body) {

        try {
            Log.d("DownloadImage", "Reading and writing file");
            InputStream in = null;
            FileOutputStream out = null;

            try {
                in = body.byteStream();
                out = new FileOutputStream(getExternalFilesDir(null) + File.separator + "AndroidTutorialPoint.jpg");
                int c;

                while ((c = in.read()) != -1) {
                    out.write(c);
                }
            }
            catch (IOException e) {
                Log.d("DownloadImage",e.toString());
                return false;
            }
            finally {
                if (in != null) {
                    in.close();
                }
                if (out != null) {
                    out.close();
                }
            }

            int width, height;
            ImageView image = (ImageView) findViewById(R.id.imageViewId);
            Bitmap bMap = BitmapFactory.decodeFile(getExternalFilesDir(null) + File.separator + "AndroidTutorialPoint.jpg");
            width = 2*bMap.getWidth();
            height = 6*bMap.getHeight();
            Bitmap bMap2 = Bitmap.createScaledBitmap(bMap, width, height, false);
            image.setImageBitmap(bMap2);

            return true;

        } catch (IOException e) {
            Log.d("DownloadImage",e.toString());
            return false;
        }
    }

这是使用 Android Retrofit 2.0 完成的。我希望它对你有所帮助。

来源:Image Download using Retrofit 2.0

【讨论】:

    【解决方案8】:

    Retrofit 正在将您的字节数组编码为基数 64。因此,解码您的字符串,您就可以开始了。通过这种方式,您可以检索图像列表。

    public static Bitmap getBitmapByEncodedString(String base64String) {
        String imageDataBytes = base64String.substring(base64String.indexOf(",")+1);
        InputStream stream = new ByteArrayInputStream(Base64.decode(imageDataBytes.getBytes(), Base64.DEFAULT));
        return BitmapFactory.decodeStream(stream);
    }
    

    【讨论】:

      猜你喜欢
      • 2023-03-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-02
      • 1970-01-01
      • 1970-01-01
      • 2015-12-17
      相关资源
      最近更新 更多