使用OutOfMemoryException调用订阅者onError()时出现IllegalStateException

时间:2017-04-06 20:43:44

标签: java android out-of-memory rx-java

我的代码旨在获取原始图像,异步处理它,并让用户看到他们处理过的图像。有时,图像处理会导致OutOfMemory错误,然后我想通知用户他们的设备内存不足。

但是,当我尝试捕获错误并通知订阅者时,我遇到了崩溃。此外,当我在代码中放置断点时,正在捕获OutOfMemory错误,并且subscriber.onError(e)行被命中,但永远不会达到onPhotoProcessingError(e)

以下是订阅的位置:

imageDelegate.cropBitmap(rawPhoto, cameraManager.getPictureRotationDegrees(), cameraManager.getCurrentCameraId() == Camera.CameraInfo.CAMERA_FACING_FRONT)
            .subscribeOn(Schedulers.computation())
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe(new Subscriber<Bitmap>() {
                @Override
                public void onNext(Bitmap bitmap)
                {
                    onPhotoProcessed(bitmap);
                }

                @Override
                public void onError(Throwable e)
                {
                    onPhotoProcessingError(e);
                }

                @Override
                public void onCompleted()
                {

                }
            });

以下是捕获OutOfMemory错误的代码:

public Observable<Bitmap> cropBitmap(Bitmap rawPhoto, int rotation, boolean isFrontFacing)
{
    return Observable.create(new Observable.OnSubscribe<Bitmap>()
    {
        @Override
        public void call(Subscriber<? super Bitmap> subscriber)
        {
            try
            {
                // rotates the photo so that it's portrait and cuts off the
                // bottom of the photo so that we have a square photo
                Bitmap rotatedSquarePhoto = PhotoUtils.rotateAndTruncateBottomOfPictureIntoSquare(rawPhoto, rotation);
                rawPhoto.recycle();

                // we want to mirror the image to match what the user saw when taking a selfie
                if (isFrontFacing)
                {
                    rotatedSquarePhoto = PhotoUtils.reflectAboutYAxis(rotatedSquarePhoto);
                }

                subscriber.onNext(rotatedSquarePhoto);
            }
            catch (OutOfMemoryError e)
            {
                subscriber.onError(e);
            }
        }
    });
}

以下是崩溃的堆栈跟踪:

    04-06 16:40:55.303 22729-22729/ E/AndroidRuntime: FATAL EXCEPTION: main
   Process: com.emersonboyd, PID: 22729
   java.lang.IllegalStateException: Fatal Exception thrown on Scheduler.Worker thread.
       at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:62)
       at android.os.Handler.handleCallback(Handler.java:751)
       at android.os.Handler.dispatchMessage(Handler.java:95)
       at android.os.Looper.loop(Looper.java:154)
       at android.app.ActivityThread.main(ActivityThread.java:6119)
       at java.lang.reflect.Method.invoke(Native Method)
       at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:886)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:776)
    Caused by: java.lang.OutOfMemoryError: Failed to allocate a 921612 byte allocation with 861608 free bytes and 841KB until OOM
       at dalvik.system.VMRuntime.newNonMovableArray(Native Method)
       at android.graphics.Bitmap.nativeCreate(Native Method)
       at android.graphics.Bitmap.createBitmap(Bitmap.java:879)
       at android.graphics.Bitmap.createBitmap(Bitmap.java:856)
       at android.graphics.Bitmap.createBitmap(Bitmap.java:787)
       at com.emersonboyd.util.PhotoUtils.rotateAndTruncateBottomOfPictureIntoSquare(PhotoUtils.java:225)
       at com.emersonboyd.delgate.ImageDelegate$2.call(ImageDelegate.java:124)
       at com.emersonboyd.delgate.ImageDelegate$2.call(ImageDelegate.java:116)
       at rx.Observable.unsafeSubscribe(Observable.java:8460)
       at rx.internal.operators.OperatorSubscribeOn$1.call(OperatorSubscribeOn.java:94)
       at rx.internal.schedulers.EventLoopsScheduler$EventLoopWorker$1.call(EventLoopsScheduler.java:169)
       at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
       at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:428)
       at java.util.concurrent.FutureTask.run(FutureTask.java:237)
       at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:272)
       at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1133)
       at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:607)
       at java.lang.Thread.run(Thread.java:761)

任何帮助我指明正确方向的帮助都会很棒。

1 个答案:

答案 0 :(得分:2)

这是因为OutOfMemoryErrorRxJava视为致命错误。此类错误不会在内部被“吞没”,也不会传递给onError回调。

以下是RxJava 1 源代码的片段。在每个内部错误处理块的开头调用此方法:

public static void throwIfFatal(Throwable t) {
    if (t instanceof OnErrorNotImplementedException) {
        throw (OnErrorNotImplementedException) t;
    } else if (t instanceof OnErrorFailedException) {
        throw (OnErrorFailedException) t;
    } else if (t instanceof OnCompletedFailedException) {
        throw (OnCompletedFailedException) t;
    }
    // values here derived from https://github.com/ReactiveX/RxJava/issues/748#issuecomment-32471495
    else if (t instanceof VirtualMachineError) {
        throw (VirtualMachineError) t;
    } else if (t instanceof ThreadDeath) {
        throw (ThreadDeath) t;
    } else if (t instanceof LinkageError) {
        throw (LinkageError) t;
    }
}

OutOfMemoryErrorVirtualMachineError的后代。

更多信息: https://github.com/ReactiveX/RxJava/issues/296 https://github.com/ReactiveX/RxJava/issues/748

您的问题的解决方案:抓住您的OutOfMemoryError,但改为推送任何适当的Exception子类。

P.S。 提示:对于您的案例,请使用Observable.fromCallable代替Observable.create