使用RxJava和Retrofit上传多个图像时,出现内存不足的问题。请检查下面的Logcat。
Throwing OutOfMemoryError "Failed to allocate a 68559200 byte allocation with 25165824 free bytes and 56MB until OOM, max allowed footprint 167497024, growth limit 201326592"
07-09 16:10:22.807 8536-8589/com.galisto W/System.err: io.reactivex.exceptions.UndeliverableException: java.lang.OutOfMemoryError: Failed to allocate a 68559200 byte allocation with 25165824 free bytes and 56MB until OOM, max allowed footprint 167497024, growth limit 201326592
07-09 16:10:22.808 8536-8589/com.galisto W/System.err: at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:349)
07-09 16:10:22.808 8536-8589/com.galisto W/System.err: at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:69)
07-09 16:10:22.809 8536-8589/com.galisto W/System.err: at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
07-09 16:10:22.809 8536-8589/com.galisto W/System.err: at java.util.concurrent.FutureTask.run(FutureTask.java:266)
07-09 16:10:22.809 8536-8589/com.galisto W/System.err: at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:301)
07-09 16:10:22.811 8536-8589/com.galisto W/System.err: at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
07-09 16:10:22.811 8536-8589/com.galisto W/System.err: at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
07-09 16:10:22.812 8536-8589/com.galisto W/System.err: at java.lang.Thread.run(Thread.java:764)
07-09 16:10:22.813 8536-8589/com.galisto W/System.err: Caused by: java.lang.OutOfMemoryError: Failed to allocate a 68559200 byte allocation with 25165824 free bytes and 56MB until OOM, max allowed footprint 167497024, growth limit 201326592
07-09 16:10:22.813 8536-8589/com.galisto W/System.err: at java.lang.StringBuilder.toString(StringBuilder.java:410)
07-09 16:10:22.814 8536-8589/com.galisto W/System.err: at java.util.Formatter.toString(Formatter.java:2358)
07-09 16:10:22.814 8536-8589/com.galisto W/System.err: at java.lang.String.format(String.java:2770)
07-09 16:10:22.814 8536-8589/com.galisto W/System.err: at timber.log.Timber$Tree.formatMessage(Timber.java:561)
07-09 16:10:22.814 8536-8589/com.galisto W/System.err: at timber.log.Timber$Tree.prepareLog(Timber.java:547)
07-09 16:10:22.815 8536-8589/com.galisto W/System.err: at timber.log.Timber$Tree.d(Timber.java:427)
07-09 16:10:22.815 8536-8589/com.galisto W/System.err: at timber.log.Timber$1.d(Timber.java:248)
07-09 16:10:22.815 8536-8589/com.galisto W/System.err: at timber.log.Timber.d(Timber.java:38)
07-09 16:10:22.815 8536-8589/com.galisto W/System.err: at com.galisto.utils.network.ServiceFactory.lambda$provideHttpLoggingInterceptor$0$ServiceFactory(ServiceFactory.java:45)
07-09 16:10:22.816 8536-8589/com.galisto W/System.err: at com.galisto.utils.network.ServiceFactory$$Lambda$0.log(Unknown Source:19)
07-09 16:10:22.816 8536-8589/com.galisto W/System.err: at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:199)
07-09 16:10:22.816 8536-8589/com.galisto W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
07-09 16:10:22.816 8536-8589/com.galisto W/System.err: at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
07-09 16:10:22.817 8536-8589/com.galisto W/System.err: at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
07-09 16:10:22.817 8536-8589/com.galisto W/System.err: at okhttp3.RealCall.execute(RealCall.java:77)
07-09 16:10:22.817 8536-8589/com.galisto W/System.err: at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
07-09 16:10:22.818 8536-8589/com.galisto W/System.err: at retrofit2.adapter.rxjava2.CallExecuteObservable.subscribeActual(CallExecuteObservable.java:41)
07-09 16:10:22.818 8536-8589/com.galisto W/System.err: at io.reactivex.Observable.subscribe(Observable.java:11040)
07-09 16:10:22.818 8536-8589/com.galisto W/System.err: at retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
07-09 16:10:22.818 8536-8589/com.galisto W/System.err: at io.reactivex.Observable.subscribe(Observable.java:11040)
07-09 16:10:22.819 8536-8589/com.galisto W/System.err: at io.reactivex.internal.operators.observable.ObservableOnErrorNext.subscribeActual(ObservableOnErrorNext.java:38)
07-09 16:10:22.819 8536-8589/com.galisto W/System.err: at io.reactivex.Observable.subscribe(Observable.java:11040)
07-09 16:10:22.819 8536-8589/com.galisto W/System.err: at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
07-09 16:10:22.819 8536-8589/com.galisto W/System.err: at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:463)
07-09 16:10:22.820 8536-8589/com.galisto W/System.err: at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
07-09 16:10:22.820 8536-8589/com.galisto W/System.err: ... 6 more
下面是我的相同代码
private void uploadOrUpdatePost(String nodeId, String nodeName, String title, String detailText, ArrayList<FileItem> files, Post post) {
List<MultipartBody.Part> mFiles = new ArrayList<>();
for (int i = 0; i < files.size(); i++) {
MultipartBody.Part mPart = prepareFilePart("file" + i, files.get(i).getFilePath());
if (mPart != null) {
mFiles.add(mPart);
}
}
RequestBody tokenBody = prepareStringPart(getPrefsHelper().getSessionToken());
RequestBody bokId = prepareStringPart(getPrefsHelper().getBokId());
RequestBody rbNodeId = prepareStringPart(nodeId);
String postID;
if (post == null) {
Timber.d("## uploadOrUpdatePost upload post :" + title);
postID = "";
} else {
Timber.d("## uploadOrUpdatePost update post :" + post.getPostTitle());
if (post.getPostId().contains("-")) {
postID = post.getPostId();
} else {
postID = "";
}
}
RequestBody postId = prepareStringPart(getEmptyStringIfNull(postID));
RequestBody postTitle = prepareStringPart(getEmptyStringIfNull(title));
RequestBody postDetail = prepareStringPart(getEmptyStringIfNull(detailText));
service.uploadPost(tokenBody, bokId, rbNodeId, postId, postTitle, postDetail, mFiles)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.doOnNext(new Consumer<ResAddComment>() {
@Override
public void accept(ResAddComment resAddComment) throws Exception {
if (resAddComment.isSuccess() && post != null) {
mAppData.getPostDao().delete(post.getPostId());
}
}
})
.subscribeOn(Schedulers.computation())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new SimpleObserver<ResAddComment>() {
@Override
public void onSubscribe(Disposable d) {
compositeDisposable.add(d);
if (isViewAttached())
getView().showLoader();
}
@Override
public void onNext(ResAddComment response) {
if (response.isSuccess()) {
if (isViewAttached()) {
getView().hideLoader();
getView().onCommentUploaded(R.string.post_uploaded_successfully);
}
} else {
String error = response.getError().getMessage();
if (error != null && isViewAttached()) {
getView().hideLoader();
if (response.isSessionExprired()) {
getView().doLogin();
} else {
// if post is null means upload case so store it rather than update
if (post == null) {
storePostAsDraft(nodeId, nodeName, title, detailText, files, true, true);
} else {
updatePostAsDraft(post, title, detailText, files);
}
}
}
}
}
@Override
public void onError(Throwable e) {
if (isViewAttached()) {
getView().hideLoader();
// if post is null means upload case so store it rather than update
if (post == null) {
storePostAsDraft(nodeId, nodeName, title, detailText, files, true, true);
} else {
updatePostAsDraft(post, title, detailText, files);
}
}
}
@Override
public void onComplete() {
super.onComplete();
}
});
}
服务工厂类源代码如下
public class ServiceFactory {
/**
* Creates a retrofit service from an arbitrary class (clazz)
*
* @param clazz Java interface of the retrofit service
* @param endPoint REST endpoint url
* @return retrofit service with defined endpoint
*/
public static <T> T createRetrofitService(final Class<T> clazz, final String endPoint) {
final Retrofit restAdapter = new Retrofit.Builder()
.baseUrl(endPoint)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxErrorHandlingCallAdapterFactory.create())
.client(provideOkHttpClient())
.build();
return restAdapter.create(clazz);
}
private static OkHttpClient provideOkHttpClient() {
OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
builder.addInterceptor(provideHttpLoggingInterceptor());
}
builder.connectTimeout(AppConstants.CONNECTION_TIMEOUT_TIME_IN_SECONDS, TimeUnit.SECONDS);
return builder.build();
}
private static HttpLoggingInterceptor provideHttpLoggingInterceptor() {
HttpLoggingInterceptor httpLoggingInterceptor =
new HttpLoggingInterceptor(message ->
Timber.d("## RETROFIT HTTP LOG - %s", message));
httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
return httpLoggingInterceptor;
}
}
很少有人建议使用Schedulers.io()创建多个线程,这就是发生内存不足问题的原因。我尝试使用Schedulers.computation(),但仍然遇到相同的问题。预先感谢。
答案 0 :(得分:3)
摆脱provideHttpLoggingInterceptor()
及其使用,或将其替换为您自己的日志记录较少的拦截器。现在,您正在尝试通过Timber记录整个HTTP请求。这不适用于较大的请求。
答案 1 :(得分:0)
我遇到了完全相同的问题,将httpLoggingInterceptor
的日志级别从BODY
更改为HEADERS
对我来说是有效的。
public HttpLoggingInterceptor httpLoggingInterceptor() {
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
@Override
public void log(String message) {
Timber.d(message);
}
});
interceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);
return interceptor;
}