如何修复尝试通过改造抛出OutOfMemoryError时抛出的OutOfMemoryError [英] How to fix OutOfMemoryError thrown while trying to throw OutOfMemoryError with retrofit

查看:539
本文介绍了如何修复尝试通过改造抛出OutOfMemoryError时抛出的OutOfMemoryError的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用翻新程序在我的应用程序中下载一些媒体文件,例如视频,mp3,jpg,pdf等.当我想下载55MB且大小为mp4的大文件时,threre是一个问题.当我要下载此文件时,出现如下错误:

I am using retrofit to download some media files like video,mp3, jpg, pdf,... in my application.threre is a problem when I want to download a large file with 55MB with the format of mp4. when I want to download this file I get an error like this:

OutOfMemoryError threw while trying to throw OutOfMemoryError; no stack trace available

这是我的代码:

  private void downloadFile() {

    ArrayList<FileModel> filesInDB = G.bootFileFromFileDB();

    for (final FileModel fm : filesInDB) {

      APIService downloadService = ServiceGenerator.createServiceFile(APIService.class, "username", "password");

      //Id of apk file that you want to download
      Call<ResponseBody> call = downloadService.downloadFileWithDynamicUrlSync("file/download/" + String.valueOf(fm.getFileId()));
      call.enqueue(new Callback<ResponseBody>() {

        @Override
        public void onResponse(Call<ResponseBody> call, Response<ResponseBody> response) {
          if (response.isSuccess()) {

            Log.d("LOGOO", "server contacted and has file");

            boolean writtenToDisk = writeResponseBodyToDisk(response.body(), fm.getFileName(), fm.getFileExtension());

            response = null;


            Log.d("LOGOO", "file download was a success? " + writtenToDisk);


          } else {
            Log.d("LOGOO", "server contact failed");
          }
        }

        @Override
        public void onFailure(Call<ResponseBody> call, Throwable t) {
          Log.i("LOGO", "Error is : " + t.getMessage());

          Toast.makeText(ActivityInternet.this, R.string.internet_error, Toast.LENGTH_LONG).show();
          Intent intent = new Intent(ActivityInternet.this, ActivityStartup.class);
          startActivity(intent);
        }
      });
    }

这是我正在使用的'writeResponseBodyToDisk'方法:

and this is 'writeResponseBodyToDisk' method that I'm using:

  private boolean writeResponseBodyToDisk(ResponseBody body, String fileName, String fileExtension) {

    try {

      // Location to save downloaded file and filename
      File futureStudioIconFile = new File(G.DIR_APP + fileName + fileExtension);
      InputStream inputStream = null;
      OutputStream outputStream = null;
      try {
        byte[] fileReader = new byte[4096];
        long fileSize = body.contentLength();
        long fileSizeDownloaded = 0;
        inputStream = body.byteStream();
        outputStream = new FileOutputStream(futureStudioIconFile);
        while (true) {
          int read = inputStream.read(fileReader);
          if (read == -1) {
            break;
          }
          outputStream.write(fileReader, 0, read);
          fileSizeDownloaded += read;
          Log.d("LOGO", "file download: " + fileSizeDownloaded + " of " + fileSize);
        }
        outputStream.flush();
        return true;
      } catch (IOException e) {
        return false;
      } finally {
        if (inputStream != null) {
          inputStream.close();
        }
        if (outputStream != null) {
          outputStream.close();
        }
      }
    } catch (IOException e) {
      return false;
    }


  }

最后这是我的createServiceFile方法:

finally this is my createServiceFile method:

public static <S> S createServiceFile(Class<S> serviceClass, String username, String password) {
        if (username != null && password != null) {

            String credentials = username + ":" + password;
            final String basic =
              "Basic " + Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
            httpClient.addInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request original = chain.request();
                    Request.Builder requestBuilder = original.newBuilder()
                      .header("Authorization", basic)
                      .header("Accept", "text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,audio/mp4,image/jpeg,*/*;q=0.8")
                      .method(original.method(), original.body());
                    Request request = requestBuilder.build();
                    return chain.proceed(request);
                }
            });
        }
        OkHttpClient client = httpClient.build();
        Retrofit retrofit = builder.client(client).build();
        return retrofit.create(serviceClass);
    }

如果您能帮助我,我非常感谢:)

I really appreciate if you can help me :)

推荐答案

在翻新中处理大文件下载时,有四点注意事项:

There are 4 things to notice when handling large file downloads in retrofit:

  1. 确保将AndroidManifest.xml中的android:largeHeap="true"用作<application>的属性.
  2. 请确保您使用的是Retrofit中的@Streaming批注,以便流式传输解决方案而不是整体读取它,这会占用大量内存.
  3. 使用AsyncTask处理响应,如此链接.就您而言,这意味着从AsyncTask内部调用writeResponseBodyToDisk.
  4. 最后要避免的是使用Level.BODY okhttp3日志记录拦截器.将其与流式响应一起使用仍可将整个响应主体保留在内存中,从而抵消了翻新提供的@Streaming支持的优势.
  1. Make sure you are using android:largeHeap="true" in your AndroidManifest.xml, as an attribute for the <application>.
  2. Make sure you are using @Streaming annotation from Retrofit, in order to stream the solution and not read it as a whole, which encumbers memory.
  3. Handle the response using an AsyncTask as described in this link. In your case, this means calling writeResponseBodyToDisk from within the AsyncTask.
  4. The last thing to avoid is using a Level.BODY okhttp3 logging interceptor. Using it alongside a streamed response still keeps the entire response body in memory, negating the advantage of the @Streaming support provided by retrofit.

这篇关于如何修复尝试通过改造抛出OutOfMemoryError时抛出的OutOfMemoryError的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆