I have below approach to download the pdf file from the url.
My ApiService.kt
interface ApiService {
@GET("8d370189-4ec8-11ec-8469-005056ae4067/Aktionsprospekt-06-12-2021-11-12-2021-06.pdf?_ga=2.119177993.1645728632.1638630355-145806302.1638630355")
suspend fun getData(): Response<ResponseBody>
}
My ApiClient.kt
object ApiClient {
fun getClient(): ApiService {
return Retrofit.Builder().baseUrl(BASE_URL)
.addConverterFactory(GsonConverterFactory.create()).build()
.create(ApiService::class.java)
}
}
In ViewModel I have called coroutine scope as below
class MainActivityViewModel(private val apiService: ApiService, private val context: Context) : ViewModel() {
fun downloadFile() = viewModelScope.launch {
val responseBody = apiService.getData().body()
saveFile(responseBody)
}
private fun saveFile(body: ResponseBody?) : String {
if (body == null) {
return ""
}
var input: InputStream? = null
try{
input = body.byteStream()
val path = context.getExternalFilesDir(null)!!.path
val fos = FileOutputStream(path)
fos.use { output ->
val buffer = ByteArray(4 * 1024) // or other buffer size
var read: Int
while (input.read(buffer).also { read = it } != -1) {
output.write(buffer, 0, read)
}
output.flush()
}
return path
} catch(e: Exception) {
Log.e("saveFile",e.toString())
}
finally {
input?.close()
}
return ""
}
}
In Main Activity I have called above function of downloadFile()
as below:
val apiService: ApiService = ApiClient.getClient()
viewModel = getViewModel(apiService)
downloadBtn.setOnClickListener {
viewModel.downloadFile()
}
I am getting below error in saveFile()
function
2021-12-05 02:50:04.023 7027-7027/com.mms.compareandchoose E/saveFile: java.io.FileNotFoundException: /storage/emulated/0/Android/data/com.mms.compareandchoose/files (Is a directory)
2021-12-05 02:50:04.055 7027-7027/com.mms.compareandchoose E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.mms.compareandchoose, PID: 7027
android.os.NetworkOnMainThreadException
at android.os.StrictMode$AndroidBlockGuardPolicy.onNetwork(StrictMode.java:1303)
at com.android.org.conscrypt.Platform.blockGuardOnNetwork(Platform.java:300)
at com.android.org.conscrypt.OpenSSLSocketImpl$SSLOutputStream.write(OpenSSLSocketImpl.java:839)
at okio.OutputStreamSink.write(JvmOkio.kt:53)
at okio.AsyncTimeout$sink$1.write(AsyncTimeout.kt:103)
at okio.RealBufferedSink.flush(RealBufferedSink.kt:247)
at okhttp3.internal.http2.Http2Writer.rstStream(Http2Writer.kt:135)
at okhttp3.internal.http2.Http2Connection.writeSynReset$okhttp(Http2Connection.kt:354)
at okhttp3.internal.http2.Http2Stream.close(Http2Stream.kt:240)
at okhttp3.internal.http2.Http2Stream.cancelStreamIfNecessary$okhttp(Http2Stream.kt:506)
at okhttp3.internal.http2.Http2Stream$FramingSource.close(Http2Stream.kt:488)
at okio.ForwardingSource.close(ForwardingSource.kt:34)
at okhttp3.internal.connection.Exchange$ResponseBodySource.close(Exchange.kt:309)
at okio.RealBufferedSource.close(RealBufferedSource.kt:498)
at okio.ForwardingSource.close(ForwardingSource.kt:34)
at okio.RealBufferedSource.close(RealBufferedSource.kt:498)
at okio.RealBufferedSource$inputStream$1.close(RealBufferedSource.kt:170)
at com.mms.compareandchoose.models.MainActivityViewModel.saveFile(MainActivityViewModel.kt:46)
at com.mms.compareandchoose.models.MainActivityViewModel$downloadFile$1.invokeSuspend(MainActivityViewModel.kt:20)
at kotlin.coroutines.jvm.internal.BaseContinuationImpl.resumeWith(ContinuationImpl.kt:33)
at kotlinx.coroutines.DispatchedTask.run(DispatchedTask.kt:106)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6776)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1518)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
Can Anybody say why this error is there?? Also advise if this approach is good or not??
You read data from remote stream when you call input.read(buffer)
. But viewModelScope
launches code on the Main thread by default. To fix the exception you should explicitly specify that you perform the download on another (IO) thread.
fun downloadFile() = viewModelScope.launch(Dispatchers.IO) {
val responseBody = apiService.getData().body()
saveFile(responseBody)
}