RxJava OutOfMemory运行时崩溃无法创建线程

时间:2018-01-23 05:35:05

标签: android rx-java retrofit2 rx-android

我正在使用Retrofit2 + RxJava + RxAndroid进行网络通话。

我尝试了不同的 Schedulers 同样 io() newThread() 。我知道使用newThread()的后果,但仍然是应用程序关闭OOM的原因。

场景:

  • 过滤列表(过滤后65项)
  • 从网址下载gzip
  • 写入字节并存储在领域

以上网络通话发生65次。

代码:

/*form version download method*/
public void startFormDownload(List<Form> form, ApiClient apiClient) {
    Observable.fromIterable(form)
            .concatMapIterable(Form::getFormVersions) // get form version list from single form object
            .doOnSubscribe(disposable -> AppLogger.i(tag, "Form Versions download is subscribed")) // subscribe process to rx
            .filter(this::checkFormVersionToDownloadOrNot) // only get those versions who needed to downloaded
            .doOnNext(formVersion -> { // get formVersion object

                AppLogger.i(tag, "download this form ---------> " + formVersion.getFormUrl());
                AppLogger.i(tag, formVersion.getFormUrl());
                String constructURL = formVersion.getFormUrl();

                /* download form gzip process starts from here */
                apiClient.getJsonByFormURL(constructURL)
                        .subscribeOn(Schedulers.newThread())
                        .observeOn(AndroidSchedulers.mainThread())
                        .subscribe(new SingleObserver<Response<ResponseBody>>() {
                            @Override
                            public void onSubscribe(Disposable disposable) {
                                AppLogger.i(tag, "gzip download ->" + "subscribed");
                            }

                            @Override
                            public void onSuccess(Response<ResponseBody> responseBodyResponse) {
                                AppLogger.i(tag, "gzip downloaded " + " success");
                                AppLogger.i(tag, "gzip downloaded ->" + responseBodyResponse.toString());

                                if (responseBodyResponse.code() == 401) {
                                    //not authorized
                                    return;
                                }

                                ByteArrayInputStream bais = null;
                                if (responseBodyResponse.body() != null)
                                    try {
                                        bais = new ByteArrayInputStream(responseBodyResponse.body().bytes());

                                        GZIPInputStream gzis = new GZIPInputStream(bais);
                                        InputStreamReader reader = new InputStreamReader(gzis);
                                        BufferedReader in = new BufferedReader(reader);

                                        String readed;
                                        StringBuilder stringBuilder = new StringBuilder();
                                        while ((readed = in.readLine()) != null) {
                                            stringBuilder.append(readed);
                                        }

                                        baseRealm = Realm.getDefaultInstance();
                                        formVersion.setJsonString(stringBuilder.toString());
                                        baseRealm.executeTransaction(realm ->
                                                baseRealm.copyToRealmOrUpdate(formVersion));



                                    } catch (IOException e) {
                                        AppLogger.e(tag, "gzip extract exception->" + e.getLocalizedMessage(), e);
                                    }
                            }

                            @Override
                            public void onError(Throwable throwable) {
                                AppLogger.e(tag, "gzip failed->" + throwable.getMessage(), throwable);
                            }
                        });


            })
            .doOnTerminate(() -> AppLogger.i(tag, "Form Versions terminated"))
            .doOnError(Throwable::printStackTrace)
            .subscribe(
                    formVersion -> AppLogger.i(tag, "Form Versions download completed"),
                    throwable -> AppLogger.e(tag, throwable.getMessage(), throwable)
            );
}

InputStream可能会导致OOM,但我不确定在这里可以做些什么。

PS。 OOM有时只发生,而不是总是发生。

  

崩溃报告

java.lang.OutOfMemoryError: Could not allocate JNI Env
at java.lang.Thread.nativeCreate(Native Method)
at java.lang.Thread.start(Thread.java:730)
at java.util.concurrent.ThreadPoolExecutor.addWorker(ThreadPoolExecutor.java:941)
at java.util.concurrent.ThreadPoolExecutor.ensurePrestart(ThreadPoolExecutor.java:1582)
at java.util.concurrent.ScheduledThreadPoolExecutor.delayedExecute(ScheduledThreadPoolExecutor.java:313)
at java.util.concurrent.ScheduledThreadPoolExecutor.schedule(ScheduledThreadPoolExecutor.java:550)
at java.util.concurrent.ScheduledThreadPoolExecutor.submit(ScheduledThreadPoolExecutor.java:651)
at io.reactivex.internal.schedulers.NewThreadWorker.scheduleActual(NewThreadWorker.java:146)
at io.reactivex.internal.schedulers.NewThreadWorker.schedule(NewThreadWorker.java:51)
at io.reactivex.Scheduler.scheduleDirect(Scheduler.java:135)
at io.reactivex.Scheduler.scheduleDirect(Scheduler.java:111)
at io.reactivex.internal.operators.single.SingleSubscribeOn.subscribeActual(SingleSubscribeOn.java:37)
at io.reactivex.Single.subscribe(Single.java:2779)
at io.reactivex.internal.operators.single.SingleObserveOn.subscribeActual(SingleObserveOn.java:35)
at io.reactivex.Single.subscribe(Single.java:2779)
at com.visualogyx.app.fragments.VisualogyxBaseFragment.lambda$startFormDownload$10$VisualogyxBaseFragment(VisualogyxBaseFragment.java:623)
at com.visualogyx.app.fragments.VisualogyxBaseFragment$$Lambda$9.accept(Unknown Source)
at io.reactivex.internal.operators.observable.ObservableDoOnEach$DoOnEachObserver.onNext(ObservableDoOnEach.java:95)
at io.reactivex.internal.operators.observable.ObservableFilter$FilterObserver.onNext(ObservableFilter.java:52)
at io.reactivex.internal.observers.DisposableLambdaObserver.onNext(DisposableLambdaObserver.java:58)
at io.reactivex.internal.operators.observable.ObservableFlattenIterable$FlattenIterableObserver.onNext(ObservableFlattenIterable.java:111)
at io.reactivex.internal.operators.observable.ObservableFromIterable$FromIterableDisposable.run(ObservableFromIterable.java:98)
at io.reactivex.internal.operators.observable.ObservableFromIterable.subscribeActual(ObservableFromIterable.java:58)
at io.reactivex.Observable.subscribe(Observable.java:10910)
at io.reactivex.internal.operators.observable.ObservableFlattenIterable.subscribeActual(ObservableFlattenIterable.java:44)
at io.reactivex.Observable.subscribe(Observable.java:10910)
at io.reactivex.internal.operators.observable.ObservableDoOnLifecycle.subscribeActual(ObservableDoOnLifecycle.java:33)
at io.reactivex.Observable.subscribe(Observable.java:10910)
at io.reactivex.internal.operators.observable.ObservableFilter.subscribeActual(ObservableFilter.java:30)
at io.reactivex.Observable.subscribe(Observable.java:10910)
at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
at io.reactivex.Observable.subscribe(Observable.java:10910)
at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
at io.reactivex.Observable.subscribe(Observable.java:10910)
at io.reactivex.internal.operators.observable.ObservableDoOnEach.subscribeActual(ObservableDoOnEach.java:42)
at io.reactivex.Observable.subscribe(Observable.java:10910)
at io.reactivex.Observable.subscribe(Observable.java:10896)
at io.reactivex.Observable.subscribe(Observable.java:10825)
at com.visualogyx.app.fragments.VisualogyxBaseFragment.startFormDownload(VisualogyxBaseFragment.java:683)
at com.visualogyx.app.fragments.VisualogyxBaseFragment.updateJobAndDownloadCheckListForm(VisualogyxBaseFragment.java:239)
at com.visualogyx.app.fragments.VisualogyxBaseFragment.lambda$null$1$VisualogyxBaseFragment(VisualogyxBaseFragment.java:221)
at com.visualogyx.app.fragments.VisualogyxBaseFragment$$Lambda$15.test(Unknown Source)
at io.reactivex.internal.operators.observable.ObservableAllSingle$AllObserver.onNext(ObservableAllSingle.java:69)
at io.reactivex.internal.operators.observable.ObservableFromIterable$FromIterableDisposable.run(ObservableFromIterable.java:98)
at io.reactivex.internal.operators.observable.ObservableFromIterable.subscribeActual(ObservableFromIterable.java:58)
at io.reactivex.Observable.subscribe(Observable.java:10910)
at io.reactivex.internal.operators.observable.ObservableAllSingle.subscribeActual(ObservableAllSingle.java:34)
at io.reactivex.Single.subscribe(Single.java:2779)
at io.reactivex.Single.subscribe(Single.java:2765)
at io.reactivex.Single.subscribe(Single.java:2686)
at com.visualogyx.app.fragments.VisualogyxBaseFragment.lambda$storeAndRetrieveListFromDB$2$VisualogyxBaseFragment(VisualogyxBaseFragment.java:226)
at com.visualogyx.app.fragments.VisualogyxBaseFragment$$Lambda$0.execute(Unknown Source)
at io.realm.Realm.executeTransaction(Realm.java:1394)
at com.visualogyx.app.fragments.VisualogyxBaseFragment.storeAndRetrieveListFromDB(VisualogyxBaseFragment.java:200)
at com.visualogyx.app.fragments.HomeFragment$1.onSuccess(HomeFragment.java:59)
at com.visualogyx.app.fragments.HomeFragment$1.onSuccess(HomeFragment.java:54)
at com.visualogyx.app.controller.ApiCallback.onResponse(ApiCallback.java:36)
at com.visualogyx.app.controller.ApiClient$11.onResponse(ApiClient.java:264)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:836)
at android.os.Handler.dispatchMessage(Handler.java:103)
at android.os.Looper.loop(Looper.java:203)
at android.app.ActivityThread.main(ActivityThread.java:6293)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1065)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:926)

3 个答案:

答案 0 :(得分:2)

您可以使用字节流而不是byte []

String body = null;
String charset = "UTF-8"; // You should determine it based on response header.

try (
    InputStream gzippedResponse = responseBodyResponse.body().byteStream();
    InputStream ungzippedResponse = new GZIPInputStream(gzippedResponse);
    Reader reader = new InputStreamReader(ungzippedResponse, charset);
    Writer writer = new StringWriter();
) {
    char[] buffer = new char[10240];
    for (int length = 0; (length = reader.read(buffer)) > 0;) {
        writer.write(buffer, 0, length);
    }
    body = writer.toString();
}

答案 1 :(得分:0)

也许您应该将日志添加到代码的不同部分并查找内存占用内容,哪一部分占用内存。或者您可以使用AndroidStuido内存分配工具来监控内存分配。

答案 2 :(得分:0)

我必须在doFinally()块中移动与事务相关的操作。 到目前为止一切顺利。

Observable.fromIterable(form)
            .concatMapIterable(Form::getFormVersions) // get form version list from single form object
            .doOnSubscribe(disposable -> AppLogger.i(tag, "Form Versions download is subscribed")) // subscribe process to rx
            .filter(formVersion -> formVersion.getJsonString() == null) // only get those versions who is not downloaded yet
            .doOnNext(formVersion -> { // get formVersion object

                AppLogger.i(tag, "download this form ---------> " + formVersion.getFormUrl());
                AppLogger.i(tag, formVersion.getFormUrl());
                String constructURL = formVersion.getFormUrl();

                /* download form gzip process starts from here */
                apiClient.getJsonByFormURL(constructURL)
                        .subscribeOn(Schedulers.io())
                        .subscribe(new SingleObserver<Response<ResponseBody>>() {
                            @Override
                            public void onSubscribe(Disposable disposable) {
                                AppLogger.i(tag, "gzip download ->" + "subscribed");

                            }

                            @Override
                            public void onSuccess(Response<ResponseBody> responseBodyResponse) {
                                AppLogger.i(tag, "gzip downloaded " + " success");
                                AppLogger.i(tag, "gzip downloaded ->" + responseBodyResponse.toString());

                                if (responseBodyResponse.code() == 401) {
                                    //not authorized
                                    return;
                                }
                                // You should determine it based on response header.

                                if (responseBodyResponse.body() != null)
                                    try {
                                        gzipBody = null;

                                        gzis = responseBodyResponse.body().byteStream();
                                        ungzippedResponse = new GZIPInputStream(gzis);
                                        reader = new InputStreamReader(ungzippedResponse, charset);
                                        writer = new java.io.StringWriter();

                                        buffer = new char[10240];
                                        for (int length = 0; (length = reader.read(buffer)) > 0; ) {
                                            writer.write(buffer, 0, length);
                                        }
                                        gzipBody = writer.toString();

                                        formVersion.setJsonString(gzipBody);
                                        AppLogger.i(tag, "set json string ->" + gzipBody);

                                    } catch (IOException e) {
                                        AppLogger.e(tag, "gzip extract exception->" + e.getLocalizedMessage(), e);
                                    }
                            }

                            @Override
                            public void onError(Throwable throwable) {
                                AppLogger.e(tag, "gzip failed->" + throwable.getMessage(), throwable);
                            }
                        });


            })
            .doOnTerminate(() -> AppLogger.i(tag, "Form Versions terminated"))
            .doOnError(Throwable::printStackTrace)
            .doFinally(() -> {
                baseRealm = Realm.getDefaultInstance();
                baseRealm.executeTransaction(realm ->
                        realm.copyToRealmOrUpdate(form));
            })
            .subscribe(
                    formVersion -> AppLogger.i(tag, "Form Versions download completed"),
                    throwable -> AppLogger.e(tag, throwable.getMessage(), throwable)
            );