在android中使用retrofit将大文件作为字节数组下载时出现OutOfMemoryError

时间:2018-04-11 12:43:15

标签: android out-of-memory retrofit2

我正在使用改造来下载android中的大文件。 API将文件作为字节数组返回,我已经添加了@Streaming注释,但我仍然收到错误。

我能做些什么来解决它?

下载期间发生的错误。

我的代码:

//Retrofi api method
@Streaming
@GET(POOL + "DmListarDocsProjZip/{IdProjetct}")
Call<byte[]> dmListarDocsProjZip(@Path("IdProjetct") int idProject);


//THIS CODE IS INSIDE AN IntentService 
//consuming method
private byte[] buscarDocs(int idProjeto) {
    try {
        Call<byte[]> call = mApi.dmListarDocsProjZip(idProject);
        Response<byte[]> response = call.execute();// The OutOfMemoryError is ocurring in here
        return response.body();

    } catch (IOException e) {
        Log.e("buscarDocs", "dmListarDocsProjetoZip");

    } catch (Exception e) {
        verificarSeSessaoExpirou(e);
    }

    return null;
}

Process: br.gov.sp.prodesp.viafacilbombeiros, PID: 27184
    java.lang.OutOfMemoryError: Failed to allocate a 68706640 byte allocation with 16777120 free bytes and 203MB until OOM
        at java.util.ArrayList.add(ArrayList.java:118)
        at com.google.gson.internal.bind.ArrayTypeAdapter.read(ArrayTypeAdapter.java:73)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:37)
        at retrofit2.converter.gson.GsonResponseBodyConverter.convert(GsonResponseBodyConverter.java:25)
        at retrofit2.ServiceMethod.toResponse(ServiceMethod.java:117)
        at retrofit2.OkHttpCall.parseResponse(OkHttpCall.java:211)
        at retrofit2.OkHttpCall.execute(OkHttpCall.java:174)
        at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:89)
        at br.gov.sp.prodesp.viafacilbombeiros.model.DownloadDadosIntentService.buscarDocs(DownloadDadosIntentService.java:253)
        at br.gov.sp.prodesp.viafacilbombeiros.model.DownloadDadosIntentService.access$300(DownloadDadosIntentService.java:61)
        at br.gov.sp.prodesp.viafacilbombeiros.model.DownloadDadosIntentService$1.onNext(DownloadDadosIntentService.java:215)
        at br.gov.sp.prodesp.viafacilbombeiros.model.DownloadDadosIntentService$1.onNext(DownloadDadosIntentService.java:189)
        at rx.observers.SafeSubscriber.onNext(SafeSubscriber.java:139)
        at retrofit2.adapter.rxjava.OperatorMapResponseToBodyOrError$1.onNext(OperatorMapResponseToBodyOrError.java:41)
        at retrofit2.adapter.rxjava.OperatorMapResponseToBodyOrError$1.onNext(OperatorMapResponseToBodyOrError.java:38)
        at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$RequestArbiter.request(RxJavaCallAdapterFactory.java:173)
        at rx.Subscriber.setProducer(Subscriber.java:209)
        at rx.Subscriber.setProducer(Subscriber.java:205)
        at rx.Subscriber.setProducer(Subscriber.java:205)
        at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:152)
        at retrofit2.adapter.rxjava.RxJavaCallAdapterFactory$CallOnSubscribe.call(RxJavaCallAdapterFactory.java:138)
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:50)
        at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
        at rx.Observable.subscribe(Observable.java:8553)
        at rx.Observable.subscribe(Observable.java:8520)
        at br.gov.sp.prodesp.viafacilbombeiros.model.DownloadDadosIntentService.onHandleIntent(DownloadDadosIntentService.java:124)
        at android.app.IntentService$ServiceHandler.handleMessage(IntentService.java:65)
        at android.os.Handler.dispatchMessage(Handler.java:102)
        at android.os.Looper.loop(Looper.java:145)
        at android.os.HandlerThread.run(HandlerThread.java:61)

2 个答案:

答案 0 :(得分:0)

您必须将此大文件保存到磁盘。看起来你正在使用@Streaming下载它(如推荐的那样),但你将它分配给内存,所以@Streaming有点无用。 对于大文件,您需要使用注释,以便在下载过程中避免内存泄漏,但您还需要将文件保存到磁盘,因此您不需要分配那么多内存。

答案 1 :(得分:0)

您的OkHttpClient是否将LoggingInterceptor添加到日志正文? 如果尝试下载大文件,则会将所有正文保存在内存中以备日志。 让OOM很容易。

如果要记录正文,请尝试删除或仅记录基本或标头,然后重试。

如果您的代码是这样的。

val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
        .addInterceptor(logging)
        .build()

尝试一下。

val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.NONE
val client = OkHttpClient.Builder()
        .addInterceptor(logging)
        .build()