Q:

RxAndroid getting Out of memory exception while uploading multiple images

While uploading multiple images using RxJava and Retrofit I am getting Out of memory issue.  Kindly check below 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

below is my code for same
 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();
                    }
                });
    }

Service Factory class source code is as below
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;
    }
}

Few post suggested Schedulers.io() creating multiple threads that's why Out memory issues occurs.  I tried with Schedulers.computation() but still the same issues.  Thanks in advance. 

A:

Get rid of provideHttpLoggingInterceptor() and its uses, or replace it with your own interceptor that logs less information.  Right now, you are attempting to log the entire HTTP request via Timber.  This will not work for larger requests.