SurfaceView的nativeUnlockCanvasAndPost 导致的没有任何错误信息的IllegalArgumentException

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qqchenjian318/article/details/62884450

最近项目里面使用到了surfaceView,在子线程中进行页面绘制,但是出现了一个莫名其妙的bug,该Exception,没有什么信息,无从判断是什么原因导致的

<Image_1>

所以,需要我们去看看为什么会导致这样的问题,我们的代码是如下

  		if (mSurfaceHolder != null) {
                    mCanvas = mSurfaceHolder.lockCanvas();
                }

                try {

                    mCanvas.drawBitmap();//将bitmap绘制到画布上

                } catch (Exception e) {
                    e.printStackTrace();
                } finally {

                    if (mCanvas != null && mSurfaceHolder != null && !isDestroy) {
                        // 将画布解锁并显示在屏幕上
                        mSurfaceHolder.unlockCanvasAndPost(mCanvas);
                    }
                }

大致流程就是,先锁定SurfaceView的画布,然后将bitmap绘制上去,再解锁画布,但是在android 4.3的手机上出现了大量上述的错误。

查阅资料,发现网上大多都是当手机按下home键时,surfaceView已经销毁了,但是有概率子线程还是在进行绘制操作,这样就有可能出现错误,


所以,方式一:

加上home键的back监听,及时更改surfaceView的状态标志位

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        // 当按返回键时,将线程停止,避免surfaceView销毁了,而线程还在运行而报错
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            mIsThreadRunning = false;
            isDestroy = true;//将surfaceView标识为已经销毁
        }

        return super.onKeyDown(keyCode, event);
    }

虽然,进行了修改,但是在android 4.3的手机上,还是有几率会报出该异常,那么我们去看下unlockCanvasAndPost的源码

看看,他到底是为什么会抛出上面的异常

该方法的具体实现在Surface类中

public void unlockCanvasAndPost(Canvas canvas) {
        synchronized (mLock) {
            checkNotReleasedLocked();

            if (mHwuiContext != null) {
                mHwuiContext.unlockAndPost(canvas);
            } else {
                unlockSwCanvasAndPost(canvas);
            }
        }
    }
然后我们可以看见,要么调用的是mHwuiContext的方法,要么是调用的unlocakSwCanvasAndPost方法,而堆栈信息已经说明了,是调用的unlockSwCanvasAndPost方法,这个mHwuiContext其实就是是否开启硬件加速进行绘制,他的初始化是在下面这个方法里面的

 public Canvas lockHardwareCanvas() {
        synchronized (mLock) {
            checkNotReleasedLocked();
            if (mHwuiContext == null) {
                mHwuiContext = new HwuiContext();
            }
            return mHwuiContext.lockCanvas(
                    nativeGetWidth(mNativeObject),
                    nativeGetHeight(mNativeObject));
        }
    }

因为,跟我们要解决的问题无关,所以暂时不去管它。我们继续看unlockSwCanvasAndPost方法

 private void unlockSwCanvasAndPost(Canvas canvas) {
        if (canvas != mCanvas) {
            throw new IllegalArgumentException("canvas object must be the same instance that "
                    + "was previously returned by lockCanvas");
        }
        if (mNativeObject != mLockedObject) {
            Log.w(TAG, "WARNING: Surface's mNativeObject (0x" +
                    Long.toHexString(mNativeObject) + ") != mLockedObject (0x" +
                    Long.toHexString(mLockedObject) +")");
        }
        if (mLockedObject == 0) {
            throw new IllegalStateException("Surface was not locked");
        }
        try {
            nativeUnlockCanvasAndPost(mLockedObject, canvas);
        } finally {
            nativeRelease(mLockedObject);
            mLockedObject = 0;
        }
    }

我们可以看到,该方法虽然抛出了异常,但是很明显不是我们抛的那个,因为我们的那个异常是没有异常信息的。那么需要继续查看nativeUnlockCanvasAndrPost方法了,从命名规则就可以看出,这是个jni的方法。

private static native void nativeUnlockCanvasAndPost(long nativeObject, Canvas canvas);
查看代码,也确实是这样,是个native方法。

而且我们发现,这个native方法并没有抛我们出现的这个异常,这一路的调用也都没有 抛出异常。这说明,抛异常的地方,是在android的jni代码里面了,这可不好办了。。。

让我们思索片刻。。。我们好像忘记了万能的google,所以让我们google一下

最终,我们找到了下面这个地方

https://code.google.com/p/android/issues/detail?id=58385

  坑爹呢这是!!!

看来,众多的外国同行也遇到了android 4.3出现的这个问题,跟我们发生的情况一模一样,而且并没有什么明确的解决办法。

兜兜转转一大圈。。。原来还是需要android的开发组去解决,所以目前我们能做的就只有如下方式了


方式二:

 		Surface surface = mSurfaceHolder.getSurface();
                if (mCanvas != null && mSurfaceHolder != null && !isDestroy && surface != null && surface.isValid()) {
                       //说明当前帧的时候,该控件已经销毁了
                        try {
                           
                             mSurfaceHolder.unlockCanvasAndPost(mCanvas);//手动try catch一下这个方法,让程序在4.3的手机上不至于崩溃
                        }catch (Exception e){
                              e.printStackTrace();
                        }
                  }
第一个,加载surface.isValid()的判断,如果surface已经无效了,那么就不会执行下面这个函数

第二个,手动加载try catch,把异常抓一下,至少不至于在4.3的手机上崩溃


最后,如此做法并非万全之策,实属无奈之举,各位如果有好的做法,请一定联系我。。。谢谢





猜你喜欢

转载自blog.csdn.net/qqchenjian318/article/details/62884450