【问题标题】:Pagination with retrofit and rxjava OutOfMemory使用改造和 rxjava OutOfMemory 进行分页
【发布时间】:2018-08-07 10:35:42
【问题描述】:

我正在尝试手动实现分页。我的做法和sample repository 完全一样。但是有一些我无法理解的错误(在下面的日志中)。我的应用只能加载 2-3 个页面并崩溃。

日志:

06-03 09:55:30.457 22190-22423/example.me E/AndroidRuntime: FATAL EXCEPTION: RxCachedThreadScheduler-1
Process: example.me, PID: 22190
io.reactivex.exceptions.UndeliverableException: java.lang.OutOfMemoryError: Failed to allocate a 168210 byte allocation with 9792 free bytes and 9KB until OOM
    at io.reactivex.plugins.RxJavaPlugins.onError(RxJavaPlugins.java:349)
    at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:64)
    at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:52)
    at java.util.concurrent.FutureTask.run(FutureTask.java:237)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:154)
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269)
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588)
    at java.lang.Thread.run(Thread.java:818)
 Caused by: java.lang.OutOfMemoryError: Failed to allocate a 168210 byte allocation with 9792 free bytes and 9KB until OOM
    at java.lang.StringFactory.newStringFromBytes(StringFactory.java:79)
    at java.lang.StringFactory.newStringFromBytes(StringFactory.java:207)
    at okio.Buffer.readString(Buffer.java:631)
    at okio.Buffer.readString(Buffer.java:614)
    at okhttp3.logging.HttpLoggingInterceptor.intercept(HttpLoggingInterceptor.java:254)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:147)
    at okhttp3.internal.http.RealInterceptorChain.proceed(RealInterceptorChain.java:121)
    at okhttp3.RealCall.getResponseWithInterceptorChain(RealCall.java:200)
    at okhttp3.RealCall.execute(RealCall.java:77)
    at retrofit2.OkHttpCall.execute(OkHttpCall.java:180)
    at com.jakewharton.retrofit2.adapter.rxjava2.CallObservable.subscribeActual(CallObservable.java:41)
    at io.reactivex.Observable.subscribe(Observable.java:10838)
    at com.jakewharton.retrofit2.adapter.rxjava2.BodyObservable.subscribeActual(BodyObservable.java:34)
    at io.reactivex.Observable.subscribe(Observable.java:10838)
    at io.reactivex.internal.operators.observable.ObservableSubscribeOn$SubscribeTask.run(ObservableSubscribeOn.java:96)
    at io.reactivex.Scheduler$DisposeTask.run(Scheduler.java:452)
    at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:61)
    at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:52) 
    at java.util.concurrent.FutureTask.run(FutureTask.java:237) 
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.access$201(ScheduledThreadPoolExecutor.java:154) 
    at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:269) 
    at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1113) 
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:588) 
    at java.lang.Thread.run(Thread.java:818) 

或者有时:

OutOfMemoryError thrown while trying to throw OutOfMemoryError

网络请求代码:

EventApiService eventApiService = RetrofitClient.getApiService();
    compositeDisposable.add(eventApiService.getJson(citySlug, FIRST_PAGE_TO_LOAD)
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(this::handleFirstEventsDataResponse, this::handleFirstEventsDataResponseError));

响应处理代码(首页):

@Override
public void showData(ResponseData responseData) {
    this.events = responseData.getEvents();
    this.responseData = responseData;
    eventAdapter = new EventAdapter(responseData.getEvents(), eventClickListener);
    recyclerView.setAdapter(eventAdapter);
}

响应处理代码(下一页):

@Override
public void showAdditionalData(ResponseData responseData) {
    eventAdapter.removeLoadingItem();
    isLoading = false;
    eventAdapter.addData(responseData.getEvents());
    eventAdapter.addLoadingItem();
}

改造搭建方法:

private static Retrofit getRetrofitInstance() {
    HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
    logging.setLevel(HttpLoggingInterceptor.Level.BODY);
    OkHttpClient.Builder builder = new OkHttpClient.Builder();
    builder.addInterceptor(logging);
    OkHttpClient client = builder.build();
    return new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(client)
            .addCallAdapterFactory(RxJava2CallAdapterFactory.createWithScheduler(Schedulers.io()))
            .addConverterFactory(GsonConverterFactory.create())
            .build();
}

我想做什么:

  • android:largeHeap="true" 在 AndroidManifest 中。它可以让您再加载一页。
  • 将 RxJava 请求替换为普通的 Retrofit2 - 没有强烈反应。
  • 从改造构建器中删除 OkHttpClient - 没有强烈反应。

UPD:

我们发现原因在于图像加载。我添加了fit().centerCrop(),现在它加载了 5-7 页并崩溃了。我在适配器中的图像加载代码 sn-p:

String imageUrl = "";
            try {
                imageUrl = event.getImages().get(0).getImageUrl();
            } catch (NullPointerException e) {
                e.printStackTrace();
            }

            if (eventViewHolder.ivPhoto != null) {
                Picasso.get().load(imageUrl).fit().centerCrop().into(eventViewHolder.ivPhoto);
            }

新的崩溃日志:

06-03 10:32:07.791 17978-17986/example.me E/System: Uncaught exception thrown by finalizer
06-03 10:32:08.438 17978-17986/example.me E/System: java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available
06-03 10:32:08.439 17978-18715/example.me E/AndroidRuntime: FATAL EXCEPTION: Thread-163055
Process: example.me, PID: 17978
java.lang.OutOfMemoryError: OutOfMemoryError thrown while trying to throw OutOfMemoryError; no stack trace available

【问题讨论】:

  • 你在使用任何图像吗?如果是,请使用 picasso 或 glide 加载图片。
  • @minhazur 是的,我用毕加索。
  • 那么,请检查您在哪里订阅 rx 事件并处理它们。
  • @minhazur 我检查了没有图像的分页,它工作得很好。所以,当我打开它们时,它会崩溃。此外,图像不是从 1.5 页面加载的。可能是什么问题?
  • 需要检查你的图片加载流程

标签: java android pagination out-of-memory retrofit2


【解决方案1】:

当您使用 HttpLoggingInterceptor.Level.BODY 时,您尝试下载大文件,会将所有正文保存在内存中以供日志使用。

这很容易让 OOM 。

尝试删除日志正文或仅记录 NONE 、基本或标题,然后重试。

当你尝试获取大文件时,可以像这样在 retrofit2 中使用@Streaming。

@Streaming
@GET
fun downloadFileWithDynamicUrlSync(@Url fileUrl: String): Call<ResponseBody>

尝试在此处删除日志正文。

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);

试试这个。

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.NONE);

【讨论】:

    【解决方案2】:

    也许当毕加索尝试设置图像时,viewholder 已经为空。对于调试,您可以使用 picasso 独立加载图像并自行设置图像。不要忘记在设置之前检查可空性。

    Picasso.with(context)
                .load(imageUrl)
                .into(imageView, new com.squareup.picasso.Callback() {
                            @Override
                            public void onSuccess() {
                                //check nullability and set image
    
                            }
    
                            @Override
                            public void onError() {
    
                   }
    

    【讨论】:

    • 你的意思是我应该在 null 上检查 viewholder 吗?如果是,则在“onSuccess()”中它始终不为空。如果它为空,错误将是“NPE”,不是吗?
    • 你能不能,用leak canary检查是否有泄漏。
    猜你喜欢
    • 2018-05-03
    • 1970-01-01
    • 2017-01-11
    • 2016-01-29
    • 1970-01-01
    • 2018-11-06
    • 1970-01-01
    • 2020-12-31
    • 2018-10-05
    相关资源
    最近更新 更多