有时位图解码返回SkImageDecoder::Factory返回null并显示损坏的图像



我开发了下面的类来接收带有标头(22字节)发送的图像,首先我解码标头以检查图像正确性,然后我解码图像,但并非所有图像都被解码,有时它返回SkImageDecoder::Factory returned null。我正在使用这个Android教程来inSampleImage

public class SocketServerStreamDataThread extends Thread {
    private Socket streamSocket;
    MainActivity mainActivity ;
    ImageView  displayedImage ;
    Bitmap bitmapImageToDisplay = null;
    byte[] dataBuffer = new byte[1048576];
    boolean result ;
    protected static boolean mReceivingStop; // Flag used to start and stop transmitting data
    SocketServerStreamDataThread(Socket socket, MainActivity mainActivityReceived, ImageView displayedImageView) {
        streamSocket = socket;
        // received activity to work on it in our java file
        mainActivity = mainActivityReceived ;
        // received image UI component to display
        displayedImage = displayedImageView;
    }
    @Override
    public void run() {
        /* Receiving the image */
        try {
            DecodeData decodeData = new DecodeData();
            // call DecodeData to get Image data from stream
            while (mReceivingStop == false) {
                if (bitmapImageToDisplay == null) {
                    InputStream inputStream = streamSocket.getInputStream();
                    if (inputStream.read(dataBuffer) > -1) {
                        // Call the class to decode received stream and return integer with the state of the decoding of the received data
                        // result = 0  ; means that decoding header is successful.
                        // result = -1 ; means that there is a problem in decoding header.
                        byte [] dataHeaderReceived = Arrays.copyOf(dataBuffer, 23) ;
                        result = decodeData.iDecodeReceiveData(dataHeaderReceived);
                        // Evalute the received data
                        if (result == false) {
                            /* Fault on application */
                            /* Close socket */
                            streamSocket.close();
                            mReceivingStop = true;
                        }
                        if (result == true) {
                            /* Data have been received */
                            /* Show image */
                            BitmapFactory.Options imageOption = new BitmapFactory.Options();
                            imageOption.inJustDecodeBounds = true;
                            BitmapFactory.decodeResource(mainActivity.getResources(), R.id.display_image, imageOption);
                            imageOption.inSampleSize = calculateInSampleSize (imageOption, 550, 435) ;
                            imageOption.inJustDecodeBounds = false;
                            bitmapImageToDisplay = BitmapFactory.decodeByteArray(dataBuffer, 23, decodeData.imageSize, imageOption);
                            if (bitmapImageToDisplay != null) {
                                mainActivity.runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        //try to display to image
                                        displayedImage.setImageBitmap(bitmapImageToDisplay);
                                    }
                                });
                            }
                        }
                    }
                    SystemClock.sleep(300);
                    bitmapImageToDisplay = null ;
                }
            }
            streamSocket.close();
            mReceivingStop = true;
            mainActivity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(mainActivity, "Finished", Toast.LENGTH_LONG).show();
                }
            });
        } catch (IOException e) {
            e.printStackTrace();
            StringBuilder eMsg = new StringBuilder();
            eMsg.append("Something wrong: " + e.getMessage());
            final String eMessage=eMsg.toString();
            // final String eMsg = "Something wrong: " + e.getMessage();
            mainActivity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    Toast.makeText(mainActivity, eMessage, Toast.LENGTH_LONG).show();
                }
            });
        } finally {
            if(streamSocket != null){
                try {
                    streamSocket.close();
                    mReceivingStop = true ;
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

Logcat输出:

GC_FOR_ALLOC freed 1307K, 27% free 13676K/18596K, paused 34ms, total 34ms
D/dalvikvm: GC_FOR_ALLOC freed 1306K, 27% free 13676K/18596K, paused 8ms, total 8ms
D/skia: --- SkImageDecoder::Factory returned null
D/skia: --- SkImageDecoder::Factory returned null
D/dalvikvm: GC_FOR_ALLOC freed 1607K, 29% free 13376K/18596K, paused 24ms, total 24ms
D/skia: --- SkImageDecoder::Factory returned null
D/dalvikvm: GC_FOR_ALLOC freed 1255K, 27% free 13728K/18596K, paused 9ms, total 9ms

在Android上加载位图是一个庞大且相当复杂的主题。这就是为什么我建议使用一个经过验证的库来为您处理它;比如格莱德、毕加索或弗雷斯科。这些库将为您处理加载位图时出现的许多陷阱;包括内存约束、缓存和线程。


如果你真的想单独行动,你就得自己面对这些问题。如果位图不能正确解码,SkImageDecoder::Factory returned nullBitmapFactory.decode...方法吐出的,这可能是由许多问题引起的。我不知道到底是什么原因造成的,但我确实看到一些问题在你的代码。

首先,您将MainActivity传递给Thread,这只是自找麻烦。相反,您只需要将ImageView传递给Thread。确保你使用WeakReference,否则你有泄露Context的风险:

WeakReference<ImageView> mDisplayedImageView;
SocketServerStreamDataThread(Socket socket, ImageView imageView) {
    mDisplayedImageView = new WeakReference<>(imageView);
    ...
}

接下来,您使用可绘制的资源来解码Bitmap的边界,但随后您试图从Socket连接获得Bitmap。相反,您应该使用InputStream:

解码边界。
BufferedInputStream inBounds = new BufferedInputStream(streamSocket.getInputStream());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeStream(inBounds, null, options);
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
inBounds.close();

并且您不需要单独的DecodeData类,因为BitmapFactory也有解码流的方法:

options.inJustDecodeBounds = false;
BufferedInputStream in = new BufferedInputStream(streamSocket.getInputStream());
Bitmap bitmap = BitmapFactory.decodeStream(in, null, options);
in.close();

最后,如果您使用Thread.sleep()SystemClock.sleep()来克服某些障碍,您可能应该重新考虑您的解决方案。这些只是我在快速浏览中注意到的问题。如果你真的想在没有库的帮助下继续,我建议你多研究一下如何有效地加载位图;文档是一个很好的起点。

最新更新