AndroidRuntime:致命异常,java.lang.OutOfMemoryError 发送多张图片时
AndroidRuntime: FATAL EXCEPTION, java.lang.OutOfMemoryError when sending multiple images
我正在尝试使用 okHttp3
和 retrofit2
将大量图像发送到 API
。我试过从一台设备发送图像(一次 5 张图像)并且效果很好。然后我尝试同时从5台设备发送5张图片,效果很好,但是当我尝试从5台设备发送30张图片时,出现以下错误:
2020-01-17 14:57:07.416 32244-32264/my.example.com.puri W/zygote: Throwing OutOfMemoryError "Failed to allocate a 102554120 byte allocation with 8388608 free bytes and 97MB until OOM, max allowed footprint 174912000, growth limit 268435456"
2020-01-17 14:57:07.422 32244-32264/my.example.com.puri E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
Process: my.example.com.puri, PID: 32244
java.lang.OutOfMemoryError: Failed to allocate a 102554120 byte allocation with 8388608 free bytes and 97MB until OOM, max allowed footprint 174912000, growth limit 268435456
at java.lang.StringFactory.newStringFromBytes(StringFactory.java:178)
at java.lang.StringFactory.newStringFromBytes(StringFactory.java:209)
at okio.Buffer.readString(Buffer.java:620)
at okio.Buffer.readString(Buffer.java:603)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:198)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
此 error
不会出现在第一个发送请求的设备上,而是出现在第三个或最后一个设备上。我先点击send
按钮的设备总是成功的。
我也对 Manifest
进行了这些更改。
<application
android:hardwareAccelerated="false"
android:largeHeap="true"
>
这是我提出请求的方式。
@Multipart
@POST("installationdocument")
Call<Void> createNewDocument(@Header(AUTH_HEADER) String authorization, @Part ArrayList<MultipartBody.Part> images, @Part("document") RequestBody json);
并且:
@Override
public void callRestApi(PuriRestApi restApi, RestApiJobService.ApiRequestor apiRequestor) {
MediaType JSON = MediaType.parse("application/json");
MediaType JPG = MediaType.parse("image/jpg");
ArrayList<MultipartBody.Part> imageParts = new ArrayList<>();
for (String imagePath : imagePaths) {
File image = new File(imagePath);
RequestBody imageBody = RequestBody.create(JPG, image);
imageParts.add(MultipartBody.Part.createFormData("images", image.getName(), imageBody));
}
RequestBody jsonPart = RequestBody.create(JSON, documentString);
apiRequestor.request(restApi.createNewDocument(authentication, imageParts, jsonPart), response -> {
if (response.isSuccessful()) {
if (imagePaths != null && imagePaths.length > 0) {
for (String imagePath : imagePaths) {
File image = new File(imagePath);
image.delete();
}
}
return true;
} else {
return false;
}
});
}
也许创建一个大变量是引发此错误的原因。因为变量 ArrayList<MultipartBody.Part> imageParts
将包含大约 150mb 的图像数据和 30 张图像。但除此之外,我不知道是什么导致了这个错误。
非常感谢任何形式的帮助。
在清单文件中添加以下实体 android:hardwareAccelerated="false" , android:largeHeap="true" 它适用于某些环境。
<application
android:allowBackup="true"
android:hardwareAccelerated="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
您可以在将图片发送到服务器之前使用压缩方法
public Bitmap compressInputImage(Uri inputImageData){
try {
bitmapInputImage=MediaStore.Images.Media.getBitmap(context.getContentResolver(), inputImageData);
if (bitmapInputImage.getWidth() > 2048 && bitmapInputImage.getHeight() > 2048)
{
dpBitmap = Bitmap.createScaledBitmap(bitmapInputImage, 1024, 1280, true); }
else if (bitmapInputImage.getWidth() > 2048 && bitmapInputImage.getHeight() < 2048)
{
dpBitmap = Bitmap.createScaledBitmap(bitmapInputImage, 1920, 1200, true); }
else if (bitmapInputImage.getWidth() < 2048 && bitmapInputImage.getHeight() > 2048)
{
dpBitmap = Bitmap.createScaledBitmap(bitmapInputImage, 1024, 1280, true); }
else if (bitmapInputImage.getWidth() < 2048 && bitmapInputImage.getHeight() < 2048)
{
dpBitmap = Bitmap.createScaledBitmap(bitmapInputImage, bitmapInputImage.getWidth(), bitmapInputImage.getHeight(), true); }
} catch (Exception e)
{
dpBitmap = bitmapInputImage;
}
return dpBitmap;
}
它可以最大限度地减少OutOfMemory Exception
的机会。除此之外你可以处理异常
通过添加 try - catch .
除非我误读了错误堆栈,否则在我看来该错误来自 HttpLoggingInterceptor,它试图分配一个 102Mb 的缓冲区来记录整个图像数据。
如果是这样,我假设您已将日志记录级别设置为 BODY,在这种情况下,将其设置为任何其他级别都可以解决问题。
我正在尝试使用 okHttp3
和 retrofit2
将大量图像发送到 API
。我试过从一台设备发送图像(一次 5 张图像)并且效果很好。然后我尝试同时从5台设备发送5张图片,效果很好,但是当我尝试从5台设备发送30张图片时,出现以下错误:
2020-01-17 14:57:07.416 32244-32264/my.example.com.puri W/zygote: Throwing OutOfMemoryError "Failed to allocate a 102554120 byte allocation with 8388608 free bytes and 97MB until OOM, max allowed footprint 174912000, growth limit 268435456"
2020-01-17 14:57:07.422 32244-32264/my.example.com.puri E/AndroidRuntime: FATAL EXCEPTION: OkHttp Dispatcher
Process: my.example.com.puri, PID: 32244
java.lang.OutOfMemoryError: Failed to allocate a 102554120 byte allocation with 8388608 free bytes and 97MB until OOM, max allowed footprint 174912000, growth limit 268435456
at java.lang.StringFactory.newStringFromBytes(StringFactory.java:178)
at java.lang.StringFactory.newStringFromBytes(StringFactory.java:209)
at okio.Buffer.readString(Buffer.java:620)
at okio.Buffer.readString(Buffer.java:603)
at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:198)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:92)
at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:67)
at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:185)
at okhttp3.RealCall$AsyncCall.execute(RealCall.java:135)
at okhttp3.internal.NamedRunnable.run(NamedRunnable.java:32)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
at java.lang.Thread.run(Thread.java:764)
此 error
不会出现在第一个发送请求的设备上,而是出现在第三个或最后一个设备上。我先点击send
按钮的设备总是成功的。
我也对 Manifest
进行了这些更改。
<application
android:hardwareAccelerated="false"
android:largeHeap="true"
>
这是我提出请求的方式。
@Multipart
@POST("installationdocument")
Call<Void> createNewDocument(@Header(AUTH_HEADER) String authorization, @Part ArrayList<MultipartBody.Part> images, @Part("document") RequestBody json);
并且:
@Override
public void callRestApi(PuriRestApi restApi, RestApiJobService.ApiRequestor apiRequestor) {
MediaType JSON = MediaType.parse("application/json");
MediaType JPG = MediaType.parse("image/jpg");
ArrayList<MultipartBody.Part> imageParts = new ArrayList<>();
for (String imagePath : imagePaths) {
File image = new File(imagePath);
RequestBody imageBody = RequestBody.create(JPG, image);
imageParts.add(MultipartBody.Part.createFormData("images", image.getName(), imageBody));
}
RequestBody jsonPart = RequestBody.create(JSON, documentString);
apiRequestor.request(restApi.createNewDocument(authentication, imageParts, jsonPart), response -> {
if (response.isSuccessful()) {
if (imagePaths != null && imagePaths.length > 0) {
for (String imagePath : imagePaths) {
File image = new File(imagePath);
image.delete();
}
}
return true;
} else {
return false;
}
});
}
也许创建一个大变量是引发此错误的原因。因为变量 ArrayList<MultipartBody.Part> imageParts
将包含大约 150mb 的图像数据和 30 张图像。但除此之外,我不知道是什么导致了这个错误。
非常感谢任何形式的帮助。
在清单文件中添加以下实体 android:hardwareAccelerated="false" , android:largeHeap="true" 它适用于某些环境。
<application
android:allowBackup="true"
android:hardwareAccelerated="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="@style/AppTheme">
您可以在将图片发送到服务器之前使用压缩方法
public Bitmap compressInputImage(Uri inputImageData){
try {
bitmapInputImage=MediaStore.Images.Media.getBitmap(context.getContentResolver(), inputImageData);
if (bitmapInputImage.getWidth() > 2048 && bitmapInputImage.getHeight() > 2048)
{
dpBitmap = Bitmap.createScaledBitmap(bitmapInputImage, 1024, 1280, true); }
else if (bitmapInputImage.getWidth() > 2048 && bitmapInputImage.getHeight() < 2048)
{
dpBitmap = Bitmap.createScaledBitmap(bitmapInputImage, 1920, 1200, true); }
else if (bitmapInputImage.getWidth() < 2048 && bitmapInputImage.getHeight() > 2048)
{
dpBitmap = Bitmap.createScaledBitmap(bitmapInputImage, 1024, 1280, true); }
else if (bitmapInputImage.getWidth() < 2048 && bitmapInputImage.getHeight() < 2048)
{
dpBitmap = Bitmap.createScaledBitmap(bitmapInputImage, bitmapInputImage.getWidth(), bitmapInputImage.getHeight(), true); }
} catch (Exception e)
{
dpBitmap = bitmapInputImage;
}
return dpBitmap;
}
它可以最大限度地减少OutOfMemory Exception
的机会。除此之外你可以处理异常
通过添加 try - catch .
除非我误读了错误堆栈,否则在我看来该错误来自 HttpLoggingInterceptor,它试图分配一个 102Mb 的缓冲区来记录整个图像数据。
如果是这样,我假设您已将日志记录级别设置为 BODY,在这种情况下,将其设置为任何其他级别都可以解决问题。