RxAndroid上载多张图片时出现内存不足异常

时间:2018-07-09 11:43:43

标签: android rx-java retrofit rx-android

使用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(),但仍然遇到相同的问题。预先感谢。

2 个答案:

答案 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;
}