I don't know whether to use retrofit or Picasso to get this photo
It is better to use Picasso otherwise you have to write a lot of codes to load images efficiently if you download them using Retrofit.
You would be happy to know that you can use both Retrofit and Picasso depending on your choice to load images from that API.
Before going ahead with example, I want to clear one thing up that you had a misconception that you needed to send the above-mentioned JSON data as header but after playing around with the API I figured out that it takes the JSON as request body.
Examples
RemoteApi.kt
interface RemoteApi {
// Retrofit
@Streaming // Important
@POST("/api/DownloadFileForAndroid")
@Throws(Exception::class)
suspend fun getImage(@Body body: ImageRequest): ResponseBody?
companion object {
// Retrofit
fun create(): RemoteApi {
val retrofit = Retrofit.Builder()
.addConverterFactory(GsonConverterFactory.create())
.baseUrl("http://shop.atiafkar.ir")
.build()
return retrofit.create(RemoteApi::class.java)
}
// Picasso
fun getPicassoImageRequest(
builder : Picasso.Builder,
body: String,
url: String = "http://shop.atiafkar.ir/api/DownloadFileForAndroid"
) = builder.downloader(OkHttp3Downloader(getPicassoCallFactory(body)))
.build()
.load(url)
// Picasso
private fun getPicassoCallFactory(jsonBody : String): Call.Factory {
return Call.Factory { request ->
OkHttpClient().run {
RequestBody.create(MediaType.parse("application/json"), jsonBody).let {
newCall(request.newBuilder()
.post(it)
.addHeader("Content-Type", "application/json")
.build()
)
}
}
}
}
}
}
ImageRepository.kt
class ImageRepository(private val api: RemoteApi = RemoteApi.create()) {
companion object {
fun get() = ImageRepository()
}
// Retrofit
suspend fun downloadImage(body : ImageRequest = ImageRequest.default): Bitmap? {
return api.getImage(body)?.run {
withContext(Dispatchers.IO) {
bytes().let {
// to load bitmap efficiently follow the guideline provided by -
// https://developer.android.com/topic/performance/graphics/load-bitmap
// otherwise you may experience OutOfMemoryException
BitmapFactory.decodeByteArray(it, 0, it.size)
}
}
}
}
// Picasso
fun getPicassoImageLoader(
builder : Picasso.Builder,
body: ImageRequest = ImageRequest.default
) = RemoteApi.getPicassoImageRequest(builder, body.toJson())
}
ImageViewModel.kt
class ImageViewModel(private val repository: ImageRepository) : ViewModel() {
private val _progress = MutableLiveData<Boolean>()
val progress = _progress
// Retrofit
val liveImage = liveData {
_progress.value = true
emit(repository.downloadImage())
_progress.value = false
}
// Picasso
fun getPicassoImageLoader(builder: Picasso.Builder) = repository.getPicassoImageLoader(builder)
}
Finally,
ImageActivity.kt
class ImageActivity : AppCompatActivity() {
private lateinit var dataBinding : ActivityImageBinding
private val imageViewModel by lazy { ViewModelProviders.of(this, ImageViewModelFactory()).get(ImageViewModel::class.java) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
dataBinding = DataBindingUtil.setContentView(this, R.layout.activity_image)
dataBinding.lifecycleOwner = this
dataBinding.viewModel = imageViewModel
// Retrofit
imageViewModel.liveImage.observe(this, Observer {
dataBinding.imageView.setImageBitmap(it)
})
// Picasso
imageViewModel.getPicassoImageLoader(Picasso.Builder(this)).into(dataBinding.imageView2)
}
}
ImageRequest.kt
data class ImageRequest(
@field:SerializedName("UserName")
val userName: String? = null,
@field:SerializedName("ContentId")
val contentId: String? = null,
@field:SerializedName("AndroidId")
val androidId: String? = null,
@field:SerializedName("Password")
val password: String? = null,
@field:SerializedName("frame")
val frame: String? = null
) {
companion object {
val default = ImageRequest(
"ApiService",
"704",
"15df3b3a90dc5688",
"BandarAndroid",
"1"
)
}
}
fun ImageRequest.toJson() : String {
return Gson().toJson(this, ImageRequest::class.java)
}