2017-12-28 6 views
0

안드로이드에 CSV 파일을 다운로드하려고합니다. 다운로드가 끝나면 Jackson을 매핑하여 sqlite3 db에 저장합니다. 내가 직면 한 문제는 파일을 다운로드하는 전화를 걸 때 OutOfMemoryError이 나옵니다. 파일 크기는 12MB입니다. 브라우저에서 같은 것을 시도 할 때 파일을 다운로드 할 수 있으므로 엔드 포인트에 문제가 있다고 생각하지 않습니다.파일을 다운로드하는 동안 메모리가 부족합니다.

다운로드하려면 AsyncTask를 사용하고 있습니다.

class DownloadCSVTask extends AsyncTask<Void, String, Void> { 

    private final Call<ResponseBody> csvDump; 
    private final Context context; 

    public DownloadCSVTask(Call<ResponseBody> csvDump, Context context) { 
     this.csvDump = csvDump; 
     this.context = context; 
    } 

    @Override 
    protected Void doInBackground(Void... voids) { 
     try { 
      Response<ResponseBody> response = csvDump.execute(); 
      File fileFromResponse = createFileFromResponse(response); 
      mapFileContentsToModelAndPersist(fileFromResponse); 
     } catch (IOException e) { 
      Log.e(getClass().getSimpleName(), "CSV pull failed"); 
     } 
     return null; 
    } 

    private File createFileFromResponse(Response<ResponseBody> response) throws IOException { 
     File path = context.getFilesDir(); 
     File csvFile = File.createTempFile(TEMP_PREFIX, CSV_EXTENSION, path); 
     FileOutputStream fileOutputStream = context.openFileOutput(csvFile.getName(), Context.MODE_PRIVATE); 
     fileOutputStream.write(response.body().bytes()); 
     Log.i(getClass().getCanonicalName(), "Writing file completed" + csvFile.getAbsolutePath()); 
     fileOutputStream.close(); 
     return csvFile; 
    } 

    private void mapFileContentsToModelAndPersist(File fileFromResponse) { 
     try { 
      MappingIterator<City> cityIter = new CsvMapper().readerWithTypedSchemaFor(City.class).readValues(fileFromResponse); 
      List<City> cities = cityIter.readAll(); 
      Stream.of(cities).forEach(City::persist); 
     } catch (IOException e) { 
      Log.e(getClass().getSimpleName(), "Failed to map contents of file to City Class"); 
     } 
    } 
} 

나는 점점 오전 오류 로그 :

java.lang.RuntimeException: An error occured while executing doInBackground() 
         E  at android.os.AsyncTask$3.done(AsyncTask.java:300) 
         E  at java.util.concurrent.FutureTask.finishCompletion(FutureTask.java:355) 
         E  at java.util.concurrent.FutureTask.setException(FutureTask.java:222) 
         E  at java.util.concurrent.FutureTask.run(FutureTask.java:242) 
         E  at android.os.AsyncTask$SerialExecutor$1.run(AsyncTask.java:231) 
         E  at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1112) 
         E  at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:587) 
         E  at java.lang.Thread.run(Thread.java:841) 
         E Caused by: java.lang.OutOfMemoryError 
         E  at java.lang.String.<init>(String.java:354) 
         E  at java.lang.String.<init>(String.java:393) 
         E  at okio.Buffer.readString(Buffer.java:616) 
         E  at okio.Buffer.readString(Buffer.java:599) 
         E  at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:263) 
         E  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92) 
         E  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67) 
         E  at com.someclass.somepath.network.util.CustomHeadersInterceptor.intercept(CustomHeadersInterceptor.java:42) 
         E  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92) 
         E  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67) 
         E  at com.someclass.somepath.network.util.NetworkInterceptor.intercept(NetworkInterceptor.java:23) 
         E  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92) 
         E  at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67) 
         E  at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:170) 
         E  at okhttp3.RealCall.execute(RealCall.java:60) 
         E  at retrofit2.OkHttpCall.execute(OkHttpCall.java:174) 
         E  at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall.execute(ExecutorCallAdapterFactory.java:89) 
         E  at com.someclass.somepath.service.DownloadCSVTask.doInBackground(CsvPullHandler.java:60) 
         E  at com.someclass.somepath.service.DownloadCSVTask.doInBackground(CsvPullHandler.java:47) 
         E  at android.os.AsyncTask$2.call(AsyncTask.java:288) 
         E  at java.util.concurrent.FutureTask.run(FutureTask.java:237) 

내가 여기 뭔가 잘못하고 있습니까? 그것을 할 다른 방법이 있습니까?

답변

1

Android의 Java 코드 메모리 양은 16Mb (장치에 따라 다름)로 제한됩니다. 따라서이 부분을 네이티브 NDK 코드로 옮겨야합니다. 여기서 malloc()은 장치가 가지고있는만큼의 메모리를 사용하거나 파일을 디스크에 직접 저장 한 다음 전체 파일을 Java 버퍼에로드하지 않고 처리합니다. 전체 파일을 response 배열로 복사하는 대신 URLConnection.getInputStream() 또는 URLConnection.read()을 루프에서 16KB 바이트 배열로 사용하십시오. 또는 BufferedReader.readLine()을 사용하여 HTTP 스트림에서 직접 한 줄씩 읽을 수 있으며 데이터베이스를 한 줄씩 채울 수 있습니다.

+0

감사합니다. 연결에서 스트리밍 및 파일 저장이 효과적입니다. – leoOrion

관련 문제