1

I developed the below class to receive image sent with header (22 bytes) first I decode the header to check the image correctness then I decode the image but not all images are decoded sometimes it returns with SkImageDecoder::Factory returned null. I am using this Android tutorial to 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 output:

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
zelf
  • 93
  • 1
  • 1
  • 7
  • I used SystemClock.sleep(300); to give time for decoding process as before it was not catching to decode and the message of decode return false was appearing – zelf Sep 27 '16 at 11:26
  • What is `DecodeData`? And where are you getting a `resId` from? – Bryan Sep 27 '16 at 13:36
  • DecodeData is class to decode the header of the received image to get image attributes also to know the image size – zelf Sep 27 '16 at 13:48

1 Answers1

0

Loading bitmaps on Android is a large and fairly complex topic. Which is why I recommend using one of the proven libraries to handle it for you; such as Glide, Picasso or Fresco. These libraries will handle many of the pitfalls that come with loading bitmaps for you; including memory constraints, caching and threading.


If you really want to go at alone you will have to face these problems yourself. The SkImageDecoder::Factory returned null is what the BitmapFactory.decode... methods spit out if the bitmap cannot be decoded correctly, which can be caused by a number of issues. I do not know exactly what is causing this, but I do see a number of issues in your code.

First, you pass the MainActivity into the Thread, this is just asking for trouble. Instead you should only need to pass the ImageView into to the Thread. And make sure you use a WeakReference, otherwise you risk leaking your Context:

WeakReference<ImageView> mDisplayedImageView;

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

Next, you are using a drawable resource to decode the bounds of the Bitmap, but then you are trying to obtain a Bitmap from a Socket connection. Instead you should decode the bounds using the 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();

And you will not need to have a separate DecodeData class, as BitmapFactory has a method to decode a stream as well:

options.inJustDecodeBounds = false;

BufferedInputStream in = new BufferedInputStream(streamSocket.getInputStream());

Bitmap bitmap = BitmapFactory.decodeStream(in, null, options);

in.close();

Lastly, if you are using Thread.sleep() or SystemClock.sleep() to overcome some obstacle, you should probably rethink your solution. These are just the issues I noticed on a quick look through. If you really want to continue without the aid of a library, I suggest doing some more research on how to load bitmaps efficiently; a good starting point is the documentation.

Bryan
  • 14,756
  • 10
  • 70
  • 125
  • thanks for your help, but first the received image added with 22 special byte header that need to be decoded so that I am using DecodeData class it can not be decoded with BitmapFactory so I need first to read the first 22 byte to read special data. second I am using 'SystemClock.sleep()' to overcome this problem that I can not find the source of it.Also I am passing 'MainActivity' to use 'runOnUiThread' I do not know about 'WeakReference' I will search about it if it is a solution. – zelf Sep 27 '16 at 15:35
  • I have a question, Are Picasso or Glide include an image decoder inside instead of 'BitmapFactory.decode'? – zelf Sep 27 '16 at 15:41
  • 1
    As far as I know Picasso, Glide and Fresco all use the `BitmapFactory.decode...` methods to decode images, they do not contain separate decoders. – Bryan Sep 27 '16 at 16:26
  • You should also be using a [`Handler`](https://developer.android.com/reference/android/os/Handler.html), along with callbacks, instead of passing the `MainActivity` around. – Bryan Sep 27 '16 at 16:31