Hi I am new to Retrofit framework for Android. I could get JSON responses from REST services using it but I don\'t know how to download a png using retrofit. I am trying to
object 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))
}
})
}
}
RetrofitImage.getBitmapFrom(ANY_URL_STRING) {
// "it" - your bitmap
print("$it")
}
Extension for 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!!)
}
}
}
Usage of the extension
imageView?.setBitmapFrom(ANY_URL_STRING)
Of course we usually use Picasso to load image, but sometimes we really need use Retrofit to load a special image (like fetch a captcha image), you need add some header for request, get some value from header of response (of course you can also use Picasso + OkHttp, but in a project you have already use Retrofit to handle most of net requests), so here introduce how to implement by Retrofit 2.0.0 (I have already implemented in my project).
The key point is that you need use okhttp3.ResponseBody
to receive response, else Retrofit will parse the response data as JSON, not binary data.
codes:
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
}
});
You could also use Retrofit to perform the @GET
and just return the Response
. Then in code you can do isr = new BufferedInputStream(response.getBody().in())
to get the input stream of the image and write it into a Bitmap, say, by doing BitmapFactory.decodeStream(isr)
.
Retrofit is encoding your byte array to base 64. So decode your string and you are good to go. In this way you can retrieve a list of images.
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);
}
As mentioned you shouldn't use Retrofit to actually download the image itself. If your goal is to simply download the content without displaying it then you could simply use an Http client like OkHttp which is another one of Square's libraries.
Here's a few lines of code which would have you download this image. You could then read the data from the 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
}
});
Even though Retrofit isn't the man for the job to answer your question, the signature of your Interface definition would like this. But again don't do this.
public interface Api {
@GET("/media_resources/{imageName}")
void getImage(@Path("imageName") String imageName, Callback<Response> callback);
}
Declare it returning Call for instance:
@GET("/api/{api}/bla/image.png")
Call<ResponseBody> retrieveImageData();
then convert it to Bitmap yourself:
ResponseBody body = retrofitService.retrieveImageData().execute().body();
byte[] bytes = body.bytes();
Bitmap bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);